🐥

SwiftUIでCustomDismissActionを作成する

2023/06/30に公開

SwiftUIでdismissをするとき下記のキーを利用します。
https://developer.apple.com/documentation/swiftui/environmentvalues/dismiss

しかし対象OSバージョンに制限があります。
iOS14などでは下記を利用することになります。
https://developer.apple.com/documentation/swiftui/presentationmode/dismiss()

そして下記のExtensionによってOSでバージョン分けることができます。

extension EnvironmentValues {
    // this can be used as: @Environment(\.dismissable) var myDismiss
    // in any swiftui view and it will not complain about ios versions
    var dismissable: () -> Void {
        return dismissAction
    }


    // this function abstracts the availability check so you can
    // avoid the conflicting return types and any other headache
    private func dismissAction() {
        if #available(iOS 15, *) {
            dismiss()
        } else {
            presentationMode.wrappedValue.dismiss()
        }
    }
}

https://stackoverflow.com/questions/57190511/dismiss-a-swiftui-view-that-is-contained-in-a-uihostingcontroller

ただしpresentationMode.wrappedValue.dismiss()ではUIHostingControllerの更に親のViewControllerを閉じることができません。こちらでは可能です。

OSバージョンで差異を出さずに閉じることのできるDismissKeyを作成します。

環境

OS: macOS 13.3.1
Xcode.app: Version 14.3 (14E222b)
動作iOSバージョン:13以上

CustomDismissAction

CustomDismissActionKey
struct CustomDismissAction {
    private var parentViewController: UIViewController
    
    func callAsFunction(animated: Bool = true) {
        parentViewController.dismiss(animated: animated)
    }

    init(_ parentViewController: UIViewController = UIViewController()) {
        self.parentViewController = parentViewController
    }
}

struct CustomDismissActionKey: EnvironmentKey {
    static let defaultValue: CustomDismissAction = CustomDismissAction()
}

extension EnvironmentValues {
    var customDismiss: CustomDismissAction {
        get {
            self[CustomDismissActionKey.self]
        }
        set {
            self[CustomDismissActionKey.self] = newValue
        }
    }
}

extension View {
    func setupDismissAction(_ parentViewController: UIViewController) -> some View {
        environment(\.customDismiss, CustomDismissAction(parentViewController))
    }
}

利用方法

HostingControllerを利用時にsetupDismissActionモディファイアで親のViewControllerを指定します。

class SwiftUIWrapViewController: UIViewController {
----
    override func viewDidLoad() {
        super.viewDidLoad()
----
        let hostingController: UIHostingController = UIHostingController(
            rootView: SwiftUIView()
                .setupDismissAction(self)
        )
----
    }
}

DismissActionを利用する時のように@Environmentで定義し、利用したい箇所で呼びます。

@Environment(\.customDismiss) private var dismiss

今回作成したCustomDismissActionはこちらで公開していますのでご参考になればと思います。
https://github.com/kokiTakashiki/iOS14SupportDismissKey/tree/main

最後までお読みいただきありがとうございました。

Discussion