Я хочу добавить пользовательскую кнопку навигации, которая будет выглядеть примерно так:
Теперь я написал специальное представление BackButton для этого. При применении этого представления в качестве ведущего элемента панели навигации выполните следующие действия:
.navigationBarItems(leading: BackButton())
...вид навигации выглядит так:
Я играл с такими модификаторами, как:
.navigationBarItem(title: Text(""), titleDisplayMode: .automatic, hidesBackButton: true)
без везения.
Как я могу...
.navigationBarHidden(true)




Я нашел это: https://ryanashcraft.me/swiftui-programmatic-navigation/
Это работает, и может заложить основу для конечного автомата, чтобы контролировать то, что показывается, но это не так просто, как было раньше.
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 RootView: 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 ContentView: View {
var body: some View {
NavigationView {
RootView()
}
}
}
If you want to hide the button then you can replace the DetailView with this:
struct LocalDetailView: View {
var onDismiss: () -> Void
var body: some View {
Button(
"Here are details. Tap to go back.",
action: self.onDismiss
)
.navigationBarItems(leading: Text(""))
}
}
Вы знаете, что можете редактировать свои ответы? Не публикуйте новый, а нажмите маленькую Кнопка редактировать внизу вашего ответа. Затем удалите этот.
Улучшенная версия. (Свифт, iOS 13 бета 4) stackoverflow.com/questions/56853828/…
Интересно, что меня взорвали за публикацию ссылки на то, что моя учетная запись была строго ограничена, но этот последний ответ проходит отлично.
Используйте это, чтобы перейти к к вашему представлению:
NavigationLink(destination: SampleDetails()) {}
Добавьте это к самому представлению:
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
Затем в действии кнопки или что-то в этом роде отклоните представление:
presentationMode.wrappedValue.dismiss()
От родителя перейдите с помощью NavigationLink
NavigationLink(destination: SampleDetails()) {}
В DetailsView скройте navigationBarBackButton и установите пользовательскую кнопку «Назад» на ведущую navigationBarItem,
struct SampleDetails: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var btnBack : some View { Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image("ic_back") // set image here
.aspectRatio(contentMode: .fit)
.foregroundColor(.white)
Text("Go back")
}
}
}
var body: some View {
List {
Text("sample code")
}
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: btnBack)
}
}
это отлично работает, за исключением того, что PresentationMode.value теперь является PresentationMode.wrappedValue, однако это, похоже, отключает поведение прокрутки по умолчанию, чтобы вернуться назад. Любая идея о том, как включить его снова?
Любая идея о том, как добавить салфетки обратно?
Спасибо, работает нормально, но мы должны позаботиться о размере изображения. Всегда помните метод .resizable() для пользовательского размера кадра.
исправляет пролистывание: stackoverflow.com/a/60067845/196555
Когда вы хотите выполнить что-то .onAppear в Parent, с фактической навигацией по кнопке «Назад» это сработает. Но с вашей кнопкой возврата кода это не сработает
положение кнопки «Назад» немного правильное по сравнению с ожидаемым, поэтому я должен использовать .offset(x, -10). Затем возникла другая проблема: нет ответа, если вы коснетесь кнопки «Назад», так как теперь у вас есть смещение. Как я могу решить проблему?
@LiangWang Я обнаружил, что обертывание Button в HStack с отрицательным дополнением сохраняет сенсорную область неизменной. (Если вы установите фон для своей кнопки, вы увидите разницу по сравнению со смещением или дополнением самой кнопки.)
При этом вы можете получить стрелку исходного размера: Image(systemName: "chevron.backward").imageScale(Image.Scale.large)
Похоже, теперь вы можете комбинировать navigationBarBackButtonHidden и .navigationBarItems, чтобы получить эффект, которого вы пытаетесь достичь.
struct Navigation_CustomBackButton_Detail: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
ZStack {
Color("Theme3BackgroundColor")
VStack(spacing: 25) {
Image(systemName: "globe").font(.largeTitle)
Text("NavigationView").font(.largeTitle)
Text("Custom Back Button").foregroundColor(.gray)
HStack {
Image("NavBarBackButtonHidden")
Image(systemName: "plus")
Image("NavBarItems")
}
Text("Hide the system back button and then use the navigation bar items modifier to add your own.")
.frame(maxWidth: .infinity)
.padding()
.background(Color("Theme3ForegroundColor"))
.foregroundColor(Color("Theme3BackgroundColor"))
Spacer()
}
.font(.title)
.padding(.top, 50)
}
.navigationBarTitle(Text("Detail View"), displayMode: .inline)
.edgesIgnoringSafeArea(.bottom)
// Hide the system back button
.navigationBarBackButtonHidden(true)
// Add your custom back button here
.navigationBarItems(leading:
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image(systemName: "arrow.left.circle")
Text("Go Back")
}
})
}
}
Основываясь на других ответах здесь, это упрощенный ответ для варианта 2, работающего у меня в XCode 11.0:
struct DetailView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "gobackward").padding()
}
.navigationBarHidden(true)
}
}
Примечание. Чтобы панель навигации была скрыта, мне также нужно было установить, а затем скрыть панель навигации в ContentView.
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: DetailView()) {
Text("Link").padding()
}
} // Main VStack
.navigationBarTitle("Home")
.navigationBarHidden(true)
} //NavigationView
}
}
Работал как шарм в XCode 11! Спасибо
Я ожидаю, что вы захотите использовать пользовательскую кнопку «Назад» на всех навигационных экранах, поэтому я написал собственную оболочку на основе ответа @Ashish.
struct NavigationItemContainer<Content>: View where Content: View {
private let content: () -> Content
@Environment(\.presentationMode) var presentationMode
private var btnBack : some View { Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack {
Image("back_icon") // set image here
.aspectRatio(contentMode: .fit)
.foregroundColor(.black)
Text("Go back")
}
}
}
var body: some View {
content()
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: btnBack)
}
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
}
Оберните содержимое экрана в NavigationItemContainer:
Применение:
struct CreateAccountScreenView: View {
var body: some View {
NavigationItemContainer {
VStack(spacing: 21) {
AppLogoView()
//...
}
}
}
}
Проблема в вашем CreateAccountScreenView. Если у вас есть другие элементы навигации в конце CreateAccountScreenView, то только конечные или ведущие элементы навигации. Это потому, что вы не можете определить navigationBarItems дважды, и работает только один
Все решения, которые я вижу здесь, похоже, отключают функцию прокрутки, чтобы вернуться к предыдущей странице, поэтому делюсь найденным решением, которое поддерживает эту функциональность. Вы можете сделать расширение своего корневого представления и переопределить свой стиль навигации и вызвать функцию в инициализаторе представления.
Образец просмотра
struct SampleRootView: View {
init() {
overrideNavigationAppearance()
}
var body: some View {
Text("Hello, World!")
}
}
Расширение
extension SampleRootView {
func overrideNavigationAppearance() {
let navigationBarAppearance = UINavigationBarAppearance()
let barAppearace = UINavigationBar.appearance()
barAppearace.tintColor = *desired UIColor for icon*
barAppearace.barTintColor = *desired UIColor for icon*
navigationBarAppearance.setBackIndicatorImage(*desired UIImage for custom icon*, transitionMaskImage: *desired UIImage for custom icon*)
UINavigationBar.appearance().standardAppearance = navigationBarAppearance
UINavigationBar.appearance().compactAppearance = navigationBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance
}
}
Единственным недостатком этого подхода является то, что я не нашел способа удалить/изменить текст, связанный с настраиваемой кнопкой «Назад».
Пролистывание таким образом не отключается.
Работает на меня. X-код 11.3.1
Поместите это в свой корневой вид
init() {
UINavigationBar.appearance().isUserInteractionEnabled = false
UINavigationBar.appearance().backgroundColor = .clear
UINavigationBar.appearance().barTintColor = .clear
UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
UINavigationBar.appearance().shadowImage = UIImage()
UINavigationBar.appearance().tintColor = .clear
}
И это в вашем детском представлении
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
Button(action: {self.presentationMode.wrappedValue.dismiss()}) {
Image(systemName: "gobackward")
}
Спасибо! Это отличное решение для iOS 13 и iOS 14. Работа в Xcode 12.5.1.
Это решение работает для iPhone. Однако для iPad это не сработает из-за splitView.
import SwiftUI
struct NavigationBackButton: View {
var title: Text?
@Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
var body: some View {
ZStack {
VStack {
ZStack {
HStack {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
Image(systemName: "chevron.left")
.font(.title)
.frame(width: 44, height: 44)
title
}
Spacer()
}
}
Spacer()
}
}
.zIndex(1)
.navigationBarTitle("")
.navigationBarHidden(true)
}
}
struct NavigationBackButton_Previews: PreviewProvider {
static var previews: some View {
NavigationBackButton()
}
}
Действительно простой метод. Всего две строки кода ?
@Environment(\.presentationMode) var presentationMode
self.presentationMode.wrappedValue.dismiss()
Пример:
import SwiftUI
struct FirstView: View {
@State var showSecondView = false
var body: some View {
NavigationLink(destination: SecondView(),isActive : self.$showSecondView){
Text("Push to Second View")
}
}
}
struct SecondView : View{
@Environment(\.presentationMode) var presentationMode
var body : some View {
Button(action:{ self.presentationMode.wrappedValue.dismiss() }){
Text("Go Back")
}
}
}
Вы можете использовать UIAppearance для этого:
if let image = UIImage(named: "back-button") {
UINavigationBar.appearance().backIndicatorImage = image
UINavigationBar.appearance().backIndicatorTransitionMaskImage = image
}
Это должно быть добавлено в ваше приложение на ранней стадии, например App.init. Это также сохраняет встроенную функцию прокрутки назад.
Вот более сжатая версия, использующая принципы, показанные в других комментариях, чтобы изменить только текст кнопки. Значок chevron.left также можно легко заменить другим значком.
Создайте свою собственную кнопку, а затем назначьте ее с помощью .navigationBarItems(). Я обнаружил, что следующий формат наиболее близок к кнопке «Назад» по умолчанию.
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var backButton : some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}) {
HStack(spacing: 0) {
Image(systemName: "chevron.left")
.font(.title2)
Text("Cancel")
}
}
}
Убедитесь, что вы используете .navigationBarBackButtonHidden(true), чтобы скрыть кнопку по умолчанию и заменить ее своей собственной!
List(series, id:\.self, selection: $selection) { series in
Text(series.SeriesLabel)
}
.navigationBarBackButtonHidden(true)
.navigationBarItems(leading: backButton)
Это работает хорошо, но есть ли лучший способ, чтобы backButton можно было использовать в нескольких представлениях?
Просто напишите это:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
}.onAppear() {
UINavigationBar.appearance().tintColor = .clear
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "back")?.withRenderingMode(.alwaysOriginal)
UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "back")?.withRenderingMode(.alwaysOriginal)
}
}
}
это как бы «работает», но новое изображение кнопки «Назад» не выровнено правильно. Код может нуждаться в некоторых корректировках. Кроме того, может быть, было бы лучше поместить его в init() {}?
Улучшенная версия. (Свифт, iOS 13 бета 4) stackoverflow.com/questions/56853828/…