swiftswiftuiios16swiftui-zstack

How to start the overlay after last Text from the view in SwiftUI?


I have a ScrollView with some text in it and I want to use .overlay for an Image to start after last Text or element from the view.

In a simple way I used Color to showcase what I have and what I want to achieve.

Using offset would shift the view down with a fixed amount. I assume that with GeometryReader that offset could be dinamically?

But that would cover the tabBar as shown in this example.

import SwiftUI

struct ContentView: View {
    var body: some View {
        TabView {
        
            ScrollView {
                VStack {
                    Text("Text 1")
                        .font(.title)
                    Text("Text 2")
                }
                VStack {
                    Text("Text 3")
                    Text("Text4")
                }
                .padding()
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .overlay(
                ZStack {
                    Color.red
                        .offset(y: 100)
                        .opacity(0.5)
                }
            )
            .tabItem {
                Image(systemName: "house.circle")
                Text("Home")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

I tried with alignment on overlay, ZStack or on ScrollView but no success. Some of the alignment modifiers I tried are trailingLastTextBaseline or leadingLastTextBaseline.

Is there a way for the overlay to start right after Text 4 and not cover the tabBar?


Solution

  • It sounds like you want the ScrollView to use the minimum height necessary and then the remaining space should be filled with the image. If this is correct, then the solution from How to keep the ScrollView height to the minimum necessary in SwiftUI? can be used. This means moving the ScrollView to the overlay, the image just follows in the regular layout.

    If there is a lot of text then the image might need to be allowed to consume a minimum height, this is done by applying .frame(minHeight).

    Hope this works as you were wanting:

    struct ContentView: View {
    
        private var textContent: some View {
            VStack {
                Text("Text 1")
                    .font(.title)
                Text("Text 2")
    //            Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
    //                .font(.title)
                Text("Text 3")
                    .padding(.top, 50)
                Text("Text4")
            }
            .padding()
        }
    
        var body: some View {
            TabView {
                VStack(spacing: 0) {
                    textContent
                        .layoutPriority(1)
                        .hidden()
                        .overlay(alignment: .top) {
                            ScrollView {
                                textContent
                            }
                        }
                    Color.red
                        .frame(minHeight: 100)
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .tabItem {
                    Image(systemName: "house.circle")
                    Text("Home")
                }
            }
        }
    }
    

    RemainingSpace