-
SwiftUI - 조건에 따라 overlay 해주기 (feat. overlayIf)SwiftUI 2022. 11. 10. 14:13
안녕하세요. 그린입니다🍏
이번 포스팅은 SwiftUI에서 조건 분기에 따라 컨텐츠 즉 뷰를 overlay On/Off를 해줄 수 있는 ViewModifier를 만들어 보겠습니다.
SwiftUI에서 overlay와 ZStack이라는 기능들을 활용해 중첩된 뷰를 나타내줄 수 있습니다.
혹시 해당 개념이 궁금하신분들은 아래 포스팅을 먼저 참고하여 overlay 부분이라도 어떻게 사용되는지 보고 오면 도움이 됩니다🙌
https://green1229.tistory.com/173
물론 지금부터 만들 ViewModifier라는것이 없을때 기본 overlay로는 처리하는데 한계가 있습니다.
조건에 따라 다르다는 소리죠.
그럼 먼저 기본 기능만으로 처리할 수 있는 수준을 보시죠!기본 overlay ViewModifier로만 조건에 따라 오버레이 컨텐츠 보여주기
import SwiftUI struct ContentView: View { @State var displayOverlayContent: Bool = false var body: some View { VStack { Button( action: { displayOverlayContent.toggle() }, label: { Text("Show and Hide Overlay Content") } ) Spacer() } .padding() .overlay( displayOverlayContent ? Color.green.frame(width: 300, height: 300) : Color.red.frame(width: 300, height: 300) ) } }
위와 같이 간단한 뷰가 있습니다.
버튼이 클릭되면 overlay되는 컨텐츠는 네모난 색상을 가진 단순한 뷰가 됩니다.
이 뷰는 클릭 여부에 따라 어떤 색상을 보여줄지 변경되죠!
그런데 여기서 중요한건 overlay 내부에서 삼항 연산자로 분기를 줘 각 해당하는 뷰를 보여줄 순 있습니다.
아래처럼 말이죠.
이거까지는 기본적인 overlay로 가능하며 쉽습니다.
그런데 만약 클릭 되지 않았을때 즉 displayOverlayContent 값이 false일때 아무 뷰도 보여주고 싶지 않다면 어떻게 해야 할까요?
EmptyView 혹은 Spacer를 두어도 해결되지 않습니다.
.overlay( displayOverlayContent ? Color.green.frame(width: 300, height: 300) as! EmptyView : EmptyView() )
타입을 맞춰주고자 이렇게 형변환을 해버린다 해도 런타임 에러를 발생시키죠.
그렇기에 적절한 방향이 아니라 봅니다.
그렇기에 오늘 만들어볼 overlayIf라는 ViewModifier를 바로 이제 보시겠습니다!
overlayIf ViewModifier 만들기
위 문제를 해결하는 아주 간단한 구현이 있습니다.
코드부터 보시죠.
extension View { @ViewBuilder public func overlayIf<T: View>( _ condition: Bool, _ content: T, alignment: Alignment = .center ) -> some View { if condition { self.overlay(content, alignment: alignment) } else { self } } }
View를 extension합니다.
그리고 overlayIf라는 메서드에 제네릭하게 뷰를 받습니다.
나타날 조건을 나타내는 condition 파라미터와 받아온 뷰를 content로 삼습니다.
또 overlay 시 정렬 값을 받아줍니다.
그 다음 구현부는 보시는것처럼 조건에 따라 오버레이해서 그 뷰를 넘겨줄지 아니면 받아온 뷰 자체를 그대로 넘겨줄지 판단합니다.
이 모든것은 클로저를 통해 뷰를 구성하고 반환시켜주니 ViewBuilder를 이용해줍니다.
아주 일방적인 방식이죠😄
이렇게 코드를 다시 만져볼께요!
import SwiftUI struct ContentView: View { @State var displayOverlayContent: Bool = false var body: some View { VStack { Button( action: { displayOverlayContent.toggle() }, label: { Text("Show and Hide Overlay Content") } ) Spacer() } .padding() .overlayIf( displayOverlayContent, Color.green.frame(width: 300, height: 300) ) } }
자 그럼 아래와 같이 클릭이 되면 나오고 안나오는 원하던 동작을 해줍니다.
결국 만들어진 overlayIf는 기존 overlay 내부에서 조건을 태워 각 다른 뷰를 보여줄 수 있지 않고 넘어온 오버레이 컨텐츠를 보여줄지 말지를 결정해주고 사용해줄때 적합합니다.
만약 값에 따라 다른 뷰를 보여줘야한다면 overlayIf보다는 기존 overlay를 분기 태워 사용하거나 해당하는 ViewModifier를 만들 수도 있겠습니다.
음 그럼 한번 말나온김에 기존 overlay를 분기 태우지 않고 ViewModifier로 만들어 overlayIf말고 다른걸 만들어 볼께요!
@ViewBuilder public func overlayIf<T: View, U: View>( _ condition: Bool, _ firstContent: T, _ secondContent: U, alignment: Alignment = .center ) -> some View { if condition { self.overlay(firstContent, alignment: alignment) } else { self.overlay(secondContent, alignment: alignment) } }
overlayIf 그대로 메서드명은 사용하고 들어오는 파라미터에 따라 구분 지어줄 수 있어요.
위와 같이 두개의 오버레이 컨텐츠를 넘겨주게 되면 값에 따라 보여줄 컨텐츠를 달리 해줄 수 있습니다.
즉, 위와 같이 원하는 동작을 취할 수 있습니다.
또 여기서 중요한건 secondContent에 Spacer, Empty를 넣어주면 첫번째 overlayIf 동작과 동일하게 구현 해줄 수 있어요.즉 저는 최종적으로 두개를 합쳐 아래와 같이 사용하면 좋지 않을까 생각합니다.
extension View { @ViewBuilder public func overlayIf<T: View, U: View>( _ condition: Bool, _ firstContent: T, _ secondContent: U = Spacer(), alignment: Alignment = .center ) -> some View { if condition { self.overlay(firstContent, alignment: alignment) } else { self.overlay(secondContent, alignment: alignment) } } }
기본값을 주어 해결해주면 깔끔합니다.
마무리
이렇게 간단히 알아봤어요!
ViewModifier를 통해 적절히 커스텀하게 만들어준다면 현재 부족한 SwiftUI의 기능들을 메꿀 수 있습니다.
구현하다보니 어느정도 이런 느낌의 필요성이 있는 기능들을 확장해 만든 pure-swift-ui라는 라이브러리가 있더라구요.
https://github.com/CodeSlicing/pure-swift-ui
직접 만들어도 좋고 라이브러리를 채택해도 좋지만 차라리 라이브러리를 보고 필요한것만 확장시켜 만드는게 더 나아보입니다!
크게 부담되거나 어렵지 않은 기능들이라고 보여서요🚀
'SwiftUI' 카테고리의 다른 글
SwiftUI - PinnedScrollableViews (a.k.a Sticky View) (0) 2022.11.21 SwiftUI - Infinity Carousel View (feat. TCA) (1) 2022.11.17 SwiftUI에서 TapGesture를 통해 현재 Position 구하기 (10) 2022.11.07 SwiftUI - ScrollViewReader (4) 2022.11.03 SwiftUI - DisclosureGroup & OutlineGroup (4) 2022.09.29