Cách xóa không gian Thanh điều hướng mặc định trong SwiftUI NavigiationView


82

Tôi chưa quen với SwiftUI (giống như hầu hết mọi người) và đang cố gắng tìm cách xóa một số khoảng trắng phía trên Danh sách mà tôi đã nhúng trong NavigationView

Trong hình ảnh này, bạn có thể thấy rằng có một số khoảng trắng phía trên Danh sách

Phiên bản hiện tại

Điều tôi muốn đạt được là

Phiên bản lý tưởng

Tôi đã thử sử dụng

.navigationBarHidden(true)

nhưng điều này không tạo ra bất kỳ thay đổi đáng chú ý nào.

Tôi hiện đang thiết lập điều hướng của mìnhXem như thế này

 NavigationView {
                FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
                    .navigationBarHidden(true)
                }

trong đó FileBrowserView là dạng xem có Danh sách và Ô được định nghĩa như thế này

List {
   Section(header: Text("Root")){
    FileCell(name: "Test", fileType: "JPG",fileDesc: "Test number 1")

                    FileCell(name: "Test 2", fileType: "txt",fileDesc: "Test number 2")
                    FileCell(name: "test3", fileType: "fasta", fileDesc: "")
}
}

Tôi muốn lưu ý rằng mục tiêu cuối cùng ở đây là bạn có thể nhấp vào các ô này để điều hướng sâu hơn vào cây tệp và do đó sẽ hiển thị nút Quay lại trên thanh khi điều hướng sâu hơn, nhưng tôi không muốn bất kỳ điều gì ở hàng đầu như vậy trong lần xem ban đầu của tôi.

Câu trả lời:


132

Đối với một số lý do, SwiftUI đòi hỏi rằng bạn cũng thiết lập .navigationBarTitlecho .navigationBarHiddencông việc đúng cách.

NavigationView {
    FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
        .navigationBarTitle("")
        .navigationBarHidden(true)
}

Cập nhật

Như @Peacemoon đã chỉ ra trong các nhận xét, thanh điều hướng vẫn bị ẩn khi bạn điều hướng sâu hơn trong ngăn xếp điều hướng, bất kể bạn có đặt navigationBarHiddenthànhfalse hưởng quang cảnh tiếp theo. Như tôi đã nói trong phần bình luận, đây là kết quả của việc triển khai kém từ phía Apple hoặc chỉ là tài liệu tồi tệ (ai biết được, có thể có một cách "đúng" để thực hiện điều này).

Dù là gì đi nữa, tôi đã đưa ra một giải pháp có vẻ như tạo ra kết quả mong muốn của người đăng ban đầu. Tôi do dự khi đề xuất nó bởi vì nó có vẻ hacky không cần thiết, nhưng không có bất kỳ cách dễ dàng nào để ẩn và hiện thanh điều hướng, đây là cách tốt nhất tôi có thể làm.

Ví dụ này sử dụng ba dạng xem - View1có một thanh điều hướng ẩn View2View3cả hai đều có các thanh điều hướng hiển thị với tiêu đề.

struct View1: View {
    @State var isNavigationBarHidden: Bool = true

    var body: some View {
        NavigationView {
            ZStack {
                Color.red
                NavigationLink("View 2", destination: View2(isNavigationBarHidden: self.$isNavigationBarHidden))
            }
            .navigationBarTitle("Hidden Title")
            .navigationBarHidden(self.isNavigationBarHidden)
            .onAppear {
                self.isNavigationBarHidden = true
            }
        }
    }
}

struct View2: View {
    @Binding var isNavigationBarHidden: Bool

    var body: some View {
        ZStack {
            Color.green
            NavigationLink("View 3", destination: View3())
        }
        .navigationBarTitle("Visible Title 1")
        .onAppear {
            self.isNavigationBarHidden = false
        }
    }
}

struct View3: View {
    var body: some View {
        Color.blue
            .navigationBarTitle("Visible Title 2")
    }
}

