Home > Back-end >  How to horizontally and vertically center an image inside a scrollview in SwiftUI
How to horizontally and vertically center an image inside a scrollview in SwiftUI

Time:05-11

Imagine a simple scrollview that contains a content view with an image in it which should be centered vertically if height is smaller than the scrollview's height.

If the image exceeds the height of the scrollview the scrollview should allow scrolling to display the complete image.

Additionally if the image is narrower than its container it should be centered horizontally.

When being scaled down the image should always retain its intrinsic aspectRatio.

These are the requirements

How would you do that in SwiftUI?

What are the problems that need to be tackled here?

  • content views of scrollviews are not automatically centered, they are aligned top-leading
  • resizable images always take all the space they get, even if intrinsically smaller than its frames (thus they scale up, which is nasty)
  • the aspect ratio of the image needs to be as it was originally
  • if the image gets taller than the scrollview itself the content needs to be scrollable to reveal the whole image

What should not be part of the solution:

  • pinch to scale

Similar issues on Stackoverflow (not really solving the issue):

CodePudding user response:

One suggested solution would be the following:

struct TestView: View {

    let image: UIImage

    var body: some View {
        GeometryReader { geo in
            ScrollView {
                SwiftUI.Group {
                    Image(uiImage: self.image)
                        // make image resizable to expand to frame's bounds
                        .resizable()
                        // make sure the aspect ratio when scaling is
                        // like the original image ratio
                        .aspectRatio(contentMode: .fit)
                        // limit the image's width to its intrinsic width
                        // this gets rid of **upscaling**
                        .frame(maxWidth: image.size.width)

                }
                // makes sure the container is always at least as tall as the scrollview
                // so that the **vertical alignment** works properly
                // additionally we let the frame expand horizontally to the edges of
                // the scrollview for horizontal centering to work                
                .frame(maxWidth: geo.size.width, minHeight: geo.size.height)
            }
        }
    }
}

Comments have been added to explain each commands purpose.

CodePudding user response:

Though @Fab1n's solution is a good one, it uses GeometryReader which can cause layout issues - I tend to stick away from this. Instead, this solution uses UIScreen.main.bounds.height and UIScreen.main.bounds.width.

struct MyImageView: View {
//This can be a UIImage or a normal SwiftUI image
    let image: UIImage

    var body: some View {
        
            ScrollView {
                Group {
                    Image(uiImage: self.image)
                        .resizable()
                        .scaledToFit()
                        .frame(maxWidth: image.size.width)

                }           
                .frame(maxWidth: UIScreen.main.bounds.width, minHeight: UIScreen.main.bounds.height)
        }
    }
}
  • Related