iOS SwiftUI:以编程方式弹出或关闭视图

时间:2019-06-09 09:45:05

标签: ios swift swiftui

我找不到有关通过SwiftUI呈现视图的弹出关闭 以编程方式的任何方式的参考。< / p>

在我看来,唯一的方法是对模态使用已经集成的幻灯片dow动作(以及是否/如何禁用此功能?),以及导航堆栈的后退按钮。

有人知道解决方案吗? 您知道这是一个错误还是会一直保持这种状态?

10 个答案:

答案 0 :(得分:19)

此示例使用Beta 5版本说明中记录的新环境var,该环境使用value属性。在更高的Beta中对其进行了更改,以使用wrappedValue属性。该示例现在是GM版本的最新示例。完全相同的概念可以消除使用.sheet修饰符显示的模态视图。

import SwiftUI

struct DetailView: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        Button(
            "Here is Detail View. Tap to go back.",
            action: { self.presentationMode.wrappedValue.dismiss() }
        )
    }
}

struct RootView: View {
    var body: some View {
        VStack {
            NavigationLink(destination: DetailView())
            { Text("I am Root. Tap for Detail View.") }
        }
    }
}

struct ContentView: View {
    var body: some View {
        NavigationView {
            RootView()
        }
    }
}

答案 1 :(得分:6)

现在,您可以根据需要以编程方式弹出NavigationView。这是beta5。请注意,您不需要后退按钮。您可以通过任何方式以编程方式在DetailView中触发showSelf属性。而且您不必在母版中显示“推送”文本。可能是EmptyView(),从而创建了不可见的序列。

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            MasterView()
        }
    }
}

struct MasterView: View {
    @State private var showDetail = false

    var body: some View {
        VStack {
            NavigationLink(destination: DetailView(showSelf: $showDetail), isActive: $showDetail) {
                Text("Push")
            }
        }
    }
}

struct DetailView: View {
    @Binding var showSelf: Bool

    var body: some View {
        Button(action: {
            self.showSelf = false
        }) {
            Text("Pop")
        }
    }
}

#if DEBUG
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

答案 2 :(得分:6)

我最近创建了一个名为swiftui-navigation-stackhttps://github.com/biobeats/swiftui-navigation-stack)的开源项目,其中包含NavigationStackView,这是SwiftUI的替代导航堆栈。它提供了回购自述文件中描述的几个功能。例如,您可以轻松地以编程方式推送和弹出视图。我将通过一个简单的示例向您展示如何做到这一点:

首先将层次结构嵌入NavigationStackVew

struct RootView: View {
    var body: some View {
        NavigationStackView {
            View1()
        }
    }
}

NavigationStackView使您的层次结构可以访问名为NavigationStack的有用环境对象。例如,您可以根据上面的问题使用它来以编程方式弹出视图:

struct View1: View {
    var body: some View {
        ZStack {
            Color.yellow.edgesIgnoringSafeArea(.all)
            VStack {
                Text("VIEW 1")
                Spacer()

                PushView(destination: View2()) {
                    Text("PUSH TO VIEW 2")
                }
            }
        }
    }
}

struct View2: View {
    @EnvironmentObject var navStack: NavigationStack
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)
            VStack {
                Text("VIEW 2")
                Spacer()

                Button(action: {
                    self.navStack.pop()
                }, label: {
                    Text("PROGRAMMATICALLY POP TO VIEW 1")
                })
            }
        }
    }
}

在此示例中,我使用PushView来轻按以触发推送导航。然后,在View2中,我使用环境对象以编程方式返回。

这是完整的示例:

import SwiftUI
import NavigationStack

struct RootView: View {
    var body: some View {
        NavigationStackView {
            View1()
        }
    }
}

struct View1: View {
    var body: some View {
        ZStack {
            Color.yellow.edgesIgnoringSafeArea(.all)
            VStack {
                Text("VIEW 1")
                Spacer()

                PushView(destination: View2()) {
                    Text("PUSH TO VIEW 2")
                }
            }
        }
    }
}

struct View2: View {
    @EnvironmentObject var navStack: NavigationStack
    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.all)
            VStack {
                Text("VIEW 2")
                Spacer()

                Button(action: {
                    self.navStack.pop()
                }, label: {
                    Text("PROGRAMMATICALLY POP TO VIEW 1")
                })
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        RootView()
    }
}

结果是:

enter image description here

答案 3 :(得分:2)

您可以尝试使用自定义视图和/var/www/html/my-app/index.html

这是一个自定义模式。

  # If an existing asset or directory is requested go to it as it is
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
  RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
  RewriteRule ^ - [L]

 # If it is against the gateway go to the gateway (APIs)
 RewriteCond %{REQUEST_URI} ^/gateway*
 RewriteRule ^ - [L]

 # If the requested resource doesn't exist, use index.html
 RewriteRule ^ /index.html

我重用了Animation Views and Transition教程中的Transition

它的定义如下:

struct ModalView<Content>: View where Content: View {

    @Binding var isShowing: Bool
    var content: () -> Content

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .center) {
                if (!self.isShowing) {
                    self.content()
                }
                if (self.isShowing) {
                    self.content()
                        .disabled(true)
                        .blur(radius: 3)

                    VStack {
                        Text("Modal")
                    }
                    .frame(width: geometry.size.width / 2,
                           height: geometry.size.height / 5)
                    .background(Color.secondary.colorInvert())
                    .foregroundColor(Color.primary)
                    .cornerRadius(20)
                    .transition(.moveAndFade) // associated transition to the modal view
                }
            }
        }
    }

}

