Last active
October 30, 2022 13:26
-
-
Save nathanborror/7fcb09f1418bc52c7b1d1123f115d200 to your computer and use it in GitHub Desktop.
SwiftUI wrapper around Slack's PanModal (https://github.com/slackhq/PanModal)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import PanModal | |
struct ExampleView: View { | |
@State var detail: AnyView? = nil | |
@State var items: [String] = ["Detail 1", "Detail 2", "Detail 3"] | |
var body: some View { | |
NavigationView { | |
SheetPresenterView(master: master, detail: $detail) | |
.navigationBarTitle("Master") | |
.navigationBarItems(trailing: Button(action: self.addItem) { Text("Add") }) | |
} | |
} | |
var master: some View { | |
List(items, id: \.self) { item in | |
Button(action: { self.detail = AnyView(Text(item)) }) { Text(item) } | |
} | |
} | |
func addItem() { | |
items.append("Detail \(items.count + 1)") | |
} | |
} | |
/// SheetPresenterView is a wrapper around SheetMasterController and accepts a master | |
struct SheetPresenterView<Master: View, Detail: View>: UIViewControllerRepresentable { | |
var master: Master | |
@Binding var detail: Detail? | |
func makeUIViewController(context: Context) -> SheetMasterController { | |
let controller = SheetMasterController(masterController: UIHostingController(rootView: master)) | |
controller.didDismiss = { self.detail = nil } | |
return controller | |
} | |
func updateUIViewController(_ uiViewController: SheetMasterController, context: Context) { | |
uiViewController.masterController = UIHostingController(rootView: master) | |
if let view = detail { | |
uiViewController.detailController = UIHostingController(rootView: view) | |
} | |
} | |
} | |
/// SheetMasterController contains and manages the master and detail view controllers. | |
class SheetMasterController: UIViewController { | |
var masterController: UIViewController { | |
willSet { removeMaster() } | |
didSet { addMaster() } | |
} | |
var detailController: UIViewController? = nil { | |
didSet { | |
guard let viewController = detailController else { return } | |
guard !isPanModalPresented else { return } | |
presentPanModal(SheetDetailController(rootViewController: viewController)) | |
} | |
} | |
var didDismiss: (() -> Void)? = nil | |
init(masterController: UIViewController) { | |
self.masterController = masterController | |
super.init(nibName: nil, bundle: nil) | |
} | |
required init?(coder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
addMaster() | |
} | |
override func viewDidAppear(_ animated: Bool) { | |
super.viewDidDisappear(animated) | |
didDismiss?() | |
} | |
func addMaster() { | |
guard isViewLoaded else { return } | |
masterController.view.frame = view.bounds | |
masterController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] | |
view.addSubview(masterController.view) | |
addChild(masterController) | |
masterController.didMove(toParent: self) | |
} | |
func removeMaster() { | |
masterController.willMove(toParent: nil) | |
masterController.removeFromParent() | |
masterController.view.removeFromSuperview() | |
} | |
} | |
/// SheetDetailController implements the PanModalPresentable protocol and manages the detail presentation. | |
class SheetDetailController: UIViewController, PanModalPresentable { | |
let rootViewController: UIViewController | |
init(rootViewController: UIViewController) { | |
self.rootViewController = rootViewController | |
super.init(nibName: nil, bundle: nil) | |
} | |
required init?(coder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
var panScrollable: UIScrollView? { | |
return nil | |
} | |
var shortFormHeight: PanModalHeight { | |
return .contentHeight(400) | |
} | |
var showDragIndicator: Bool { | |
return false | |
} | |
var panModalBackgroundColor: UIColor { | |
return UIColor(white: 0, alpha: 0.1) | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
view.backgroundColor = .white | |
rootViewController.view.frame = view.bounds | |
rootViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] | |
view.addSubview(rootViewController.view) | |
addChild(rootViewController) | |
rootViewController.didMove(toParent: self) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for your example! But I got some problem when adding a ScrollView to detail view, the gesture doesn't work well. Do you have a way to fix it?