Показывать ведущую кнопку navigationBarItems, только если она отображается как модальная - PullRequest
0 голосов
/ 06 марта 2020

У меня есть представление, которое может быть показано как модальное, или просто помещено в стек навигации. Когда она нажата, она имеет кнопку «Назад» в левом верхнем углу, и когда она отображается как модальная, я хочу добавить кнопку «Закрыть» (многие из моих тестеров не могли легко понять, что они могут скользить вниз по модальному и действительно ожидается явная кнопка закрытия).

Теперь у меня несколько проблем.

  1. Как мне определить, отображается ли представление модально или нет? Или, в качестве альтернативы, если это не первый взгляд на стек навигации? В UIKit есть несколько способов легко сделать это. Добавление переменной presentationMode @Environment не помогает, поскольку ее значение isPresented также верно для всплывающих экранов. Я, конечно, мог бы передать переменную isModal сам, но мне кажется странным, что это единственный путь?
  2. Как условно добавить ведущий navigationBarItem? Проблема в том, что если вы введете nil, даже кнопка возврата по умолчанию будет скрыта.

Код для копирования и вставки в XCode и воспроизведения с:

import SwiftUI

struct ContentView: View {
  @State private var showModal = false

  var body: some View {
    NavigationView {
      VStack(spacing: 20) {
        Button("Open modally") {
          self.showModal = true
        }

        NavigationLink("Push", destination: DetailView(isModal: false))
      }
      .navigationBarTitle("Home")
    }
    .sheet(isPresented: $showModal) {
      NavigationView {
        DetailView(isModal: true)
      }
    }
  }
}

struct DetailView: View {
  @Environment(\.presentationMode) private var presentationMode
  let isModal: Bool

  var body: some View {
    Text("Hello World")
      .navigationBarTitle(Text("Detail"), displayMode: .inline)
      .navigationBarItems(leading: closeButton, trailing: deleteButton)
  }

  private var closeButton: some View {
    Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
      Image(systemName: "xmark")
        .frame(height: 36)
    }
  }

  private var deleteButton: some View {
    Button(action: { print("DELETE") }) {
      Image(systemName: "trash")
        .frame(height: 36)
    }
  }
}

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

Если Я изменяю closeButton, чтобы вернуть необязательный AnyView?, а затем возвращаю nil, когда isModal ложно, я вообще не получаю кнопку возврата. Я также не могу звонить navigationBarItems дважды, один раз с ведущей и один раз с конечной кнопкой, потому что последний вызов отменяет первый вызов. Я как бы застрял здесь.

Ответы [ 3 ]

1 голос
/ 06 марта 2020

Не вижу никаких проблем, просто добавьте кнопку «Отключить» на панель навигации. Вам нужно только перестроить иерархию View, и нет необходимости передавать какие-либо привязки к вашему DetailView

import SwiftUI

struct DetailView: View {
    var body: some View {
        Text("Detail View")
    }
}
struct ContentView: View {
    @State var sheet = false
    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Button("Open modally") {
                    self.sheet = true
                }

                NavigationLink("Push", destination: DetailView())
            }.navigationBarTitle("Home")
        }
        .sheet(isPresented: $sheet) {
            NavigationView {
                DetailView().navigationBarTitle("Title").navigationBarItems(leading: Button(action: {
                    self.sheet.toggle()
                }, label: {
                    Text("Dismiss")
                }))
            }
        }
    }
}



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

enter image description here

Вы по-прежнему можете отклонить его с помощью проведите пальцем вниз, вы можете добавить несколько кнопок (как часть объявления DetailView) ... et c.

При нажатии у вас есть кнопка возврата по умолчанию, если она отображается модально, у вас действительно есть кнопка отклонения.

ОБНОВЛЕНИЕ (на основе обсуждения)

.sheet(isPresented: $sheet) {
    NavigationView {
        GeometryReader { proxy in
            DetailView().navigationBarTitle("Title")
                .navigationBarItems(leading:
                    HStack {

                        Button(action: {
                            self.sheet.toggle()
                        }, label: {
                            Text("Dismiss").padding(.horizontal)
                        })
                        Color.clear
                        Button(action: {

                        }, label: {
                            Image(systemName: "trash")
                                .imageScale(.large)
                                .padding(.horizontal)
                        })

                    }.frame(width: proxy.size.width)
            )
        }
    }
}

enter image description here

наконец Я предлагаю вас использовать

extension View {
    @available(watchOS, unavailable)
    public func navigationBarItems<L, T>(leading: L?, trailing: T) -> some View where L : View, T : View {
        Group {
            if leading != nil {
                self.navigationBarItems(leading: leading!, trailing: trailing)
            } else {
                self.navigationBarItems(trailing: trailing)
            }
        }
    }
}
0 голосов
/ 06 марта 2020

Хорошо, мне это удалось. Это не красиво, и я очень открыт для различных предложений, но это работает ?

import SwiftUI

extension View {
  func eraseToAnyView() -> AnyView {
    AnyView(self)
  }

  public func conditionalNavigationBarItems(_ condition: Bool, leading: AnyView, trailing: AnyView) -> some View {
    Group {
      if condition {
        self.navigationBarItems(leading: leading, trailing: trailing)
      } else {
        self
      }
    }
  }
}

struct ContentView: View {
  @State private var showModal = false

  var body: some View {
    NavigationView {
      VStack(spacing: 20) {
        Button("Open modally") {
          self.showModal = true
        }

        NavigationLink("Push", destination: DetailView(isModal: false))
      }
      .navigationBarTitle("Home")
    }
    .sheet(isPresented: $showModal) {
      NavigationView {
        DetailView(isModal: true)
      }
    }
  }
}

struct DetailView: View {
  @Environment(\.presentationMode) private var presentationMode
  let isModal: Bool

  var body: some View {
    Text("Hello World")
      .navigationBarTitle(Text("Detail"), displayMode: .inline)
      .navigationBarItems(trailing: deleteButton)
      .conditionalNavigationBarItems(isModal, leading: closeButton, trailing: deleteButton)
  }

  private var closeButton: AnyView {
    Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
      Image(systemName: "xmark")
        .frame(height: 36)
    }.eraseToAnyView()
  }

  private var deleteButton: AnyView {
    Button(action: { print("DELETE") }) {
      Image(systemName: "trash")
        .frame(height: 36)
    }.eraseToAnyView()
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}
0 голосов
/ 06 марта 2020

Всякий раз, когда мы предоставляем .navigationBarItems(leading: _anything_), ie что-нибудь , стандартная кнопка возврата исчезает, поэтому вы должны предоставить свою собственную кнопку возврата условно.

Работает следующий подход ( протестировано с Xcode 11.2 / iOS 13.2)

    .navigationBarItems(leading: Group {
        if isModal {
            closeButton
        } else {
            // custom back button here calling same dismiss
        }
    }, trailing: deleteButton)

Обновление: альтернативный подход может быть следующим (проверено в том же)

var body: some View {
    VStack {
        if isModal {
            Text("Hello")
                .navigationBarItems(leading: closeButton, trailing: deleteButton)
        } else {
            Text("Hello")
                .navigationBarItems(trailing: deleteButton)
        }
    }
    .navigationBarTitle("Test", displayMode: .inline)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...