Okay, so I'm having a problem with the TabView height. This is a basic version of my code.
struct MainView: View {
var body: some View {
ScrollView {
VStack {
ProfileHStack { ... }
BioVStack { ... }
ButtonHStack { ... }
IconHStack { ... }
TabView {
GridLayout()
//Other screens...
}
.tabViewStyle(PageTabViewStyle())
}
}
}
}
My grid layout view looks like this.
struct GridLayout: View {
let columns = [GridItem(.flexible(), spacing: 1), GridItem(.flexible(), spacing: 1), GridItem(.flexible(), spacing: 1)]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 1) {
ForEach(0..<30) { n in
Image("placeholder-image")
.resizable()
.scaledToFill()
.frame(maxWidth: UIScreen.main.bounds.width/3, minHeight: UIScreen.main.bounds.width/3)
.clipped()
}
}
}
}
}
Here a screen shot what my full code looks like.

As you can see the tabview height is getting messed up. It only works with a fixed height. Im trying to make the tabview height as big as the content in my grid layout. How would I be able to achieve this.
This is how my full code looks to achieve the screenshot, so you can copy and paste it in Xcode and see if someone can get it to work.
struct ProfileScreen: View {
@State var selectedButton: ProfileButton = .post
@State var tabHeight: CGFloat = 0
@State var didTapProfileTab = false
let width = UIScreen.main.bounds.width / 4
let columns = [GridItem(.flexible(), spacing: 1), GridItem(.flexible(), spacing: 1), GridItem(.flexible(), spacing: 1)]
var body: some View {
NavigationView {
ScrollView {
//MARK: User Image & Followers
HStack(spacing: 16) {
UserProfileImageView(imageName: "gal-gadot", width: UIScreen.main.bounds.width/4, lineWidth: 8)
Spacer()
UserProfileInfo(numberOfPosts: "1,570", numberOfFollows: "67.1M", numberOfFollowing: "1,047")
}
.padding(.horizontal)
//MARK: User Name, Profession & Bio
VStack(alignment: .leading) {
Spacer()
.frame(maxWidth: .infinity)
Text("Gal Gadot")
.font(.headline)
.fontWeight(.semibold)
Text("Actress")
.font(.subheadline)
.foregroundColor(.secondary)
Text("🌈 meet GOODLES - Mac & Cheese that is just gooder!")
.lineLimit(4)
}
.padding(.horizontal)
//MARK: Follow & Message Button
HStack {
Button {
} label: {
Text("Follow")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity, minHeight: 44)
.background(K.AssetColor.purple)
.tint(.white)
.cornerRadius(8)
Button {
} label: {
Text("Message")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity, minHeight: 44)
.foregroundColor(.primary)
.cornerRadius(8)
.overlay(RoundedRectangle(cornerSize: CGSize(width: 8, height: 8)).stroke(.secondary))
}
.padding(.horizontal)
.padding(.bottom)
//MARK: Buttons
HStack(spacing: 0) {
ImageButton(systemName: "squareshape.split.3x3", assignedButton: .post, selectedButton: $selectedButton) {
selectedButton = .post
}
ImageButton(systemName: "film", assignedButton: .reels, selectedButton: $selectedButton) {
selectedButton = .reels
}
ImageButton(systemName: "video", assignedButton: .video, selectedButton: $selectedButton) {
selectedButton = .video
}
ImageButton(systemName: "person.2", assignedButton: .tag, selectedButton: $selectedButton) {
selectedButton = .tag
}
}
.padding(.bottom, -6)
TabView {
GridLayout()
}
.tabViewStyle(PageTabViewStyle())
}
.navigationTitle("gal_gadot")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Image(systemName: "chevron.left")
.font(.system(size: 24))
}
ToolbarItem(placement: .navigationBarTrailing) {
Image(systemName: "ellipsis")
.padding(.trailing, 8)
}
}
}
}
}
The ProfileButton looks like this.
enum ProfileButton {
case post,reels,video,tag
}
UserProfileImage
struct UserProfileImageView: View {
let imageName: String
let width: CGFloat
let lineWidth: CGFloat
var body: some View {
ZStack {
LinearGradient(colors: [K.AssetColor.purple,K.AssetColor.yellow], startPoint: .bottomLeading, endPoint: .topTrailing)
Circle()
.foregroundColor(.white)
.frame(width: width + lineWidth, height: width + lineWidth)
Image(imageName)
.resizable()
.scaledToFill()
.clipShape(Circle())
.frame(width: width, height: width)
}
.frame(width: width + lineWidth * 2, height: width + lineWidth * 2)
.clipShape(Circle())
}
}
UserProfileInfo
struct UserProfileInfo: View {
let numberOfPosts: String
let numberOfFollows: String
let numberOfFollowing: String
var body: some View {
HStack(spacing: 0) {
Group {
VStack(alignment: .center) {
Text(numberOfPosts)
.fontWeight(.semibold)
.font(.headline)
Text("Posts")
.font(.subheadline)
.foregroundColor(.secondary)
}
Spacer()
VStack(alignment: .center) {
Text(numberOfFollows)
.fontWeight(.semibold)
.font(.headline)
Text("Followers")
.font(.subheadline)
.foregroundColor(.secondary)
}
Spacer()
VStack(alignment: .center) {
Text(numberOfFollowing)
.fontWeight(.semibold)
.font(.headline)
Text("Following")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
}
ImageButton
struct ImageButton: View {
let systemName: String
let assignedButton: ProfileButton
@Binding var selectedButton: ProfileButton
let action: () -> Void
var body: some View {
VStack {
Button(action: action) {
Image(systemName: systemName)
.font(.title2)
.foregroundColor(selectedButton == assignedButton ? K.AssetColor.purple : K.AssetColor.purple.opacity(0.5))
}
Spacer()
Rectangle()
.frame(minWidth: UIScreen.main.bounds.width / 4, maxHeight: 1)
.foregroundColor(selectedButton == assignedButton ? K.AssetColor.purple : K.AssetColor.purple.opacity(0.5))
}
}
}
So now the problem is when I have another screen with a different height.
TabView {
//Height 1000
Screen1(height: $height)
//Height 300
Screen2(height: $height)
}
.tabViewStyle(PageTabViewStyle())
.frame(height: height)
So I included this on both screens.
@Binding var height: CGFloat
.background(
GeometryReader { geo in
Color.clear
.preference(
key: HeightPreferenceKey.self,
value: geo.size.height
)
}
.onPreferenceChange(HeightPreferenceKey.self) { height in
self.height = height
}
)
The onPreferenceChange only gets called once so when I switch from screen 2 to screen 1, screen 1 height is now the height of screen 2. Now I had to add this to both screens for it to work.
@State var screenHeight: CGFloat
Instead of setting the self.height I set the screenHeight in onPreferenceChange. Then I set the self.height to screenHeight in onAppear.
Now the only thing now is that when I just drag a little to screen2 the height of screen1 one changes to screen2. Here is a visual example..
Firstly, you should remove the inner ScrollView. It's not a good idea to have a ScrollView in a ScrollView, because it doesn't make sense to be scrolling in something already scrollable.
I solved this by reading the actual height of the LazyVStack with GeometryReader, set that value to a @Binding so it is propagated up, then set the height of the TabView to this value.
Code:
struct ProfileScreen: View {
/* ... */
@State private var height: CGFloat = 0
var body: some View {
NavigationView {
ScrollView {
//MARK: User Image & Followers
/* ... */
//MARK: User Name, Profession & Bio
/* ... */
//MARK: Follow & Message Button
/* ... */
//MARK: Buttons
/* ... */
TabView {
GridLayout(height: $height)
}
.tabViewStyle(PageTabViewStyle())
.frame(height: height)
}
/* ... */
}
}
}
struct GridLayout: View {
let columns = [GridItem(.flexible(), spacing: 1), GridItem(.flexible(), spacing: 1), GridItem(.flexible(), spacing: 1)]
@Binding var height: CGFloat
var body: some View {
LazyVGrid(columns: columns, spacing: 1) {
/* ... */
}
.background(
GeometryReader { geo in
Color.clear
.preference(
key: HeightPreferenceKey.self,
value: geo.size.height
)
}
.onPreferenceChange(HeightPreferenceKey.self) { height in
self.height = height
}
)
}
}
struct HeightPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With