您可以在模拟器中而不是在预览中进行测试,如下所示:

Transition.moveAndFade

由于进行了这种转换,您将看到模式extension AnyTransition { static var moveAndFade: AnyTransition { let insertion = AnyTransition.move(edge: .trailing) .combined(with: .opacity) let removal = AnyTransition.scale() .combined(with: .opacity) return .asymmetric(insertion: insertion, removal: removal) } } ,它将出现struct ContentView: View { @State var isShowingModal: Bool = false func toggleModal() { DispatchQueue.main.asyncAfter(deadline: .now() + 3) { withAnimation { self.isShowingModal = true } DispatchQueue.main.asyncAfter(deadline: .now() + 3) { withAnimation { self.isShowingModal = false } } } } var body: some View { ModalView(isShowing: $isShowingModal) { NavigationView { List(["1", "2", "3", "4", "5"].identified(by: \.self)) { row in Text(row) }.navigationBarTitle(Text("A List"), displayMode: .large) }.onAppear { self.toggleModal() } } } }

答案 4 :(得分:2)

请检查以下代码,它是如此简单。

FirstView

struct StartUpVC: View {
@State var selection: Int? = nil

var body: some View {
    NavigationView{
        NavigationLink(destination: LoginView().hiddenNavigationBarStyle(), tag: 1, selection: $selection) {
            Button(action: {
                print("Signup tapped")
                self.selection = 1
            }) {
                HStack {
                    Spacer()
                    Text("Sign up")
                    Spacer()
                }
            }
        }
    }
}

SecondView

struct LoginView: View {
@Environment(\.presentationMode) var presentationMode
    
var body: some View {
    NavigationView{
        Button(action: {
           print("Login tapped")
           self.presentationMode.wrappedValue.dismiss()
        }) {
           HStack {
              Image("Back")
              .resizable()
              .frame(width: 20, height: 20)
              .padding(.leading, 20)
           }
        }
      }
   }
}

答案 5 :(得分:2)

iOS 15

从 iOS 15 开始,我们可以使用新的 @Environment(\.dismiss)

struct SheetView: View {
    @Environment(\.dismiss) var dismiss

    var body: some View {
        NavigationView {
            Text("Sheet")
                .toolbar {
                    Button("Done") {
                        dismiss()
                    }
                }
        }
    }
}

(不再需要使用 presentationMode.wrappedValue.dismiss()。)


有用的链接:

答案 6 :(得分:1)

SwiftUI Xcode 11.0

首先,声明@Environment,它具有一个dismiss方法,您可以在任何地方使用它来关闭视图。

<TextInput
           onChangeText={value => onSetComment(value, item.id)}
            label='Write Comment'
            underlineColor="#36a"
            value={item.commentValue}
            style={{backgroundColor:'#fff', width:'90%'}}
          />

{
  item.commentValue &&
  <Icon
  //  iconStyle={[(isEnabled=== true) ? styles.likedColor : styles.unLikedColor]}
    iconStyle={styles.likedColor}
    name='md-arrow-round-forward'
    type='ionicon'
    color='#999'
    onPress={() => {onCommentPost(item)}}
  />
}

答案 7 :(得分:0)

SwiftUI的核心概念是监视数据流。

您必须使用@State变量,并对该变量的值进行更改,以控制弹出和关闭。

struct MyView: View {
    @State
    var showsUp = false

    var body: some View {
        Button(action: { self.showsUp.toggle() }) {
            Text("Pop")
        }
        .presentation(
            showsUp ? Modal(
                Button(action: { self.showsUp.toggle() }) {
                    Text("Dismiss")
                }
            ) : nil
        )
    }
}

答案 8 :(得分:0)

我在尝试对presentationMode绑定调用value时遇到编译器问题。将属性更改为wrappedValue可以解决我的问题。我假设value-> wrappedValue是语言更新。我认为此注释更适合作为对Chuck H的答案的评论,但没有足够的代表要发表评论,我也建议将此更改作为and edit,但我的编辑被拒绝为更适合作为评论或答案。

答案 9 :(得分:0)

或者,如果您不想通过按钮以编程方式执行此操作,则可以在需要弹出时从视图模型中发出它。 订阅一个@Published,只要保存完成,该值就会更改。

struct ContentView: View {
    @ObservedObject var viewModel: ContentViewModel
    @Environment(\.presentationMode) var presentationMode

    init(viewModel: ContentViewModel) {
        self.viewModel = viewModel
    }

    var body: some View {
        Form {
            TextField("Name", text: $viewModel.name)
                .textContentType(.name)
        }
        .onAppear {
            self.viewModel.cancellable = self.viewModel
                .$saved
                .sink(receiveValue: { saved in
                    guard saved else { return }
                    self.presentationMode.wrappedValue.dismiss()
                }
            )
        }
    }
}

class ContentViewModel: ObservableObject {
    @Published var saved = false // This can store any value.
    @Published var name = ""
    var cancellable: AnyCancellable? // You can use a cancellable set if you have multiple observers.

    func onSave() {
        // Do the save.

        // Emit the new value.
        saved = true
    }
}