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:
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)
}
}