I'm struggling to find documentation to support this but it seems as though the values of
GeometryReader.size.width & height change when the keyboard opens. This can be proven through something like:
var body: some View {
TabView {
GeometryReader { g in
Rectangle()
.frame(width:g.size.width/2,height:g.size.height/20)
TextField(...)
}
}
}
which shows the rectangle resizing when the keyboard opens by clicking on the textfield.
How would I prevent this from happening? I want to specify the frame size relative to screen size to support many screen sizes...
You don't need a geometry reader to know the screen's size. you can get screen's dimensions using UIScreen.main.bounds.width and UIScreen.main.bounds.height.
Note width always shows the horizontal-dimension's size, and height always shows the vertical one (incase of screen rotation)
Add this to the GeometryReader:
.ignoresSafeArea(.keyboard)
Related
My app involves displaying <100 image thumbnails and for some reason my iPad Pro 2018 is struggling to scroll through the images smoothly. I recreated a simplified example below. The image is 200px square.
Replacing the images with colored rectangles eliminates the lag. Removing the shadow also removes the lag. I think rendering 50 images with a shadow should be within my device's capabilities, but let me know if anyone disagrees.
struct ContentView: View {
var body: some View {
ScrollView(.vertical, showsIndicators: false, content: {
let gridLayout = [(GridItem(.adaptive(minimum: 160)))]
LazyVGrid(columns: gridLayout, spacing: 8) {
ForEach(0..<50) { index in
Image("cookie_200")
.resizable()
.aspectRatio(1.0, contentMode: .fit)
.padding(8)
.shadow(radius: 4)
}
}
})
}
}
Question
Is there a less performance-intensive way to show these thumbnails?
Screen capture with shadow (laggy scroll):
https://share.icloud.com/photos/04eFNISH1khfkFqgAmGfGJJZw
Screen capture without shadow (smooth):
https://share.icloud.com/photos/02etl7kVG30Cnc6cr_dwSJK-Q
Image:
Shadows and transparency make the runtime do a lot of work. Hence the lag.
Question Is there a less performance-intensive way to show these thumbnails?
Yes. Instead of making the runtime draw the shadows, you draw the shadows. In particular you make an image consisting of the thumbnail and the shadow, on an opaque background the same color as your view background. Now scrolling is perfectly performant.
In SwiftUI when a horizontal array of Circles are made like this:
HStack(spacing : 4) {
Foreach(0..<5) { index in
Circle()
}
}
How the (horizontal) spacing value of 4 is applied? Is it between the centre of two circles or from their edges instead?
Building on top of #Asperi's comment: the spacing is applied in the same way it's applied to Text or Button standard views, which is between the frames of the views.
If you click on an element in the preview (this doesn't work when on live preview mode), you can see the frame of an element outlined in blue. The spacing is applied between the edges of the frames of each view.
When using a plain styled List in SwiftUI with more than one Text view inside a VStack as show below, the preview of the view when showing its context menu doesn't have corner radius. If you remove one of the Text views it will have corner radius. Also the rows that you need to scroll down to will also show corner radius most of the time. I've tried using the contentShape modifier with RoundedRectangle but doesn't fix it. How can I get it to show with corner radius all the time?
List {
ForEach(1...20, id: \.self) { _ in
VStack {
Text("Hello")
Text("World")
}.contextMenu {
Button {} label: { Text("Hello") }
}
}
}.listStyle(.plain)
I thinks you should fill a bug to Apple in this case.
After some try I have notice that if you define a frame for your Label , the view is correctly rounded.
I tryed to fixedSize the text without success :
Text("world").fixedSize()
The problem seems to come from _UIMorphingPlatterView, more precisely the _UIPlatterClippingView which use _UIPortalView.
If the frame is not fixed the clip is not apply correctly.
The debug view hierarchy give this :
I'm doing this tutorial and it requires me to place numbers(CGFloats) in for offset and padding, the problem is, this looks different on different devices. For example, on iPod touch, it goes off the screen.
My question is, how do I make these values change with the size of the screen? I know how to write a function to do this, so I guess I'm really asking: How do I retrieve the screen size data in order to use it?
CircleImage()
.offset(y: -130)
.padding(.bottom, -100)
You can use GeometryReader to get the size of the view that the item is in and then make calculations based on the size.
struct ContentView : View {
var body: some View {
GeometryReader { geometry in
Circle().offset(y: geometry.size.height / 4)
}
}
}
Note that this just retrieves the size of the current View and by default expands to fill available space.
Additional reading on GeometryReader: https://www.hackingwithswift.com/quick-start/swiftui/how-to-provide-relative-sizes-using-geometryreader
In SwiftUI, how does one change the color of the arrow that connects a popover to its anchor point?
When working with the underlying UIPopoverController outside of SwiftUI, I believe it's done by changing the backgroundColor property, but I don't see a way to access that here. Even setting background as the very last modifier only changes the view within the popover; not the popover itself.
For example, adding the following code to a view:
#State private var showDetailedView: Bool = false
// ...
.popover(isPresented: self.$showDetailedView) {
Text("Hello!")
.padding()
.background(Color.red)
}
.onTapGesture {
self.showDetailedView = true
}
...results in an arrow that's still the default background color (this example taken from native macOS in Dark Mode):
...and like this on iOS (running via Catalyst), which is even worse!
Here is a pure SwiftUI solution using GeometryReader and two .frame calls. The key idea is to make a background larger than the size of your presented view. Since SwiftUI does not clip contents at this moment, this will override the default background on the popover arrow.
Do notice that this only works with a solid background. In Catalyst, a solid background is already painted so transparent content would reveal the ugly black as you have posted. We might have to resort to things like UIViewRepresentable for such case.
Consider the following example that changes the color of an arrow on the top edge:
.background(GeometryReader { geometry in
Color
.white
.frame(width: geometry.size.width,
height: geometry.size.height + 100)
.frame(width: geometry.size.width,
height: geometry.size.height,
alignment: .bottom)
})
Explanation:
The first inner frame creates a white rectangle that is 100px higher than your presented view.
The second outer frame creates a new frame that is of the same size as your presented view. this is achieved through the GeometryReader.
The alignment: argument in the second outer frame makes sure that these two frames align on the bottom.
Since the outer frame is as large as the GeometryReader, it fills the whole background of your presented view.
The "overflowed" content overrides the default black arrow color.
To make this work with arbitrary arrow edge, you might want to change the inner frame, increasing both the width and height. As for the alignment for outer frame, using the default argument of .center should work.