Thiết navigationBarHiddenđể falsetrên quan điểm sâu sắc hơn trong việc điều khiển chồng dường như không ghi đè đúng sở thích của quan điểm cho rằng ban đầu thiết lập navigationBarHiddenđể true, do đó chỉ workaround tôi có thể đưa ra đã sử dụng một ràng buộc phải thay đổi sở thích của quan điểm ban đầu khi mới chế độ xem được đẩy lên ngăn xếp điều hướng.

Như tôi đã nói, đây là một giải pháp hacky, nhưng không có giải pháp chính thức từ Apple, đây là giải pháp tốt nhất mà tôi có thể nghĩ ra.


5
Điều này đã khắc phục sự cố của tôi! Đó là rất lạ mà bạn phải có một tiêu đề trước khi bạn có thể ẩn thanh điều hướng ...
Vapidant

5
Lỗi vẫn còn đó bên ngoài phiên bản beta: /
Daniel Ryan

1
@Peacemoon Tôi đã không nhận thấy điều đó trước đây. Nhìn chung, có vẻ như việc triển khai từ Apple ở đây khá cẩu thả. Bạn không cần phải đặt tiêu đề chỉ để ẩn thanh bắt đầu và đặt navigationBarHiddenthành falseở chế độ xem tiếp theo sẽ hiện thanh điều hướng, nhưng nó không. Cuối cùng, tôi phát chán với SwiftUI được ghi chép kém như thế nào và quay trở lại UIKit, và thực tế là ít nhất 20 người đã đến đây chỉ để tìm hiểu cách ẩn thanh điều hướng nói lên khá kém cho việc triển khai và / hoặc tài liệu của Apple. Xin lỗi, tôi không có câu trả lời tốt hơn cho bạn.
greycampbell

2
@SambitPrakash Tôi chưa bao giờ thực sự lồng một TabView vào trong NavigationView trước đây và Apple dường như không lồng chúng theo cách đó trong ứng dụng của họ theo như tôi có thể nói. Tôi chưa bao giờ hoàn toàn rõ ràng nếu việc lồng một TabView bên trong NavigationView được dự định thực hiện và tôi biết rằng SwiftUI đã có một số lỗi kỳ lạ xuất hiện khi bạn lồng chúng theo cách đó. TabViews luôn có cảm giác giống như một hình thức điều hướng cấp cao hơn so với NavigationViews đối với tôi. Thay vào đó, nếu bạn lồng NavigationView bên trong TabView, tôi tin rằng giải pháp của tôi vẫn sẽ hoạt động.
greycampbell

2
@kar Thật thất vọng khi câu trả lời này vẫn nhận được sự chú ý và ủng hộ. Tôi đã viết nó như một giải pháp tạm thời cho những gì đáng lẽ phải là một lỗi tạm thời. Tôi đã không thử nghiệm nó gần đây, nhưng rõ ràng là có rất nhiều vấn đề với nó. Một số người cũng đã hỏi liệu bạn có thể điều hướng giữa các chế độ xem mà không cần sử dụng Chế độ xem điều hướng hay không. Câu trả lời là có, nhưng về cơ bản bạn phải viết NavigationView của riêng mình từ đầu. Bạn không thể chỉ điều hướng một cách kỳ diệu giữa các chế độ xem. Một cái gì đó phải quản lý các chế độ xem đó và cung cấp các chuyển đổi giữa chúng, đó là lý do tại sao chúng tôi có NavigationView.
greycampbell

16

Mục đích của a NavigationViewlà thêm thanh điều hướng trên đầu chế độ xem của bạn. Trong iOS, có 2 loại thanh điều hướng: lớn và tiêu chuẩn.

nhập mô tả hình ảnh ở đây

Nếu bạn không muốn có thanh điều hướng:

FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))

Nếu bạn muốn một thanh điều hướng lớn (thường được sử dụng cho các chế độ xem cấp cao nhất của bạn):

NavigationView {
    FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
    .navigationBarTitle(Text("Title"))
}

Nếu bạn muốn có một thanh điều hướng tiêu chuẩn (nội tuyến) (thường được sử dụng cho các chế độ xem cấp phụ):

NavigationView {
    FileBrowserView(jsonFromCall: URLRetrieve(URLtoFetch: applicationDelegate.apiURL))
    .navigationBarTitle(Text("Title"), displayMode: .inline)
}

