Home > Enterprise >  How Do I Make Frames Relative To The Size Of A Device?
How Do I Make Frames Relative To The Size Of A Device?

Time:10-13

I'm trying to get continuity in my views across all device sizes but my frames are a fixed size. I need to get them to change depending on the size of the device. I have tried using Geometry Reader but it didn't seem to work in some cases. Is Geometry Reader the only way or are there better alternatives?

This is an example of where Geometry Reader made the frame respond weirdly.

(This view is supposed to be a rectangle 'tile')

struct ProductCardView: View {
         
    var body: some View {
        
          GeometryReader { geometry in
        
        
        VStack {
       
            Image("ProductImage")
   
            Text("Product Name)

        }
                    
        //MARK: Framing Before Geometry Reader .frame(width: 160, height: 210)       
        .frame(width: geometry.size.width * 0.40)
        .frame(height: geometry.size.height * 0.27)
      
       } 
    }
}


How do I make frames dynamic?

Here is how it is turning out:

enter image description here

CodePudding user response:

Add the following extension in some file

extension UIScreen{
   static let screenWidth = UIScreen.main.bounds.size.width
   static let screenHeight = UIScreen.main.bounds.size.height
   static let screenSize = UIScreen.main.bounds.size
}

And whenever you want to reference a value that should be a size relative to screen width or screen height, you can call UIScreen.screenWidth for example. For example, your frame modifier

.frame(width: screenSize.width * 0.40, height: screenSize.height * 0.27)

can now be

.frame(width: UIScreen.screenWidth * 0.40, height: UIScreen.screenHeight * 0.27)

CodePudding user response:

Uses Color.clear to fill all screen then calculate its size using GeometryReader and PreferenceKey.

struct ContentView: View {
    @State private var screenSize: CGSize = .zero
    
    var body: some View {
        ZStack {
            GeometryReader { geometryProxy in
                Color.clear.preference(key: SizePreferenceKey.self, value: geometryProxy.size)
            }
            .onPreferenceChange(SizePreferenceKey.self) { size in
                self.screenSize = size
            }
            VStack {
                Image("ProductImage")
                Text("Product Name")
            }
            .frame(width: self.screenSize.width * 0.40,
                   height: self.screenSize.height * 0.27)
        }
    }
}

fileprivate struct SizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) { }
}

For ScrollView

  • Just move the calculate size to the view contains ScrollView
struct ContentView: View {
    @State var screenSize: CGSize = .zero
    
    var body: some View {
        ZStack {
            GeometryReader { geometryProxy in
                Color.clear.preference(key: SizePreferenceKey.self, value: geometryProxy.size)
            }
            .onPreferenceChange(SizePreferenceKey.self) { size in
                self.screenSize = size
            }
            ScrollView { ForEach(0 ..< 4) { _ in
                CardView(screenSize: $screenSize)
            }}
        }
    }
}

struct CardView: View {
    @Binding var screenSize: CGSize
    
    var body: some View {
        VStack {
            Circle()
            Text("ProducImage")
        }
        .frame(width: screenSize.width * 0.40,
               height: screenSize.height * 0.27)
    }
}
  • Related