最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

pull to refresh - SwiftUI .refreshable default progressView not shown if parentView has .navigationBarTitleDisplayMode(.large) -

programmeradmin3浏览0评论

I have a strange problem. I have a view with a list and I add the .refreshable to it to load data. The data was loaded, but the default progressView of the pull-to-refresh was not appearing.

I load the same app on a different device (bigger screen) and it was showing there... The first thing I notice was the NavigationView title was showing as inline (it was configured with .navigationBarTitleDisplayMode(.automatic). So I start playing with those values.

First let's add some background information. Basically I have 3 views: Login (LoginView) -> Selection Menu (SelectView) -> Job Listing (JobListingView)

After successful login, I show the Selection Menu, and on that menu (it's a kind of grid with buttons) I have an option to go to the Job Listing. In that listing is where I have the .refreshable. I'm developping for iOS15.

Here are the relevant part of my views:

LoginView

Here I define the NavigationView so I can navigate through view but I do not want the navBar to be shown that's why it's hidden

public var body: some View {
    NavigationView{
        ZStack{
            LoginView(viewModel: viewModel)
                .background(Color(.white))
                .navigationBarHidden(true)     // Hides the navigation bar
            
            NavigationLink(destination: SelectView(), isActive: $viewModel.loggedIn){
                EmptyView()
            }
        }
        
    }
}

SelectView

See the comment above: .navigationBarTitleDisplayMode(.inline)

var body: some View {
    VStack{
        HStack{
            Spacer()
            
            NavigationLink(destination: JobListingView()){
                SelectItemView(name: "Jobs", icon: "icAssignment")
            }
            
            Spacer()
            SelectItemView(name: "Bitácora", icon: "icBinnacle")
            Spacer()
        }
        .padding(.bottom, 20)
        HStack{
            Spacer()
            SelectItemView(name: "Inventario", icon: "icDolly")
            Spacer()
            SelectItemView(name: "3D", icon: "icBinnacle")
            Spacer()
        }
        
        Spacer()
    }
    .navigationTitle("MY APP")
    // IF I USE .large HERE, THEN IN THE NEXT VIEW, THE DEFAULT ProgressView OF THE .refreshable IS NOT SHOWN
    .navigationBarTitleDisplayMode(.inline)
    .navigationBarBackButtonHidden(true) // hides the "back" or previous view title button
}

JobListingView

var body: some View {
    List(viewModel.jobListDTO, id: \.id) { job in
        JobItemView(item: job)
            .listRowSeparator(.hidden)
    }
    .refreshable {
        await viewModel.requestJobListing()
    }
    .onAppear {
        Task{
            await viewModel.requestJobListing()
        }
    }
    .navigationTitle("job-list-title")
    // NO MATTER WAHT I PUT HERE .large OR .inline. IF THE PARENT IS .large THEN THE ProgressView OF THE .refreshable IS NOT SHOWN
    .navigationBarTitleDisplayMode(.large)
    .listStyle(PlainListStyle())
    .frame(maxWidth: .infinity)
}

I have a strange problem. I have a view with a list and I add the .refreshable to it to load data. The data was loaded, but the default progressView of the pull-to-refresh was not appearing.

I load the same app on a different device (bigger screen) and it was showing there... The first thing I notice was the NavigationView title was showing as inline (it was configured with .navigationBarTitleDisplayMode(.automatic). So I start playing with those values.

First let's add some background information. Basically I have 3 views: Login (LoginView) -> Selection Menu (SelectView) -> Job Listing (JobListingView)

After successful login, I show the Selection Menu, and on that menu (it's a kind of grid with buttons) I have an option to go to the Job Listing. In that listing is where I have the .refreshable. I'm developping for iOS15.

Here are the relevant part of my views:

LoginView

Here I define the NavigationView so I can navigate through view but I do not want the navBar to be shown that's why it's hidden

public var body: some View {
    NavigationView{
        ZStack{
            LoginView(viewModel: viewModel)
                .background(Color(.white))
                .navigationBarHidden(true)     // Hides the navigation bar
            
            NavigationLink(destination: SelectView(), isActive: $viewModel.loggedIn){
                EmptyView()
            }
        }
        
    }
}

SelectView

See the comment above: .navigationBarTitleDisplayMode(.inline)

var body: some View {
    VStack{
        HStack{
            Spacer()
            
            NavigationLink(destination: JobListingView()){
                SelectItemView(name: "Jobs", icon: "icAssignment")
            }
            
            Spacer()
            SelectItemView(name: "Bitácora", icon: "icBinnacle")
            Spacer()
        }
        .padding(.bottom, 20)
        HStack{
            Spacer()
            SelectItemView(name: "Inventario", icon: "icDolly")
            Spacer()
            SelectItemView(name: "3D", icon: "icBinnacle")
            Spacer()
        }
        
        Spacer()
    }
    .navigationTitle("MY APP")
    // IF I USE .large HERE, THEN IN THE NEXT VIEW, THE DEFAULT ProgressView OF THE .refreshable IS NOT SHOWN
    .navigationBarTitleDisplayMode(.inline)
    .navigationBarBackButtonHidden(true) // hides the "back" or previous view title button
}

JobListingView

var body: some View {
    List(viewModel.jobListDTO, id: \.id) { job in
        JobItemView(item: job)
            .listRowSeparator(.hidden)
    }
    .refreshable {
        await viewModel.requestJobListing()
    }
    .onAppear {
        Task{
            await viewModel.requestJobListing()
        }
    }
    .navigationTitle("job-list-title")
    // NO MATTER WAHT I PUT HERE .large OR .inline. IF THE PARENT IS .large THEN THE ProgressView OF THE .refreshable IS NOT SHOWN
    .navigationBarTitleDisplayMode(.large)
    .listStyle(PlainListStyle())
    .frame(maxWidth: .infinity)
}
Share Improve this question asked Nov 18, 2024 at 14:09 sebasirasebasira 1,8441 gold badge27 silver badges44 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

Workaround:

Specify navigationViewStyle as stack. Now refresh view will be visible.

NavigationView {
...
}
.navigationViewStyle(.stack)

Since the issue occurs only in smaller devices like iPhone, you can use the below for different navigation style based on device

NavigationView {
...
}
.deviceSpecificNavigationViewStyle()

extension View {
    func deviceSpecificNavigationViewStyle() -> some View {
        Group {
            if UIDevice.current.userInterfaceIdiom == .phone {
                navigationViewStyle(.stack)
            } else {
                navigationViewStyle(.automatic)
            }
        }
    }
}

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论