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
1 Answer
Reset to default 1Workaround:
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)
}
}
}
}