【问题标题】:Present a new view in SwiftUI在 SwiftUI 中呈现新视图
【发布时间】:2020-03-16 10:29:44
【问题描述】:

我想单击一个按钮,然后在 UIKit 中呈现一个新视图,例如 present modally

我已经看到“How to present a new view using sheets”,但我不想将它作为模态表附加到主视图。

而且我不想使用NavigationLink,因为我不希望新视图和旧视图有导航关系。

感谢您的帮助...

【问题讨论】:

  • 为什么你不想将它作为模态表附加到主视图?即使在UIKit 中,它也是一种标准方法。你有什么特殊原因吗?
  • 我试着解释一下我的想法...如果有任何错误,请纠正我。
  • Apps有3个view,1:登录页面2:TableView页面3:TableDetail页面,TableView页面和TableDetail页面是导航关系。登录后会呈现到TableView页面,TableView页面与登录后的登录页面没有任何关系
  • 所以你需要fullscreen 对吗?
  • 是的!我要fullscreen

标签: ios swiftui


【解决方案1】:

免责声明: 下面并不是真正的“原生模式”,既不是行为也不是外观和感觉,但如果有人需要一个视图相对于其他视图的自定义转换,只激活顶部视图,则如下方法可能会有所帮助。

所以,如果您期望如下所示

这是一个简单的代码演示方法(可根据需要更改动画和过渡参数)

struct ModalView : View {
    @Binding var activeModal: Bool
    var body : some View {
        VStack {
            Button(action: {
                withAnimation(.easeInOut(duration: 0.3)) {
                    self.activeModal = false
                }
            }) {
                Text("Hide modal")
            }
            Text("Modal View")
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
        .background(Color.green)
    }
}

struct MainView : View {
    @Binding var activeModal: Bool
    var body : some View {
        VStack {
            Button(action: {
                withAnimation(.easeInOut(duration: 0.3)) {
                    self.activeModal = true
                }
            }) {
                Text("Show modal")
            }
            Text("Main View")
        }
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
        .background(Color.yellow)
    }
}

struct ModalContainer: View {
    @State var showingModal = false
    var body: some View {
        ZStack {
            MainView(activeModal: $showingModal)
                .allowsHitTesting(!showingModal)
            if showingModal {
                ModalView(activeModal: $showingModal)
                    .transition(.move(edge: .bottom))
                    .zIndex(1)
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    这是一种简单的方法 - 前向视图。这很简单。

            struct ChildView: View{
               private  let colors: [Color] = [.red, .yellow,.green,.white]
               @Binding var index : Int
               var body: some View {
               let next = (self.index+1)  % MyContainer.totalChildren
                 return   ZStack{
                        colors[self.index  % colors.count]
                         Button("myNextView \(next)   ", action: {
                        withAnimation{
                            self.index = next
                        }
                        }
                    )}.transition(.asymmetric(insertion: .move(edge: .trailing)  , removal:  .move(edge: .leading)  ))
                }
            }
    
            struct MyContainer: View {
                static var totalChildren = 10
                @State private var value: Int = 0
                var body: some View {
                        HStack{
                            ForEach(0..<(Self.totalChildren) ) { index in
                                Group{
                                if    index == self.value {
                                    ChildView(index:  self.$value)
                                    }}
                                }
                    }
                    }
            }
    

    【讨论】:

      【解决方案3】:

      显示模式(iOS 13 风格)

      您只需要一个简单的sheet 即可自行关闭:

      struct ModalView: View {
          @Binding var presentedAsModal: Bool
          var body: some View {
              Button("dismiss") { self.presentedAsModal = false }
          }
      }
      

      然后像这样呈现它:

      struct ContentView: View {
          @State var presentingModal = false
          
          var body: some View {
              Button("Present") { self.presentingModal = true }
              .sheet(isPresented: $presentingModal) { ModalView(presentedAsModal: self.$presentingModal) }
          }
      }
      

      请注意我将presentingModal 传递给模态框,因此您可以将其从模态框本身中消除,但您可以摆脱它。


      让它真实呈现fullscreen(不仅仅是视觉上)

      您需要访问ViewController。所以你需要一些辅助容器和环境的东西:

      struct ViewControllerHolder {
          weak var value: UIViewController?
      }
      
      struct ViewControllerKey: EnvironmentKey {
          static var defaultValue: ViewControllerHolder {
              return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController)
      
          }
      }
      
      extension EnvironmentValues {
          var viewController: UIViewController? {
              get { return self[ViewControllerKey.self].value }
              set { self[ViewControllerKey.self].value = newValue }
          }
      }
      

      那么你应该使用实现这个扩展:

      extension UIViewController {
          func present<Content: View>(style: UIModalPresentationStyle = .automatic, @ViewBuilder builder: () -> Content) {
              let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
              toPresent.modalPresentationStyle = style
              toPresent.rootView = AnyView(
                  builder()
                      .environment(\.viewController, toPresent)
              )
              NotificationCenter.default.addObserver(forName: Notification.Name(rawValue: "dismissModal"), object: nil, queue: nil) { [weak toPresent] _ in
                  toPresent?.dismiss(animated: true, completion: nil)
              }
              self.present(toPresent, animated: true, completion: nil)
          }
      }
      

      终于

      你可以让它fullscreen喜欢:

      struct ContentView: View {
          @Environment(\.viewController) private var viewControllerHolder: UIViewController?
          
          var body: some View {
              Button("Login") {
                  self.viewControllerHolder?.present(style: .fullScreen) {
                      Text("Main") // Or any other view you like
      // uncomment and add the below button for dismissing the modal
                  // Button("Cancel") {
                  //       NotificationCenter.default.post(name: Notification.Name(rawValue: "dismissModal"), object: nil)
                  //        }
                  }
              }
          }
      }
      

      【讨论】:

      • 我在环境属性包装器中收到此错误:无法将类型“Environment”的值转换为指定类型“UIViewController”
      • 它应该被默认处理,但是尝试在行尾添加?。 @jsbeginnerNodeJS
      • 我在控制台中收到此错误:``` 警告:尝试在 <_ttgc7swiftui19uihostingcontrollervs_7anyview_:> 上显示 <_ttgc7swiftui19uihostingcontrollervs_7anyview_:>,其视图不在窗口层次结构中!```
      • 你怎么解散它?
      • @gabrielapittari dismiss
      【解决方案4】:

      对于 iOS 14 和 Xcode 12:

      struct ContentView: View {
          @State private var isPresented = false
      
      var body: some View {
          Button("Show Modal with full screen") {
              self.isPresented.toggle()
          }
          .fullScreenCover(isPresented: $isPresented, content: FullScreenModalView.init)
          }
      }
      
      struct FullScreenModalView: View {
           @Environment(\.presentationMode) var presentationMode
      
      var body: some View {
          VStack {
              Text("This is a modal view")
          }
          .frame(maxWidth: .infinity, maxHeight: .infinity)
          .background(Color.red)
          .edgesIgnoringSafeArea(.all)
          .onTapGesture {
              presentationMode.wrappedValue.dismiss()
          }
      }
      }
      

      希望这个答案可以帮助到大家!请在下方评论您的结果。

      参考:This Link

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-07-29
        • 1970-01-01
        • 2022-07-12
        • 2020-05-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多