场景: 在主UIKit
的项目中Present
出一个包含SwiftUI
的UIHostingController
当需要Dismiss
该控制器的时候遇到了麻烦
一:在SwiftUI中Dismiss一个页面的正常操作
下面的实现方式在正常的SwiftUI
体系下是可以正常工作的 但是在UIKit
混编时就失效了
Copy struct ContentView : View , SwiftUIBridgeProtocol {
@Environment ( \.presentationMode ) var presentationMode: Binding < PresentationMode >
var body: some View {
ZStack {
VStack {
StoreTopView ( dismissClosure : {
presentationMode.wrappedValue.dismiss ()
} )
. padding ()
}
}
}
}
我们看下打印的信息:
Copy (lldb) po presentationMode
▿ Binding < PresentationMode >
▿ transaction : Transaction
▿ plist : []
- elements : nil
▿ location : < LocationBox < FunctionalLocation < PresentationMode >>: 0x2838313c0 >
▿ _value : PresentationMode
- isPresented : false
(lldb) po presentationMode.wrappedValue
▿ PresentationMode
- isPresented : false
二:如何才能获取到HostController
呢
方案1. 一些方案
下面的文正提供了一些尝试的方案,但不够SwiftUI风格
Controlling UIHostingController with SwiftUI View
方案2.@Environment
通过一段时间的SwiftUI
学习,发现通过@Environment
可以获取到很多当前页面的一些有用信息。 但是通过观察可用的字段并没有发现有关HostController
的线索。
那是否可以添加自定义字段呢?
可以! 查看EnvironmentKey
相关的文档或者注释我们可以发现,其中已经提供了自定义的示例代码:
You can create custom environment values by extending the EnvironmentValues structure with new properties. First declare a new environment key type and specify a value for the required defaultValue property:
Copy private struct MyEnvironmentKey : EnvironmentKey {
static let defaultValue: String = "Default value"
}
The Swift compiler automatically infers the associated Value type as the type you specify for the default value. Then use the key to define a new environment value property:
Copy extension EnvironmentValues {
var myCustomValue: String {
get { self [ MyEnvironmentKey. self ] }
set { self [ MyEnvironmentKey. self ] = newValue }
}
}
Clients of your environment value never use the key directly. Instead, they use the key path of your custom environment value property. To set the environment value for a view and all its subviews, add the environment(: :) view modifier to that view:
Copy MyView ()
. environment ( \.myCustomValue, "Another string" )
As a convenience, you can also define a dedicated view modifier to apply this environment value:
Copy extension View {
func myCustomValue ( _ myCustomValue : String ) -> some View {
environment ( \.myCustomValue, myCustomValue )
}
}
This improves clarity at the call site:
Copy MyView ()
. myCustomValue ( "Another string" )
To read the value from inside MyView or one of its descendants, use the Environment property wrapper:
Copy struct MyView : View {
@Environment ( \.myCustomValue ) var customValue: String
var body: some View {
Text ( customValue ) // Displays "Another value".
}
}
三:下面按照上面的思路实现一下
3.1 用以承载ViewController
的容器
Copy public struct ViewControllerHolder {
public weak var value: UIViewController ?
init ( _ value : UIViewController ? ) {
self. value = value
}
}
3.2 EnvironmentKey
Copy public struct ViewControllerKey : EnvironmentKey {
public static var defaultValue: ViewControllerHolder { return ViewControllerHolder ( nil ) }
}
3.3 Extension
Copy extension EnvironmentValues {
public var viewController: ViewControllerHolder {
get { return self [ ViewControllerKey. self ] }
set { self [ ViewControllerKey. self ] = newValue }
}
}
3.4 打开页面
Copy extension UIViewController {
public func present < Content : View > ( presentationStyle : UIModalPresentationStyle = .automatic, transitionStyle : UIModalTransitionStyle = .coverVertical, animated : Bool = true , completion : @escaping () -> Void = {}, @ ViewBuilder builder : () -> Content) {
let toPresent = UIHostingController ( rootView : AnyView ( EmptyView ()))
toPresent.modalPresentationStyle = presentationStyle
toPresent.rootView = AnyView (
builder ()
.environment ( \.viewController, ViewControllerHolder ( toPresent ))
)
if presentationStyle == .overCurrentContext {
toPresent. view .backgroundColor = .clear
}
self. present ( toPresent, animated : animated, completion : completion )
}
}
3.5 关闭页面
Copy struct ShopContentView : View {
...
// MARK: - Body
var body: some View {
ZStack ( alignment : .top ) {
...
viewControllerHolder. value ? . dismiss ( animated : false , completion : nil )
...
})
}
...
// MARK: - Var: Environment
@Environment ( \.viewController ) var viewControllerHolder
...
}
3.6 SwiftUIEx
这里进行了封装SwiftUIEx ,觉得有用欢迎留下一颗⭐️~
思考
通过@Environment
实现Dismiss
,了解了其更灵活的使用方式。也为解决之后开发中遇到的问题提供了一点思路。同时也使解决方式更加SwiftUI
风格