У меня есть базовое представление с кнопкой, использующей SwiftUI, и я пытаюсь представить новый экран/представление при нажатии кнопки. Как мне это сделать? Должен ли я создать делегата для этого представления, который сообщит приложению SceneDelegate представить новый контроллер представления?
import SwiftUI
struct ContentView : View {
var body: some View {
VStack {
Text("Hello World")
Button(action: {
//go to another view
}) {
Text("Do Something")
.font(.largeTitle)
.fontWeight(.ultraLight)
}
}
}
}





Ключ в том, чтобы использовать NavigationView и NavigationLink:
import SwiftUI
struct ContentView : View {
var body: some View {
NavigationView {
VStack {
Text("Hello World")
NavigationLink(destination: DetailView()) {
Text("Do Something")
}
}
}
}
}
NB На данный момент этот подход не работает на Apple Watch, поскольку «NavigationView» недоступен в watchOS. Вы можете использовать только NavigationLink, как описано здесь.
Это работает, но тесно связывает эти представления. Есть ли лучший подход, при котором одно представление не знает о другом?
Есть ли способ сделать это с помощью Button вместо уродливой NavigationLink?
Необходимо создать DetailView, например LandmarkDetail(). и вызовите NavigationButton с пунктом назначения LandmarkDetail(). Теперь детальный вид был открыт.
Для передачи значений в подробные средства экрана. отправив код, как показано ниже.
struct LandmarkList: View {
var body: some View {
NavigationView {
List(landmarkData) { landmark in
NavigationButton(destination: LandmarkDetail()) {
LandmarkRow(landmark: landmark)
}
}
.navigationBarTitle(Text("Landmarks"))
}
}
}
Вот еще один способ представить представление БЕЗ использования NavigationView. Это как UIKit UIModalPresentationStyle.currentContext.
struct PresenterButtonView: View {
var body: some View {
PresentationButton(Text("Tap to present"),
destination: Text("Hello world"))
}}
Хм. Откуда PresentationButton?
На OP здесь отвечали несколько раз, но я просто хотел также продемонстрировать интересные аспекты SwiftUI, показав, что если у вас есть представление A с данными, которые также будет использовать представление B, вы можете передавать данные, создав @State в представлении A и объявление той же переменной с объявлением @Binding в представлении B
struct ViewA : View {
@State var myItems: [Items]
var body: some View {
NavigationView {
VStack {
NavigationButton(destination: ViewB(items: $myItems)) {
Text("Go To ViewB")
}
}
}
}
}
struct ViewB : View {
@Binding var myItems: [Items]
var body: some View {
NavigationView {
List{
ForEach(myItems.identified(by: \.self)) {
Text($0.itemName)
}
}.navigationBarTitle(Text("My Items"))
}
}
}
Вы больше не можете использовать NavigationButton. Вместо этого вы должны использовать NavigationLink.
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: DetailView()) {
Text("Push new screen")
}
}
}
}
Теперь мы можем использовать НавигацияСсылка
Файл А:
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
Text("Hello World")
NavigationLink(destination: secondView()) {
Text("Hit Me!")
.fontWeight(.semibold)
.font(.title)
.padding()
.foregroundColor(.white)
.background(LinearGradient(gradient: Gradient(colors: [Color(.white),Color(.blue)]), startPoint: .leading, endPoint: .trailing))
.cornerRadius(40)
}
}
}
}
}
Файл Б:
struct secondView: View {
var body: some View {
VStack {
VStack(alignment: .leading) {
Text("Turtle Rock")
.font(.title)
HStack(alignment: .top) {
Text("Joshua Tree National Park")
.font(.subheadline)
Spacer()
Text("California")
.font(.subheadline)
}
}
.padding()
Spacer()
}
}
}
Это не должно было быть здесь лучшим ответом, а скорее альтернативным методом, если вам не нужна панель навигации. См. ответ Джейка ниже, чтобы узнать, как это сделать с помощью NavigationView и NavigationLink. Надеюсь, это все еще полезно или укажет вам правильное направление.
Если вы просто пытаетесь заставить NavigationLink работать с Binding, вы можете использовать тот же NavigationLink init, который я сделал с параметром привязки isActive.
Anyway, back to the answer…
Я сделал модификатор представления для этого. Это также означает, что нет панели навигации. Вы можете назвать это так:
.navigate(to: MainPageView(), when: $willMoveToNextScreen)
Его можно прикрепить к чему угодно, поэтому я обычно прикрепляю его к концу тела, например:
@State private var willMoveToNextScreen = false
var body: some View {
VStack {
/* ... */
}
.navigate(to: MainPageView(), when: $willMoveToNextScreen)
}
Код (не забудьте import SwiftUI):
extension View {
/// Navigate to a new view.
/// - Parameters:
/// - view: View to navigate to.
/// - binding: Only navigates when this condition is `true`.
func navigate<NewView: View>(to view: NewView, when binding: Binding<Bool>) -> some View {
NavigationView {
ZStack {
self
.navigationBarTitle("")
.navigationBarHidden(true)
NavigationLink(
destination: view
.navigationBarTitle("")
.navigationBarHidden(true),
isActive: binding
) {
EmptyView()
}
}
}
.navigationViewStyle(.stack)
}
}
аккуратный подход, хотя он, казалось, нарушил мои ранее существовавшие представления с уже примененным стилем.
очень лаконично, аккуратно. Лучший
Хорошо сделано. Еще один момент, есть ли шанс изменить анимацию навигации? По умолчанию всегда было слева направо.
@sakiM Как бы вы его оживили? Это значение по умолчанию и стандарт, поэтому я просто оставил все как есть. Если вы хотите, чтобы это исходило снизу, я бы предложил вместо этого .sheet. Не путайте пользователей неожиданным поведением
К сожалению, в Xcode 12.2 это работает, но в середине исходного экрана появляется маленький серый прямоугольник. Так что это уже нежизнеспособно.
@Plasma Это кажется очень странным. Можете ли вы загрузить на imgur и ответить со ссылкой, чтобы я мог видеть?
@George_E вот, пожалуйста .. маленький серый круглый прямоугольник появляется сразу после добавления .navigate(to: MainPageView(), when: $willMoveToNextScreen) внизу моего представления, если я закомментирую эту строку, круглый прямоугольник исчезнет... .. imgur.com/r4lblhn
@Plasma Попробуйте использовать это в пустом проекте. Мне кажется, это вообще никак не связано со мной. Это может быть проблема с предварительным просмотром (попробуйте симулятор), но если нет, то, вероятно, что-то в вашем коде вы случайно пропустили. Для меня это почти похоже на .redacted(reason: .placeholder), но я не уверен
@George_E после тщательного тестирования кажется, что проблема возникает только в том случае, если целью является tvOS, отлично работает на IOS. Но при установке на tvOS серый прямоугольник виден в предварительном просмотре, симуляторе и на реальном устройстве.
Я получаю Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want с каждым .navigate()
Это потрясающе. спасибо, что поделились этим.
Привет, @George, у меня та же проблема, что и у Раухала Гаура при использовании iOS 15. С iOS 14 у меня нет никаких ошибок.
@RahulGaur, вы нашли способ исправить ошибки ограничений?
@TiagoMendes Добавление .navigationViewStyle(.stack) к NavigationView, вероятно, исправит это.
@ Джордж спасибо ? это решило проблему
Я думаю, что ответ Джейка - это основной путь к NextView.
И я думаю, что способ, описанный ниже, — это способ простой, формальный и динамичный попробовать, если вам действительно нужно нажать КНОПКУ. Согласно Видео Пола Хадсона, с 10:00 до 12:00.
(должен идти от 12:00 до 15:00, если вы хотите перейти к разные взгляды, нажимая разные кнопки.) (должен идти от 15:00 до 16:00, если вы хотите перейти к второму виду и вернуться назад автоматически.) И многое другое.
А вот и пример кода.
import SwiftUI
struct ContentView: View {
@State var areYouGoingToSecondView: Bool // Step 2
var body: some View {
NavigationView{ // Step 1
VStack {
// Step 3
NavigationLink(destination: YourSecondView(), isActive: $areYouGoingToSecondView) { EmptyView() }
Text("Hello World")
Button(action: {
self.areYouGoingToSecondView = true // Step 4
}) {
Text("Do Something (Go To Second View)")
.font(.largeTitle)
.fontWeight(.ultraLight)
}
}
}
}
}
если не хочет показать навигацияВид, вы можете скрыть его в пункте назначения.
struct ContentViewA : View {
var body: some View {
NavigationView {
VStack {
Text("Hello World")
NavigationLink(destination: ContentViewB()) {
Text("Go To Next Step")
}
}
}
}
}
struct ContentViewB : View {
var body: some View {
NavigationView {
VStack {
Text("Hello World B")
}.navigationBarTitle("")
.navigationBarHidden(true)
}
}
}
или если вы хотите скрыть его в зависимости от условий, вы можете использовать @State для изменения видимости.
Примечание. .navigationBarTitle("") скрывает текст, но неуклюже оставляет пробелы в пустом представлении. Таким образом, можно просто использовать модификатор .navigationBarHidden(true), а затем реализовать пользовательскую кнопку «Назад» или альтернативную навигацию.
Проблема с использованием навигационного представления и просто сокрытием вещей заключается в том, что 1) вы все еще находитесь в модели push/pop, и если вы никогда не собираетесь возвращаться назад (pop), то это представление сидит там, потребляя память и циклы (в зависимости от по своей структуре и сложности) и никогда не утилизируется. Может быть, это не проблема в большой схеме, но мне кажется, что это немного жирно.
Мы можем использовать текст внутри навигации и сделать его похожим на кнопку
http://g.recordit.co/CkIkjvikfu.gif
struct HomeContent: View {
var body: some View {
NavigationView{
VStack {
NavigationLink(
destination: LoginContentView()) {
Text("Login")
.font(.title)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 300.0, height: 50.0)
.background(Color(UIColor.appLightBlue))
}
}
}
}
}
Я думаю, вы упустили суть вопроса. 1) у входа не должно быть кнопки «Назад», 2) все, что осталось под формой входа, все еще там, не удалено, не утилизировано, и 3) что происходит, когда вы нажимаете вход? Конечно, вы захотите перейти на другой экран, возможно, на главный домашний экран приложения, который должен стать новым корнем, а не где-то в середине стека навигации.
Пожалуйста, прочитайте вопрос еще раз, вопрос заключался в том, чтобы открыть новый экран, а мой код открывает новый экран.
Я думаю, что это самый простой и понятный способ. Используйте полноэкранный обложка после инструмента пользовательского интерфейса.
Button(action: {
//code
}){
Text("Send")
}.fullScreenCover(isPresented: self.$model.goToOtherView, content: {
OtherView()
})
Проблема в том, что он не устанавливает цвет фона для fullScreenCover.
Интересно, но я также вижу, что это функция только для iOS 14.0 в XCode.
Чтобы программно перейти к ссылке без отображения визуального элемента, наложите на одно из представлений скрытую NavigationLink.
В этом примере пользовательский интерфейс будет перемещаться, когда какой-то код устанавливает для shouldNavigate значение true:
struct ProgramaticSample {
@State var shouldNavigate = false
var body: some View {
Text("Hello navigation")
.overlay(NavigationLink(
destination: DestinationScreen(),
isActive: $shouldNavigate) {}
.hidden())
}
}
Ознакомьтесь с навигационной библиотекой SwiftUI github.com/canopas/UIPilot для удобной навигации.