Hy vọng câu trả lời này sẽ giúp ích cho bạn.

Thông tin thêm: Tài liệu Apple


25
Có những lý do tại sao bạn có thể muốn ẩn thanh điều hướng trong khi vẫn duy trì chức năng của a NavigationView. Mục đích của a NavigationViewkhông chỉ để hiển thị thanh điều hướng.
greycampbell

6
Tôi muốn NavigiationView cho chức năng điều hướng trong ngăn xếp và có khả năng quay lại từ các chế độ xem một cách dễ dàng, tôi không cần có NavigiationBar ở chế độ xem ban đầu.
Vapidant

2
Có cách nào để điều hướng qua các chế độ xem mà không có Chế độ xem điều hướng không?
user1445685 11/11/19

Về cơ bản. Không. Ít nhất là chưa đến swiftui
Gustavo Parrado

Câu trả lời này không hữu ích vì có Chế độ xem điều hướng sẽ ảnh hưởng đến câu hỏi ban đầu vì nó cần thiết để điều hướng đến chế độ xem khác.
JaseTheAce

14

Xem Công cụ sửa đổi giúp dễ dàng:

//ViewModifiers.swift

struct HiddenNavigationBar: ViewModifier {
    func body(content: Content) -> some View {
        content
        .navigationBarTitle("", displayMode: .inline)
        .navigationBarHidden(true)
    }
}

extension View {
    func hiddenNavigationBarStyle() -> some View {
        modifier( HiddenNavigationBar() )
    }
}

Thí dụ: nhập mô tả hình ảnh ở đây

import SwiftUI

struct MyView: View {
    var body: some View {
        NavigationView {
            VStack {
                Spacer()
                HStack {  
                    Spacer()
                    Text("Hello World!")
                    Spacer()
                }
                Spacer()
            }
            .padding()
            .background(Color.green)
            //remove the default Navigation Bar space:
            .hiddenNavigationBarStyle()
        }
    }
}

4
Không khắc phục được sự cố đối với bộ điều khiển chế độ xem được đẩy.
damjandd

Có vẻ như mấu chốt ở đây là công cụ sửa đổi không được thêm vào NavigationView mà chỉ là chế độ xem bên trong. Điều này đã tạo ra tất cả sự khác biệt để nó hoạt động. Cảm ơn! :-)
JaseTheAce

9

Tôi cũng đã thử tất cả các giải pháp được đề cập trên trang này và chỉ thấy giải pháp @graycampbell là giải pháp hoạt động tốt, với các hoạt ảnh hoạt động tốt. Vì vậy, tôi đã cố gắng tạo ra một giá trị mà tôi có thể sử dụng trên toàn ứng dụng mà tôi có thể truy cập ở mọi nơi bằng ví dụ về hackingwithswift.com

Tôi đã tạo ra một ObservableObject lớp học

class NavBarPreferences: ObservableObject {
    @Published var navBarIsHidden = true
}

Và chuyển nó sang chế độ xem ban đầu trong SceneDelegate tương tự

var navBarPreferences = NavBarPreferences()
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(navBarPreferences))

Sau đó, trong ContentView chúng ta có thể theo dõi đối tượng có thể quan sát này như vậy và tạo liên kết đến SomeView:

struct ContentView: View {
    //This variable listens to the ObservableObject class
    @EnvironmentObject var navBarPrefs: NavBarPreferences

    var body: some View {
        NavigationView {
                NavigationLink (
                destination: SomeView()) {
                    VStack{
                        Text("Hello first screen")
                            .multilineTextAlignment(.center)
                            .accentColor(.black)
                    }
                }
                .navigationBarTitle(Text(""),displayMode: .inline)
                .navigationBarHidden(navBarPrefs.navBarIsHidden)
                .onAppear{
                    self.navBarPrefs.navBarIsHidden = true
            }
        }
    }
}

Và sau đó khi truy cập chế độ xem thứ hai (SomeView), chúng tôi ẩn nó một lần nữa như thế này:

struct SomeView: View {
    @EnvironmentObject var navBarPrefs: NavBarPreferences

