2

My View simply consists of an Image object at the top and a button beneath it, both contained within a vertical ScrollView. I need to somehow gradually keep resizing (making smaller) the image as the user scrolls down, and return it to its original size as the user scrolls back up. I wasnt able to find any way to get notified and therefore be able to run code when the user scrolls, so Im unsure how to approach this or if it is even possible at all with current SwiftUI. Something tells me a GeometryReader might be needed here, but I honestly wouldnt know where to put it in the first place & it seems, according to my research, that GeometryReaders should be avoided whenever possible, although I would definitely make use of them if they can help here.

Code couldnt be simpler as of now:

struct ContentView: View {
var body: some View {
    ScrollView {
        Image("Spaghetti")
            .resizable()
            .frame(width: 200, height: 300)
        Button {
            // do something
        } label: {
            Text("Send")
                .foregroundColor(.white)
                .padding()
                .background(Color.blue)
                .cornerRadius(20)
        }
        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.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.")
            .padding()
            .multilineTextAlignment(.center)
    }
}

}

Deployment target is iOS 15 so OS version isnt an issue at all.

Thanks!

stompy
  • 227
  • 1
  • 13

1 Answers1

0

I've done something similar by using the solution described HERE. Assuming you use this solution it should be relatively easy to plug it in for your purposes.

The ScrollViewOffet view will notify you when the scroll offset changes. You can use that offset to determine how to resize the view.

You will want to use state variables to store your image size so you can make adjustments as the scroll offset changes.

struct ContentView: View {

    enum Constants {
        static let imageWidth = 200.0
        static let imageHeight = 200.0
    }

    @State var imageWidth: CGFloat = Constants.imageWidth
    @State var imageHeight: CGFloat = Constants.imageHeight
    @State var showLongText: Bool = false

    var body: some View {
        ScrollViewOffset {
            imagePlaceholder
                .overlay(alignment: .bottom) {
                    image
                }
            sendButton
            text
        } onOffsetChange: { offset in
            resizeImage(offset: offset)
        }
        .background(.orange)
    }
}

The idea here is to scale the original image size by the "offset to original image height" ratio. By that, I mean, if the scroll offset is equal to the original image height, then the scaled image size should be zero.

extension ContentView {
    private func resizeImage(offset: CGFloat) {
        let scale = 1.0 - (min(Constants.imageHeight, -offset) / Constants.imageHeight)
        imageWidth = Constants.imageWidth * scale
        imageHeight = Constants.imageHeight * scale
    }
}

I like to separate out my views so you can see the main body cleanly.

Now the imagePlaceholder is just a placeholder with the same size as the original image. You don't want to put the image directly in the scroll view because scrolling gets wonky if the image size is changing as the scroll offset is changing.

The actual image is going to added as an overlay aligned bottom of the placeholder. That way, when the user scrolls, and we resize the image, the scroll view content size will remain the same.

extension ContentView {
    private var imagePlaceholder: some View {
        Color.clear
            .frame(width: Constants.imageWidth, height: Constants.imageHeight)
    }
    private var sendButton: some View {
        Button {
            showLongText.toggle()
        } label: {
            Text("Send")
                .foregroundColor(.white)
                .padding()
                .background(.blue)
                .cornerRadius(20)
        }
    }
    private var text: some View {
        Text(showLongText ? .longText() : .shortText())
            .padding()
            .multilineTextAlignment(.center)

    }
    private var image: some View {
        Image("Spaghetti")
            .resizable()
            .frame(width: imageWidth, height: imageHeight)
    }
}

This is just for convenience and organizational purposes.

private extension LocalizedStringKey {

    static let textBase: String = "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.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."

    static func shortText() -> LocalizedStringKey {
        .init(textBase)
    }

    static func longText() -> LocalizedStringKey {
        .init(textBase + "               " + textBase)
    }
}
Rob C
  • 4,877
  • 1
  • 11
  • 24