momodudu.zip
#3 SwiftUI에서 Swift UIKit 사용하는것에 대해 - UIRepresentable 본문
완전 오랜만에 포스팅~ 맘놓고 포스팅하기에 적합한 환경(?)이 아니었고 그동안 주말공부에 좀 소홀해서 못했다. ㅋㅋㅋ
오늘은 SwiftUI에서 Swift UIKit을 사용하는 방법에 대해서 포스팅 해보고자 한다! 약간 그동안 SwiftUI 포스팅을 하지 않았어서 뭔가 급진도(?)를 빼는 느낌이 들긴 하지만..
SwiftUI는 많은 좋은 기능들을가지고 있긴 하지만, 아무래도 나온지 얼마 안됐다보니까 지원하지 않는 기능이나 기존에 UIKit이 더 유용한(?) 경우도 많다. 전에 얼핏 봤던 포스팅으로는 UICollectionView가 그렇다고 하더라... 아무튼. SwiftUI이면서도 Swift 언어로 된 프레임워크인 UIKit이 필요한 경우가 많다. 당장 나만해도, 현재 UIView를 wrapping해서 커스터마이징 한 View를 개발중이기도 하고... 이런 경우에는 SwiftUI를 사용하더라도 UIKit이 불가피한 경우가 아직 많다.
그래서! SwiftUI에서는 이 UIKit을 SwiftUI에 맞게 wrapping해주는 기능을 제공한다. 바로 UIViewRepresentable 프로토콜!
이 프로토콜은 말그대로 UIKit view를 SwiftUI에서 사용할 수 있도록 랩핑해준다. 그외에 VC를 래핑하는 UIViewControllerRepresentable도 있지만, 이번 포스팅에서는 UIViewRepresentable만 다루는걸로~.
일단 이번 포스팅에서는 UIActivityIndicator를 사용해볼건데, 이를 위해서 아래와 같이 커스터마이징 할 struct를 정의해주고, 이 struct에 UIViewRepresentable을 conform하도록 한다.
struct ActivityIndicator: UIViewRepresentable {
}
이렇게! 그럼 컴파일 에러가 뜰텐데, structure가 프로토콜을 준수하지 않는다고 뜬다. 에러를 고치려면 두가지 함수를 추가해주어야 한다.
- makeUIView(context:) -> UIView
ㄴ UIView를 생성하는 메소드로, SwiftUI의 View 라이프 사이클동안 "한번" 호출된다. 래핑하고자 하는 UIView를 여기서 생성해서 리턴해주면 된다. 여기서 생성한 UIView를 UIViewRepresentable로 래핑하여 SwiftUI의 View가 된다.
- updateUIView(:context:)
ㄴ SwiftUI View의 state가 바뀔때마다 트리거된다고 한다. 이 메소드 안에서 view의 정보를 업데이트 할 수 있다. 또한, @Binding기능을 이용해서 SwiftUI view의 상태도 가져올 수 있다! 단, read only이다.
struct ActivityIndicator: UIViewRepresentable {
func makeUIView(context: Context) -> UIActivityIndicatorView {
UIActivityIndicatorView()
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {
}
}
이렇게 두가지 프로토콜 stub을 추가하면 더이상 컴파일 에러가 나지 않는다~
makeUIView 함수 내에서 생성되는 view의 여러가지 프로퍼티들을 지정해 줄 수 있다. 원하는 뷰를 생성하고 리턴해주기만 하면 된다.
또한, updateUIView에서는 해당 예제에서 Binding 변수를 이용해서 swfitUI로부터 감지한 상태 변화를 이용하여 Indicator를 start/stopt시킨다.
struct ActivityIndicator: UIViewRepresentable {
@Binding var isAnimating: Bool
func makeUIView(context: Context) -> UIActivityIndicatorView {
let view = UIActivityIndicatorView()
view.style = .large
return view
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {
if isAnimating {
uiView.startAnimating()
}
else {
uiView.stopAnimating()
}
}
}
이렇게 makeUIView에서 인디케이터 뷰 사이즈를 large로 설정하고, UIView를 리턴하여 SwiftUI View를 생성한다. updateUIView에서는 뭔지 모르겠지만 SwiftUI View 상태가 변했을 때 호출되고, Binding된 isAnmating 변수가 true일경우 인디케이터 뷰를 애니메이션 시키고, false인 경우 stopAnimation을 한다.. 이렇게 해주면, 일단 내부는 UIKit view지만, UIRepresentable로 감싼 struct가 생긴다!
이제 이 struct를 SwiftUI에서 어떻게 쓸까? 그냥 쓰면 된다.
struct ContentView: View {
@State var isAnimating: Bool = false
var body: some View {
VStack {
ActivityIndicator(isAnimating: $isAnimating)
Button(action: { self.isAnimating = !self.isAnimating },
label: { Text("show/hide")
.foregroundColor(Color.white)
})
.background(Color.black)
}
}
}
일단 ContentView를 하나 생성하고, 여기 있는 View를 위에서 생성한 ActivityIndicator와 버튼 하나 , 총 두개를 VStack으로 감싼다. 여기서 버튼의 액션이나 기타 프로퍼티 정의는 굳이 설명하지 않아도 될 것 같고... 유의해서 봐야할 점은! 컨텐츠뷰 내부에 꼭 위에서 @Binding한 변수를 @State로 정의해주어야 한다. 즉, 이 변수는 SwiftUI에서 직접 트리거를 해야한다. 이 State는 버튼 클릭에 따라 토글시키는 방식이다.
이 코드를 실행시키게 되면, 버튼을 클릭하면 activiyIndicator가 동작하고, 다시 클릭하면 인디케이터가 멈춘다.
이정도면 설명이 됐는데, updateUIView에서 아직 설명하지 않은 파라미터가 있다..! Context!!! 이 컨텍스트에 대해서는 다음 포스팅에서 마저 하는걸로~
'ios > SwiftUI' 카테고리의 다른 글
#4 SwiftUI의 Coordinator (2) | 2020.08.25 |
---|---|
#2 SwiftUI - View Layout (0) | 2020.06.07 |
#1 SwiftUI - SwiftUI 시작해보기 Hello, World! (0) | 2020.06.07 |