2023. 5. 7. 20:07, 공부/iOS
2023.04.20 목요일
GitSpace 개발 중 차단/신고 모달 뷰를 half sheet로 구현하기 위해 작성한 글입니다.
iOS 16에는 pretent라고 해서 modal의 크기를 마음대로 조정할 수 있는 수정자를 제공한다.
그러면 그 이전 버전은요..?
-> '직접' 만들어써야 한다.
GitSpace는 iOS 15를 채택하고 있기 때문에, UIKit의 기능을 불러와서 직접 Half Modal을 구현하기로 했다.
아래는 구현에 필요한 파일들이다.
- ContentView: View
- extension View
- HalfSheetManager << UIKit Integration
- CustomHostingController
일반 수정자처럼 Half Modal을 사용할 수 있게 하자.
.sheet { ... }
처럼 Half Modal도 .halfSheet { ... }
처럼 사용해보자.
// Custom Half Modal Modifier
extension View {
// Binding Show Vairables
func halfSheet<SheetView: View> (
showSheet: Binding<Bool>,
@ViewBuilder sheetView: @escaping ()->SheetView
) -> some View {
return self
}
}
// ContentView
struct ContentView: View {
@State var isShowing: Bool = false
var body: some View {
VStack {
Text("Hello!")
Button {
isShowing.toggle()
} label: {
Text("Show Half Modal Sheet.")
}
}
.halfSheet {
Text("This is Half Sheet.")
}
}
}
UIKit의 기능을 가져오자.
// UIKit Integration
struct HalfSheetManager<SheetView: View>: UIViewControllerRepresentable {
var sheetView: SheetView
let controller: UIViewController()
func makeUIViewController(context: Context) -> UIViewController {
controller.view.background = .clear
return controller
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}
HalfSheetModifier 수정
Swiftui frame 사이즈에 맞게 자동으로 맞춰지도록 코드를 수정하자.
// Custom Half Modal Modifier
extension View {
// Binding Show Vairables
func halfSheet<SheetView: View> (
showSheet: Binding<Bool>,
@ViewBuilder sheetView: @escaping ()->SheetView
) -> some View {
// why we using overlay or background
// because it will automatically use the swiftui frame size only!
return self
.background {
HalfSheetManager(sheetView: sheetView(), showSheet: showSheet)
}
}
}
UIKit의 기능을 가져오자.
- isShowing 프로퍼티 바인딩
sheet가 나오는 상황을 조절해주는 isShowing을 바인딩하여 HalfSheetManager에 전달해주자.
// UIKit Integration
struct HalfSheetManager<SheetView: View>: UIViewControllerRepresentable {
var sheetView: SheetView
let controller: UIViewController()
@Binding var showSheet: Bool
func makeUIViewController(context: Context) -> UIViewController {
controller.view.background = .clear
return controller
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
if showSheet {
// Presenting Modal View
let sheetController = UIHostingController(rootView: sheetView)
uiViewController.present(sheetController, animated = true) {
// tooling the show State
DispatchQueue.main.async {
self.showSheet.toggle()
}
}
}
}
}
Custom UIHostingController
UISheetPresentationController로 모달의 크기를 정해준다.
해당 클래스를 사용하여 모달의 크기를 설정해주자. 도큐먼트를 읽고 원하는데로 조정하면 된다!
// Custom UIHostingController for halfsheet
class CustomHostingController<Content: View>: UIHostingContrller<Content> {
override func viewDidLoad() {
// setting presentation controller properties
if let presentationController = presentationController as? UISheetPresentationController {
presentationController.detents = [
.medium(),
.large()
]
// to show grab protion
presentationController.preferGrabberVisible = true
}
}
}
dismiss 설정하기 + 전체 코드
/* VIEW */
struct ContentView: View {
@State var showSheet: Bool = false
var body: some View {
VStack {
Text("Hello!")
Button {
showSheet.toggle()
} label: {
Text("Show Half Modal Sheet.")
}
}
.halfSheet(showSheet: $showSheet) {
VStack {
Text("This is Half Sheet.")
Button {
showSheet.toggle()
} label: {
Text("Dismiss")
}
}
} onEnd: {
print("Dismissed.")
}
}
}
// Custom Half Modal Modifier
extension View {
// Binding Show Vairables
func halfSheet<SheetView: View> (
showSheet: Binding<Bool>,
@ViewBuilder sheetView: @escaping ()->SheetView,
/* 새로 추가한 부분 */
onEnd: @escaping ()->()
) -> some View {
// why we using overlay or background
// because it will automatically use the swiftui frame size only!
return self
.background {
HalfSheetManager(
sheetView: sheetView,
showSheet: shotSheet(),
/* 새로 추가한 부분*/
onEnd: onEnd
)
}
}
}
/* HALF SHEET MANAGER */
// UIKit Integration
struct HalfSheetManager<SheetView: View>: UIViewControllerRepresentable {
var sheetView: SheetView
let controller = UIViewController()
@Binding var showSheet: Bool
/* 새로 추가한 부분 */
var onEnd: () -> ()
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
func makeUIViewController(context: Context) -> UIViewController {
controller.view.backgroundColor = .clear
return controller
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
if showSheet {
let sheetController = CustomHostingController(rootView: sheetView)
sheetController.presentationController?.delegate = context.coordinator
uiViewController.present(sheetController, animated: true)
} else {
// choosing view when showsheet toggled againg
uiViewController.dismiss(animated: true)
}
}
/* 새로 추가한 부분 */
// On dismiss
class Coordinator: NSObject, UISheetPresentationControllerDelegate {
var parent: HalfSheetManager
init(parent: HalfSheetManager) {
self.parent = parent
}
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
parent.showSheet = false
parent.onEnd()
}
}
}
/* CUSTOM HOSTING CONTROLLER */
// Custom UIHostingController for halfsheet
class CustomHostingController<Content: View>: UIHostingController<Content> {
override func viewDidLoad() {
view.backgroundColor = .clear
// setting presentation controller properties
if let presentationController = presentationController as? UISheetPresentationController {
presentationController.detents = [
.medium(),
.large()
]
// to show grab protion
presentationController.prefersGrabberVisible = true
}
}
}
reference
- https://www.youtube.com/watch?v=rQKT7tn4uag
GitSpace에 적용하기
- TargetUserProfileView
- @State var showSheet: Bool
- halfSheet
- View Extension
- func halfSheet() { return self }
- HalfSheetManager
- UIViewControllerPresentable
- CustomHostingController
- Coordinator
- -Dismiss
- CustomHostingController
- viewDidLoad
- UISheetPresentationController
- viewDidLoad
'공부 > iOS' 카테고리의 다른 글
Apple CryptoKit으로 채팅 메세지를 암호화할 수 있을까? (0) | 2023.06.14 |
---|---|
암호화 작업을 위한 Apple CryptoKit 알아보기 (2) | 2023.06.12 |
[SwiftUI] Firebase의 GitHub 로그인으로 Auto Login 구현하기 (0) | 2023.06.12 |
[SwiftUI] SwiftUI에서 무한스크롤(Infinite Scroll) 구현하기 (0) | 2023.05.07 |
Comments, Trackbacks