SwiftUI - есть ли аналог PopViewController в SwiftUI?
Я играл с 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")
}
}
}
Ответы
Ответ 1
Измените вашу структуру 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") }
}
}
Ответ 2
Кажется, что тонна базовых навигационных функций супер глючит, что вызывает разочарование и, возможно, на данный момент стоит отойти, чтобы сэкономить часы разочарования. Для меня PresentationButton - единственный, который работает. Вкладки TabbedView не работают должным образом, и NavigationButton не работает для меня вообще. Похоже, YMMV, если NavigationButton работает для вас.
Я надеюсь, что они исправят это одновременно с исправлением автозаполнения, что даст нам гораздо лучшее представление о том, что нам доступно. В то же время я неохотно пишу код и веду записи, когда появятся исправления. Отстойно, когда приходится выяснять, делаем ли мы что-то неправильно или просто не работает, но эта бета-версия для вас!
Ответ 3
Теперь есть возможность программно вставлять в NavigationView, если хотите. Это бета-версия 5.
Обратите внимание, что вам не нужна кнопка назад. Вы можете программно вызвать свойство showSelf в DetailView любым удобным для вас способом. И вам не нужно отображать текст "Push" в мастере. Это может быть 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
Ответ 4
Обновление: API NavigationDestinationLink в этом решении устарело начиная с iOS 13 Beta 5. Теперь рекомендуется использовать NavigationLink с привязкой isActive.
Я разобрался с решением для программного перетаскивания представлений в NavigationView, используя NavigationDestinationLink.
Вот простой пример:
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()
}
}
}
Я написал об этом в блоге здесь.
Ответ 5
Кажется, это работает для меня на watchOS (еще не пытался на iOS):
@Environment(\.presentationMode) var presentationMode
А потом, когда вам нужно выскочить
self.presentationMode.wrappedValue.dismiss()
Ответ 6
Ниже у меня работает в XCode11 GM
self.myPresentationMode.wrappedValue.dismiss()
Ответ 7
Я думаю, что это правильный способ создания модальных представлений в SwiftUI. Вы также можете использовать элемент трейлинг-бара или оба одновременно. Нажатие на кнопку предоставит пользователю недопустимый модальный вид.
struct ContentView: View {
var body: some View {
NavigationView {
// define some parts of your view, e.g. a rectangle
Rectangle()
.navigationBarTitle(Text("SomeTitle"),
displayMode:.inline )
.navigationBarItems(
leading: PresentationButton(
// add some image to the button
Image(systemName: "gear")
.imageScale(.large)
.accessibility(label: Text("Recommended accessibility label")),
// specify the destination, which is any view conforming to View protocol
destination: Text("Name of new view")
))
}
}
}
Ответ 8
РЕДАКТИРОВАТЬ: Этот ответ здесь лучше, чем мой, но оба работают: SwiftUI отклонить модальный
То, что вы действительно хотите (или должны хотеть), - это модальная презентация, о которой несколько человек упомянули здесь. Если вы пойдете по этому пути, вам определенно потребуется возможность программно отклонить модал, и у Эрики Садун есть отличный пример того, как это сделать здесь: https://ericasadun.com/2019/06/16/swiftui-modal -презентация/
Учитывая разницу между декларативным кодированием и императивным кодированием, решение там может быть неочевидным (например, переключение bool в false для исключения модального режима), но имеет смысл, если ваше состояние модели является источником истины, а не состояние самого интерфейса.
Вот мой быстрый пример Эрики, использующий привязку, переданную в 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)
}
}
Ответ 9
вместо NavigationButton используйте Navigation DestinationLink
но вы должны импортировать Combine
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")
}
}
}
Ответ 10
В пункте назначения передайте представление, которое вы хотите перенаправить, а внутри блока передайте данные, которые вы передадите в другое представление.
NavigationLink(destination: "Pass the particuter View") {
Text("Push")
}
Ответ 11
Возможно, если вы попытаетесь встроить этот последний BView в первый Aview чуть ниже предпоследней фигурной скобки... просто так:
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")
}
}
}