    var body: some View {
        Text("Hello second screen")
        .onAppear {
            self.navBarPrefs.navBarIsHidden = false
        }
    } 
}

Để giữ cho bản xem trước hoạt động, hãy thêm NavBarPreferences vào bản xem trước như sau:

struct SomeView_Previews: PreviewProvider {
    static var previews: some View {
        SomeView().environmentObject(NavBarPreferences())
    }
}

2
sử dụng @EnosystemObject sẽ tốt hơn nhiều để chuyển dữ liệu trong toàn ứng dụng hơn là @State , vì vậy tôi muốn bạn trả lời nhiều hơn
Arafin Russell

6

Đây là một lỗi có trong SwiftUI ( vẫn còn như Xcode 11.2.1). Tôi đã viết một ViewModifierđể sửa lỗi này, dựa trên mã từ các câu trả lời hiện có:

public struct NavigationBarHider: ViewModifier {
    @State var isHidden: Bool = false

    public func body(content: Content) -> some View {
        content
            .navigationBarTitle("")
            .navigationBarHidden(isHidden)
            .onAppear { self.isHidden = true }
    }
}

extension View {
    public func hideNavigationBar() -> some View {
        modifier(NavigationBarHider())
    }
}

2
Với điều này, cử chỉ "nhanh chóng quay lại" không còn hoạt động nữa
Urkman

5

Bạn có thể mở rộng giao thức Chế độ xem gốc như thế này:

extension View {
    func hideNavigationBar() -> some View {
        self
            .navigationBarTitle("", displayMode: .inline)
            .navigationBarHidden(true)
    }
}

Sau đó, chỉ cần gọi ví dụ:

ZStack {
    *YOUR CONTENT*
}
.hideNavigationBar()

5

Nếu bạn đặt tiêu đề là nội tuyến cho Chế độ xem mà bạn muốn xóa không gian trên đó, thì việc này không cần phải được thực hiện trên chế độ xem có Chế độ xem điều hướng, nhưng chế độ này cũng được điều hướng.

.navigationBarTitle("", displayMode: .inline)

Sự cố bắt đầu giải pháp 1 sau đó chỉ cần thay đổi giao diện của Thanh điều hướng

init() {
    UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)
    UINavigationBar.appearance().shadowImage = UIImage()
}

trên dạng xem chứa NavigationView ban đầu. giải pháp cuối cùng

Nếu bạn muốn thay đổi Giao diện từ màn hình này sang màn hình khác, hãy thay đổi giao diện trong các dạng xem thích hợp


Giải pháp này rất hữu ích
Murilo Medeiros

4

Đối với tôi, tôi đã áp dụng .navigationBarTitlecho NavigationViewvà không phải Listlà thủ phạm. Điều này phù hợp với tôi trên Xcode 11.2.1:

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink(destination: DetailView()) {
                    Text("I'm a cell")
                }
            }.navigationBarTitle("Title", displayMode: .inline)
        }
    }
}

Thanh điều hướng và danh sách không có khoảng trống ở trên cùng


5
Không liên quan đến câu hỏi đã hỏi
Ahmed Sahib

3
@AhmedSahib Câu hỏi là "Làm thế nào để loại bỏ không gian Thanh điều hướng mặc định trong SwiftUI NavigiationView" và mã của tôi thực hiện được điều đó.
Genki

1
Lời khuyên tuyệt vời. Đối với giải pháp của tôi, tôi phải áp dụng hai công cụ sửa đổi cho danh sách bên trong để loại bỏ khoảng cách: .navigationBarTitle ("", displayMode: .automatic) .navigationBarHidden (true) Sau đó, trên NavigationView bên ngoài, tôi phải áp dụng: .navigationBarTitle (" TITLE ", displayMode: .inline)
Frankenstein ngày

2

Tương tự như câu trả lời của @graycampbell nhưng đơn giản hơn một chút:

struct YourView: View {

    @State private var isNavigationBarHidden = true

