Reputation: 31
I've already asked here how to get rid of an out of range index error when using page styled TabView when I get data from API. There I was advised to do this:
Add an if rockets.isEmpty clause around the entire TabView and display something else if the array is empty (like a loading indicator)
// MARK: - API
class InfoApi {
func getRockets(completion: @escaping ([RocketInfo]) -> ()) {
guard let url = URL(string: "https://api.spacexdata.com/v4/rockets") else {
return
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
let rocketsInfo = try JSONDecoder().decode([RocketInfo].self, from: data!)
DispatchQueue.main.async {
completion(rocketsInfo)
}
} catch {
print(error.localizedDescription)
}
}
.resume()
}
}
// MARK: - ROCKET MODEL
struct RocketInfo: Codable, Identifiable {
let id = UUID()
let name: String
let country: String
}
// MARK: - CONTENT VIEW
struct ContentView: View {
@State var rockets: [RocketInfo] = []
var body: some View {
Group {
if rockets.isEmpty {
ProgressView()
} else {
NavigationView {
TabView {
ForEach(rockets) { rocket in
ScrollView(.vertical, showsIndicators: false) {
VStack(alignment: .center) {
Image(systemName: "globe")
.renderingMode(.original)
.resizable()
.scaledToFit()
.frame(height: 190, alignment: .center)
ZStack {
RoundedRectangle(cornerRadius: 32)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 865, maxHeight: .infinity, alignment: .center)
.foregroundColor(Color.gray)
VStack(spacing: 40) {
HStack {
Text("Name")
.font(.title)
Spacer()
Text(rocket.name)
.font(.title)
}
} //: VSTACK
.padding(.horizontal)
} //: ZSTACK
} //: VSTACK
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
} //: SCROLL
} //: LOOP
} //: TAB
.tabViewStyle(.page)
.edgesIgnoringSafeArea(.vertical)
} //: NAVIGATION
}
} //: GROUP
.onAppear {
InfoApi().getRockets { rockets in
self.rockets = rockets
}
}
.edgesIgnoringSafeArea(.vertical)
}
}
// MARK: - PREVIEW
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
It helped, but there was another problem. To wrap TabView in an if-else clause, I had to add some other View outside it (for example, NavigationView) and call the onAppear method for this View to make everything work. There was a problem with the fact that I cannot completely remove the white safe area at the top of NavigationView.
That's what it looks like in the Simulator
If I wrap TabView in HStack instead of NavigationView, then when scrolling ScrollView to the end, I see a white bar at the very bottom, which I also don't know how to get rid of.
This white space at the bottom of the ScrollView I was talking about
Upvotes: 0
Views: 100
Reputation: 31
Unfortunately, it turned out that it is quite impossible to get rid of navbar if you use both TabBar and NavigationView in one View.
Upvotes: 0
Reputation: 52387
There are a number of potential solutions to this. All involve getting rid of the NavigationView
since it seems that your only use for it was that you needed some sort of wrapper view.
Solution #1:
Move your isEmpty
check inside the TabView
var body: some View {
TabView {
if rockets.isEmpty {
//ProgressView
} else {
//your tabs
}
}.onAppear {
//API call
}
}
Solution #2:
Instead of NavigationView
, wrap your content in a Group
:
var body: some View {
Group {
if !rockets.isEmpty {
TabView { /*...*/ }
}
}.onAppear {
//API call
}
}
Solution #3: call onAppear
on the progress view when it appears
if rockets.isEmpty {
ProgressView()
.onAppear {
//API call
}
} else {
TabView {
//tab content
}
}
Edit: complete example with mocked objects/API
struct TabBarView: View {
@State var rockets: [RocketInfo] = []
var body: some View {
Group {
if rockets.isEmpty {
ProgressView()
} else {
NavigationView {
TabView {
ForEach(rockets) { rocket in
Text(rocket.title)
}
}
.tabViewStyle(.page)
}
}
}
.onAppear {
InfoApi().getRockets { rockets in
self.rockets = rockets
}
}
}
}
Upvotes: 1