4

Is there a way to build UI elements in SwiftUI inside scroll view with bottom Alignment?

My use case: I have a screen where the screen has

  1. Spacer (What ever is left after allocating below elements)
  2. LogoView
  3. Spacer() - 30
  4. Some Text - 4/5 lines
  5. Spacer() - 50 (this will be calculated off of GR size Height)
  6. HStack with Two Button - This should be pinned to bottom of view / ScrollView

I would like to know how Can I pin the HStack view to ScrollView Bottom

I've replicated my setup and reproduced my problems in a Swift playground like so

struct ContentView: View {
        var body: some View {
        GeometryReader { gr in
            ScrollView {
                VStack {
                    Spacer()
                    Image(systemName: "applelogo")
                        .resizable()
                        .frame(width: gr.size.width * 0.5, height: gr.size.height * 0.3, alignment: .center)
                    Spacer().padding(.bottom, gr.size.height * 0.01)
                    Text("SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME")
                        .fontWeight(.regular)
                        .foregroundColor(.green)
                        .multilineTextAlignment(.center)
                        .padding(.top, gr.size.height * 0.05)
                        .padding(.horizontal, 40)
                        .layoutPriority(1)
                    Spacer()
                        .frame(minHeight: gr.size.height * 0.12)
                    HStack {
                        Button(action: {

                        }, label: {
                            Text("Button 1")
                        })
                        .foregroundColor(Color.white)
                        .padding(.vertical)
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .background(Color.blue)
                        .cornerRadius(8)

                        Button(action: {

                        }, label: {
                            Text("Button 2")
                        })
                        .foregroundColor(Color.white)
                        .padding(.vertical)
                        .frame(minWidth: 0, maxWidth: .infinity)
                        .background(Color.blue)
                        .cornerRadius(8)
                    }
                    .padding(.horizontal, 20)
                }
                //.frame(maxWidth: .infinity, maxHeight: .infinity)
            }
        }
    }
}

enter image description here

I understand that when scrollView is introduced in SwiftUI view Spacer length is changed to Zero, Would like to know what's the best way to achieve this

Pablo Dev
  • 317
  • 5
  • 16
  • Can you upload an image or a sketch of your intent UI? – Harshil Patel Dec 20 '20 at 21:29
  • @HarshilPatel Please find the updated description above (hope it helps). I want the buttons to pin from bottom of view (aka Scroll View) – Pablo Dev Dec 21 '20 at 00:30
  • Does this answer your question https://stackoverflow.com/a/58708206/12299030? – Asperi Dec 21 '20 at 04:59
  • @Asperi, No. I am not looking to customize scrollView behavior, was looking for approach of how to pin views to scrollView bottom (similar to storyboard bottom constraint). – Pablo Dev Dec 21 '20 at 05:54
  • Approach is to have a spacer above your button. The height of this spacer needs to be computed based on scroll view height - (content of scroll view). Then this needs to be passed on up the view hierarchy using `PreferenceKey` and then use it to set the spacer height – user1046037 Dec 21 '20 at 07:10
  • I would like to add 25% of empty space between button and text, rest 75% of empty space between view top to apple logo. In other words, building a view from the bottom pinning my content view bottom to scrollView bottom – Pablo Dev Dec 21 '20 at 20:11

2 Answers2

10
struct SampleView: View {
    var body: some View {
        GeometryReader { gr in
            VStack {
                ScrollView {
                    VStack {

                        // Fills whatever space is left
                        Rectangle()
                            .foregroundColor(.clear)

                        Image(systemName: "applelogo")
                            .resizable()
                            .frame(width: gr.size.width * 0.5, height: gr.size.height * 0.3, alignment: .center)
                            .padding(.bottom, gr.size.height * 0.06)


                        Text("SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME TEXT SOME")
                            .fontWeight(.regular)
                            .foregroundColor(.green)
                            .multilineTextAlignment(.center)
                            .padding(.horizontal, 40)
                            .layoutPriority(1)


                        // Fills 12 %
                        Rectangle()
                            .frame(height: gr.size.height * 0.12)
                            .foregroundColor(.clear)

                        HStack {

                            Button(action: {
                            }, label: {
                                Text("Button 1")
                            })
                            .foregroundColor(Color.white)
                            .padding(.vertical)
                            .frame(minWidth: 0, maxWidth: .infinity)
                            .background(Color.blue)
                            .cornerRadius(8)

                            Button(action: {
                            }, label: {
                                Text("Button 2")
                            })
                            .foregroundColor(Color.white)
                            .padding(.vertical)
                            .frame(minWidth: 0, maxWidth: .infinity)
                            .background(Color.blue)
                            .cornerRadius(8)
                        }
                        .padding(.horizontal, 20)
                        .padding(.bottom, 20)

                    }

                    // Makes the content stretch to fill the whole scroll view, but won't be limited (it can grow beyond if needed)
                    .frame(minHeight: gr.size.height)
                }
            }
        }
    }
}
Pablo Dev
  • 317
  • 5
  • 16
1

I wanted the scrollview to appear bottom to top instead of top to bottom. This had my answer: https://www.thirdrocktechkno.com/blog/implementing-reversed-scrolling-behaviour-in-swiftui/

TLDR: .rotationEffect(Angle(degrees: 180)).scaleEffect(x: -1.0, y: 1.0, anchor: .center) on the scrollview AND on the items in the scrollview

jonmecer
  • 611
  • 6
  • 15