    var body: some View {
        NavigationView {
            VStack {
                Text("This is the master view")
                NavigationLink("Details", destination: Text("These are the details"))
            }
                .navigationBarHidden(isNavigationBarHidden)
                .navigationBarTitle("Master")
                .onAppear {
                    self.isNavigationBarHidden = true
                }
                .onDisappear {
                    self.isNavigationBarHidden = false
                }
        }
    }
}

Đặt tiêu đề là cần thiết vì nó được hiển thị bên cạnh nút quay lại trong các dạng xem bạn điều hướng đến.


2

Đối với tôi, đó là bởi vì tôi đã đẩy NavigationView của mình từ một cái hiện có. Trên thực tế, có một cái bên trong cái kia. Nếu bạn đến từ NavigationView, bạn không cần phải tạo một cái bên trong cái tiếp theo vì bạn đã ở trong NavigatonView.


0

Thực sự yêu thích ý tưởng do @Vatsal Manot đưa ra Để tạo công cụ sửa đổi cho điều này.
Xóa thuộc isHiddentính khỏi câu trả lời của anh ấy, vì tôi không thấy nó hữu ích vì bản thân tên công cụ sửa đổi gợi ý rằng ẩn thanh điều hướng.

// Hide navigation bar.
public struct NavigationBarHider: ViewModifier {

    public func body(content: Content) -> some View {
        content
            .navigationBarTitle("")
            .navigationBarHidden(true)
    }
}

extension View {
    public func hideNavigationBar() -> some View {
        modifier(NavigationBarHider())
    }
}

0

Tôi đã gặp sự cố tương tự khi làm việc trên một ứng dụng mà TabView sẽ được hiển thị sau khi người dùng đăng nhập.

Như @graycampbell đã đề xuất trong nhận xét của anh ấy, TabView không nên được nhúng vào NavigationView, nếu không "khoảng trống" sẽ xuất hiện, ngay cả khi sử dụng .navigationBarHidden(true)

Tôi đã sử dụng một ZStackđể ẩn NavigationView. Lưu ý rằng đối với ví dụ đơn giản này, tôi sử dụng @State@Bindingquản lý khả năng hiển thị giao diện người dùng, nhưng bạn có thể muốn sử dụng thứ gì đó phức tạp hơn, chẳng hạn như đối tượng môi trường.

struct ContentView: View {

    @State var isHidden = false

    var body: some View {
        
        ZStack {
            if isHidden {
                DetailView(isHidden: self.$isHidden)
            } else {
                NavigationView {
                    Button("Log in"){
                        self.isHidden.toggle()
                    }
                    .navigationBarTitle("Login Page")
                }
            }
        }
    }
}

Khi chúng ta nhấn nút Đăng nhập, trang đầu tiên sẽ biến mất và Chế độ xem chi tiết được tải. Trang Đăng nhập xuất hiện lại khi chúng tôi bật nút Đăng xuất

struct DetailView: View {
    
    @Binding var isHidden: Bool
    
    var body: some View {
        TabView{
            NavigationView {
                Button("Log out"){
                    self.isHidden.toggle()
                }
                .navigationBarTitle("Home")
            }
            .tabItem {
                Image(systemName: "star")
                Text("One")
            }
        }
    }
}

0

Giải pháp của tôi cho vấn đề này giống với @Genki và @Frankenstein đề xuất.

Tôi đã áp dụng hai công cụ sửa đổi cho danh sách bên trong (KHÔNG phải là NavigationView) để loại bỏ khoảng cách:

.navigationBarTitle("", displayMode: .automatic)
.navigationBarHidden(true) 

Trên NavigationView bên ngoài, sau đó được áp dụng .navigationBarTitle("TITLE")để đặt tiêu đề.


1
Nó không làm gì cả.
TruMan1

0

SwiftUI 2

Có một công cụ sửa đổi chuyên dụng để làm cho thanh điều hướng chiếm ít không gian hơn:

.navigationBarTitleDisplayMode(.inline)

Không còn cần phải ẩn thanh điều hướng hoặc đặt tiêu đề của nó.


-7

Thử đặt NavigationViewbên trong a GeometryReader.

GeometryReader {
    NavigationView {
        Text("Hello World!")
    }
}

Tôi đã trải qua hành vi kỳ lạ khi NavigationViewchế độ xem gốc.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.