私はSwiftUIで遊んでいて、ボタンをタップしたときに前のビューに戻ることができるようにしたいので、popViewController
をUINavigationController
内で使用します。これまでに提供された方法はありますか?
私はNavigationDestinationLink
を使用して成功させることも試みました。
struct AView: View {
var body: some View {
NavigationView {
NavigationButton(destination: BView()) {
Text("Go to B")
}
}
}
}
struct BView: View {
var body: some View {
Button(action: {
// Trying to go back to the previous view
// previously: navigationController.popViewController(animated: true)
}) {
Text("Come back to A")
}
}
}
BView構造体を次のように変更します。ボタンは、popViewControllerがUIKitで行ったのと同じように実行されます。
struct BView: View {
@Environment(\.presentationMode) var mode: Binding<PresentationMode>
var body: some View {
Button(action: { self.mode.wrappedValue.dismiss() })
{ Text("Come back to A") }
}
}
必要に応じて、プログラムでNavigationViewをポップする方法があります。これはベータ5です。
戻るボタンは必要ないことに注意してください。プログラムによって、DetailViewのshowSelfプロパティを任意の方法でトリガーできます。また、マスターに「プッシュ」テキストを表示する必要はありません。それはEmptyView()である可能性があり、それによって目に見えないセグエを作成します。
(新しいNavigationLink機能は、非推奨のNavigationDestinationLinkを引き継ぎます)
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
MasterView()
}
}
}
struct MasterView: View {
@State 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
これは私にとってwatchOSで動作するようです(iOSでは試していません):
@Environment(\.presentationMode) var presentationMode
そして、あなたがポップする必要があるとき
self.presentationMode.wrappedValue.dismiss()
大量の基本的なナビゲーション機能は非常にバグが多いようです。これはがっかりすることであり、イライラの時間を節約するために、今は立ち去る価値があるかもしれません。私にとって、機能するのはPresentationButtonだけです。 TabbedViewタブが適切に機能せず、NavigationButtonがまったく機能しません。 NavigationButtonが機能する場合、YMMVのように聞こえます。
彼らがオートコンプリートを修正すると同時に修正されることを願っています。これにより、何が利用可能であるかについてより良い洞察が得られます。それまでの間、しぶしぶコーディングし、修正が出たときのメモをとっています。私たちが何か悪いことをしているのか、それともうまくいかないのかを理解するのは面倒ですが、それはあなたにとってベータ版です!
更新:このソリューションのNavigationDestinationLink APIは、iOS 13 Beta 5で非推奨になりました。isActiveバインディングでNavigationLinkを使用することをお勧めします。
NavigationDestinationLinkを使用して、NavigationViewのビューのプログラムによるプッシュ/ポップのソリューションを見つけました。
以下に簡単な例を示します。
import Combine
import SwiftUI
struct DetailView: View {
var onDismiss: () -> Void
var body: some View {
Button(
"Here are details. Tap to go back.",
action: self.onDismiss
)
}
}
struct MainView: View {
var link: NavigationDestinationLink<DetailView>
var publisher: AnyPublisher<Void, Never>
init() {
let publisher = PassthroughSubject<Void, Never>()
self.link = NavigationDestinationLink(
DetailView(onDismiss: { publisher.send() }),
isDetail: false
)
self.publisher = publisher.eraseToAnyPublisher()
}
var body: some View {
VStack {
Button("I am root. Tap for more details.", action: {
self.link.presented?.value = true
})
}
.onReceive(publisher, perform: { _ in
self.link.presented?.value = false
})
}
}
struct RootView: View {
var body: some View {
NavigationView {
MainView()
}
}
}
これについてはブログの投稿 here で書きました。
状態変数あり。やってみて。
struct ContentViewRoot: View {
@State var pushed: Bool = false
var body: some View {
NavigationView{
VStack{
NavigationLink(destination:ContentViewFirst(pushed: self.$pushed), isActive: self.$pushed) { EmptyView() }
.navigationBarTitle("Root")
Button("Push"){
self.pushed = true
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct ContentViewFirst: View {
@Binding var pushed: Bool
@State var secondPushed: Bool = false
var body: some View {
VStack{
NavigationLink(destination: ContentViewSecond(pushed: self.$pushed, secondPushed: self.$secondPushed), isActive: self.$secondPushed) { EmptyView() }
.navigationBarTitle("1st")
Button("Push"){
self.secondPushed = true;
}
}
}
}
struct ContentViewSecond: View {
@Binding var pushed: Bool
@Binding var secondPushed: Bool
var body: some View {
VStack{
Spacer()
Button("PopToRoot"){
self.pushed = false
} .navigationBarTitle("2st")
Spacer()
Button("Pop"){
self.secondPushed = false
} .navigationBarTitle("1st")
Spacer()
}
}
}
以下はXCode11 GMで私のために働きます
self.myPresentationMode.wrappedValue.dismiss()
編集:ここでのこの答えは私のものより優れていますが、どちらも機能します:SwiftUI dismiss modal
あなたが本当に欲しがっている(または欲しがるべき)のはモーダルプレゼンテーションであり、ここで数人が言及しました。あなたがその道を進むなら、あなたは間違いなくプログラム的にモーダルを閉じることができる必要があるでしょう、そしてエリカ・サドゥンはここでそれを行う方法の素晴らしい例を持っています: https://ericasadun.com/2019/06/16/swiftui-modal-presentation /
宣言型コーディングと命令型コーディングの違いを考えると、自明ではない解決策(ブール値をfalseに切り替えてモーダルを閉じるなど)があるかもしれませんが、モデルの状態が真ではなく、 UI自体の状態。
エリカの例を簡単に紹介します。TestModalに渡されたバインディングを使用して、ContentView自体のメンバーでなくてもそれを閉じることができるようにします(簡単にするために、エリカはそうです)。
struct TestModal: View {
@State var isPresented: Binding<Bool>
var body: some View {
Button(action: { self.isPresented.value = false }, label: { Text("Done") })
}
}
struct ContentView : View {
@State var modalPresented = false
var body: some View {
NavigationView {
Text("Hello World")
.navigationBarTitle(Text("View"))
.navigationBarItems(trailing:
Button(action: { self.modalPresented = true }) { Text("Show Modal") })
}
.presentation(self.modalPresented ? Modal(TestModal(isPresented: $modalPresented)) {
self.modalPresented.toggle()
} : nil)
}
}
@Environment(\.presentationMode) var presentationMode
を使用して、前のビューに戻ります。詳細については、以下のコードを確認してください。
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
Color.gray.opacity(0.2)
NavigationLink(destination: NextView(), label: {Text("Go to Next View").font(.largeTitle)})
}.navigationBarTitle(Text("This is Navigation"), displayMode: .large)
.edgesIgnoringSafeArea(.bottom)
}
}
}
struct NextView: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
ZStack {
Color.gray.opacity(0.2)
}.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: { Image(systemName: "arrow.left") }))
.navigationBarTitle("", displayMode: .inline)
}
}
struct NameRow: View {
var name: String
var body: some View {
HStack {
Image(systemName: "circle.fill").foregroundColor(Color.green)
Text(name)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Xcode 11.3
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
...
// If animation is strange, try this.
DispatchQueue.main.async {
self.presentationMode.wrappedValue.dismiss()
}
navigationButtonの代わりにNavigation DestinationLinkを使用します
しかし、あなたは結合をインポートする必要があります
struct AView: View {
var link: NavigationDestinationLink<BView>
var publisher: AnyPublisher<Void, Never>
init() {
let publisher = PassthroughSubject<Void, Never>()
self.link = NavigationDestinationLink(
BView(onDismiss: { publisher.send() }),
isDetail: false
)
self.publisher = publisher.eraseToAnyPublisher()
}
var body: some View {
NavigationView {
Button(action:{
self.link.presented?.value = true
}) {
Text("Go to B")
}.onReceive(publisher, perform: { _ in
self.link.presented?.value = false
})
}
}
}
struct BView: View {
var onDismiss: () -> Void
var body: some View {
Button(action: self.onDismiss) {
Text("Come back to A")
}
}
}