SwiftUI: Error in dragging logic or is this a bug?
DragGesture
's default CoordinateSpace
is .local
, which is the coordinate space inside your Circle
. What happens when you move the Circle
? Since your finger doesn't move, the location of your finger in the Circle
's geometry suddenly changes, which causes the Circle
to move again. Repeat ad nauseum.
Try using CoordinateSpace.global
:
DragGesture(minimumDistance: 10, coordinateSpace: .global)
You'll probably also want to use value.translation
instead of value.location
to avoid the initial jump when you put your finger down.
Focusing a TextField causes LayoutConstraint errors in Super Simple SwiftUI app
I am unable to reproduce in 14.3 Apparently it has been fixed.
SwiftUI ZStack alignment bug
I've seen such Text
truncation problems often in SwiftUI. The workaround which most of the time helps: ensure that Text
is allowed to adjust it's font size automatically using the minimumScaleFactor
modifier.
By modifying VCardView
Text(self.text)
.minimumScaleFactor(0.5)
The misalignment problem disappears for all possible text values (text1
, text2
, text2Alt1
and text2Alt2
).
The resulting text does not look scaled at all. Everything fits nicely.
I do not have a good explanation, but I think if you tell SwiftUI that your Text
is not 100% rigid/stiff, it performs better in calculating the elements extents.
Perhaps it is a SwiftUI bug, but I was always OK with this minimumScaleFactor
workaround as it hadn't negatively impacted my results.
ScrollView + NavigationView animation glitch SwiftUI
Setting the top padding to 1 is breaking at least 2 major things:
- The scroll view does not extend under NavigationView and TabView - this making it loose the beautiful blur effect of the content that scrolls under the bars.
- Setting background on the scroll view will cause Large Title NavigationView to stop collapsing.
I've encountered these issues when i had to change the background color on all screens of the app i was working on.
So i did a little bit more digging and experimenting and managed to figure out a pretty nice solution to the problem.
Here is the raw solution:
We wrap the ScrollView into 2 geometry readers.
The top one is respecting the safe area - we need this one in order to read the safe area insets
The second is going full screen.
We put the scroll view into the second geometry reader - making it size to full screen.
Then we add the content using VStack, by applying safe area paddings.
At the end - we have scroll view that does not flicker and accepts background without breaking the large title of the navigation bar.
struct ContentView: View {
var body: some View {
NavigationView {
GeometryReader { geometryWithSafeArea in
GeometryReader { geometry in
ScrollView {
VStack {
Color.red.frame(width: 100, height: 100, alignment: .center)
ForEach(0..<5) { i in
Text("\(i)")
.frame(maxWidth: .infinity)
.background(Color.green)
Spacer()
}
Color.red.frame(width: 100, height: 100, alignment: .center)
}
.padding(.top, geometryWithSafeArea.safeAreaInsets.top)
.padding(.bottom, geometryWithSafeArea.safeAreaInsets.bottom)
.padding(.leading, geometryWithSafeArea.safeAreaInsets.leading)
.padding(.trailing, geometryWithSafeArea.safeAreaInsets.trailing)
}
.background(Color.yellow)
}
.edgesIgnoringSafeArea(.all)
}
.navigationBarTitle(Text("Example"))
}
}
}
The elegant solution
Since the solution is clear now - lets create an elegant solution that can be reused and applied to any existing ScrollView by just replacing the padding fix.
We create an extension of ScrollView that declares the fixFlickering
function.
The logic is basically we wrap the receiver into the geometry readers and wrap its content into the VStack with the safe area paddings - that's it.
The ScrollView is used, because the compiler incorrectly infers the Content of the nested scroll view as should being the same as the receiver. Declaring AnyView explicitly will make it accept the wrapped content.
There are 2 overloads:
- the first one does not accept any arguments and you can just call it on any of your existing scroll views, eg. you can replace the
.padding(.top, 1)
with.fixFlickering()
- thats it. - the second one accept a configurator closure, which is used to give you the chance to setup the nested scroll view. Thats needed because we don't use the receiver and just wrap it, but we create a new instance of ScrollView and use only the receiver's configuration and content. In this closure you can modify the provided ScrollView in any way you would like, eg. setting a background color.
extension ScrollView {
public func fixFlickering() -> some View {
return self.fixFlickering { (scrollView) in
return scrollView
}
}
public func fixFlickering<T: View>(@ViewBuilder configurator: @escaping (ScrollView<AnyView>) -> T) -> some View {
GeometryReader { geometryWithSafeArea in
GeometryReader { geometry in
configurator(
ScrollView<AnyView>(self.axes, showsIndicators: self.showsIndicators) {
AnyView(
VStack {
self.content
}
.padding(.top, geometryWithSafeArea.safeAreaInsets.top)
.padding(.bottom, geometryWithSafeArea.safeAreaInsets.bottom)
.padding(.leading, geometryWithSafeArea.safeAreaInsets.leading)
.padding(.trailing, geometryWithSafeArea.safeAreaInsets.trailing)
)
}
)
}
.edgesIgnoringSafeArea(.all)
}
}
}
Example 1
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
VStack {
Color.red.frame(width: 100, height: 100, alignment: .center)
ForEach(0..<5) { i in
Text("\(i)")
.frame(maxWidth: .infinity)
.background(Color.green)
Spacer()
}
Color.red.frame(width: 100, height: 100, alignment: .center)
}
}
.fixFlickering { scrollView in
scrollView
.background(Color.yellow)
}
.navigationBarTitle(Text("Example"))
}
}
}
Example 2
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
VStack {
Color.red.frame(width: 100, height: 100, alignment: .center)
ForEach(0..<5) { i in
Text("\(i)")
.frame(maxWidth: .infinity)
.background(Color.green)
Spacer()
}
Color.red.frame(width: 100, height: 100, alignment: .center)
}
}
.fixFlickering()
.navigationBarTitle(Text("Example"))
}
}
}
Multiple sheet(isPresented:) doesn't work in SwiftUI
UPD
Starting from Xcode 12.5.0 Beta 3 (3 March 2021) this question makes no sense anymore as it is possible now to have multiple .sheet(isPresented:)
or .fullScreenCover(isPresented:)
in a row and the code presented in the question will work just fine.
Nevertheless I find this answer still valid as it organizes the sheets very well and makes the code clean and much more readable - you have one source of truth instead of a couple of independent booleans
The actual answer
Best way to do it, which also works for iOS 14:
enum ActiveSheet: Identifiable {
case first, second
var id: Int {
hashValue
}
}
struct YourView: View {
@State var activeSheet: ActiveSheet?
var body: some View {
VStack {
Button {
activeSheet = .first
} label: {
Text("Activate first sheet")
}
Button {
activeSheet = .second
} label: {
Text("Activate second sheet")
}
}
.sheet(item: $activeSheet) { item in
switch item {
case .first:
FirstView()
case .second:
SecondView()
}
}
}
}
Read more here: https://developer.apple.com/documentation/swiftui/view/sheet(item:ondismiss:content:)
To hide the sheet just set activeSheet = nil
Bonus:
If you want your sheet to be fullscreen, then use the very same code, but instead of .sheet
write .fullScreenCover
Related Topics
How to Request Photo Gallery Permissions in iOS a Second Time
iOS 8 Uitableview First Row Has Wrong Height
How to Post Data Using Afnetworking 2.0
Phonegap Notification.Alert Not Working
Remove Duplicate Structs in Array Based on Struct Property in Swift
How to Create a Scnnode from a .Usdz
iOS 13.2 Message: Nehelper Sent Invalid Result Code [1] for Wi-Fi Information Request
Uitableview Scroll Erases Data in Text Field Inside UItableviewcell
Uicollectionview - Horizontal Paging with One Cell at a Time
Scenekit Flattenedclone - Incorrect Use of Objc_Storeweak() and Objc_Loadweak() Error
How to Fix Crash When Tap to Select Row After Scrolling The Tableview
Avplayer Removing Observer Crash in Swift 2.2
How to Get Path to a Subfolder in Main Bundle
Add Udid in Current Provisioning Profile
How to Set UIactivityviewcontroller Gmail Share Subject Different Than Body