Skip to content

Instantly share code, notes, and snippets.

@nathanborror
Last active October 30, 2022 13:26
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nathanborror/7fcb09f1418bc52c7b1d1123f115d200 to your computer and use it in GitHub Desktop.
Save nathanborror/7fcb09f1418bc52c7b1d1123f115d200 to your computer and use it in GitHub Desktop.
SwiftUI wrapper around Slack's PanModal (https://github.com/slackhq/PanModal)
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)
}
}
@Yggdrasilqh
Copy link

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment