Transition freezes for a moment after moving a couple pixels - SwiftUI - swiftui

I have a grid view with a floating button overlaid on top, which appears and disappears through the bottom edge (using .transition(.move(edge: .bottom)). However, when it disappears, a.k.a when it slides back down, it freezes for a second, and then goes all the way down. I've been trying to pinpoint the culprit, but I haven't found anything – it's not because I'm using an overlay, nor is it because of the safe area, and it's not due to the button's shape.
Here's a screen recording showing the button appearing and disappearing, and the momentary freezing.
And this is what my code looks like:
var body: some View {
ScrollView {
// ...
}
.overlay(alignment: .bottom) {
VStack {
if condition {
Button(action: { /* ... */ }) {
Text("Share")
}
.background { LinearGradient(...) }
.clipShape(Capsule())
.transition(.move(edge: .bottom))
}
}
.animation(.linear, value: condition)
}
}
Any idea why the transition momentarily freezes like this?

Related

ScrollViewReader / scrollTo(_:anchor:) not working reliably

I've build a ScrollView which contains 0-3 images and a multiline text field in a VStack. I also added a ScrollViewReader inside the scrollview and use it to scroll to the bottom of the text field upon certain events (user starts typing, image collection changes).
The point is: sometimes it works, sometimes it doesn't. When it does not work I realized, that when I scroll a little bit by hand and then try again (e.g. typing) it works.
Not sure if this is relevant, but ImageOrPlaceholderComponent first shows a placeholder as long as the image within currentEntryImages is nil, and the image after that (both states imply a change to currentEntryImages and should thus result in scrolling to the bottom of the text field).
NavigationStack {
ScrollView {
ScrollViewReader { scrollview in
VStack {
// Attached images.
AnyLayout(VStackLayout(spacing: 2.5)) {
ForEach(values: currentEntryImages) { entryImage in
ImageOrPlaceholderComponent(image: entryImage)
.clipped()
}
}
// Text field for the entry with toolbar.
TextField("...", text: $entryDTO.text, axis: .vertical)
.id(entryTextFieldAnchor)
.multilineTextAlignment(.leading)
.padding()
.focused($mainTextFieldFocused)
.onAppear { mainTextFieldFocused = true }
// Scroll to the bottom of the text field, when the user is typing ...
.onChange(of: entryDTO.text) { _ in
withAnimation {
scrollview.scrollTo(entryTextFieldAnchor, anchor: .bottom)
}
}
// ... or the entry images have changed.
.onChange(of: currentEntryImages) { _ in
withAnimation {
scrollview.scrollTo(entryTextFieldAnchor, anchor: .bottom)
}
}
}
}
}
}

SwiftUI ScrollView horizontal scroll lag on macOS

Faced with a very strange ScrollView behavior on macOS. The content freezes under the mouse during horizontal scrolling. But it is worth taking the mouse away from the window and the content scrolls normally.
This happens when I try to use a vertical scroll inside a horizontal one:
struct ScrollTestView: View {
var body: some View {
ScrollView(.horizontal) {
ScrollView(.vertical) {
VStack {
ForEach(0..<20, id: \.self) { row in
HStack {
ForEach(0..<20, id: \.self) { item in
Text("\(item)")
.font(.title)
.padding()
.background {
Color.gray
}
}
}
}
}
}
}
}
}
Yes, I know that I can use the same ScrollView for both axes simultaneously, but I need solution with two ScrollViews because of desired UX.
This solution is perfectly works on iOS, but I have this strange behavior on macOS.
Also if you swap a horizontal and a vertical ScrollView in the exact same code, everything works just fine:
struct ScrollTestView: View {
var body: some View {
ScrollView(.vertical) {
ScrollView(.horizontal) {
// ...
}
}
}
}
Looks like this is a SwiftUI bug, but I am not sure, maybe I am missing something?
Any ideas?

SwifUI onTapGesture inside ScollView not alway detect tap

I have problem with Views that have onTapGesture and are placed inside ScollView
This onTapGesture is not always reacting to tap gesture.
I need to tap precisely on such view.
It seems like there is conflict with ScrollView drag?
I've tried
highPriorityGesture
onTapGesture
gesture(DragGesture(minimumDistance:0).onChange { })
gesture(TapGesture().onEnded { })
Views have contentShape(Rectangle()) added to them
It somtimes works ok sometimes doesn't. On simulature it most of the time works ok, on physical device it is much worse.
ScrollViewReader { proxy in
HStack(spacing: spacing) {
ForEach(0 ..< elements.count, id: \.self) { i in
Text(elements[i])
.fixedSize()
.contentShape(Rectangle())
.onTapGesture {
withAnimation {
selectedElement = i
}
}
}
I couldn't reproduce the behavior that you describe with that example code, but maybe you could try the following modifier in case another gesture is operating at the same time:
.simultaneousGesture(TapGesture().onEnded({
selectedElement = 1
}))

Is creating a button slower than creating Text?

I have a LazyVGrid in a ScrollViewcontaining up to 1000s of items. I started with having a button as item:
ScrollView {
LazyVGrid(columns: columns, spacing: 0) {
ForEach(0..<number, id: \.self) { i in
Button(action: {
self.action(i)
}) {
ZStack {
...
}
}
}
}
}
But that leads to extremly high cpu usage when scrolling (always 97%+) and very "unsmooth" scrolling. When I change it to Text with inTapGesture it is way more performant and cpu only goes up to >90% for a short time.
ScrollView {
LazyVGrid(columns: columns, spacing: 0) {
ForEach(0..<number, id: \.self) { i in
ZStack {
Text().onTapGesture {
self.action(i)
}
...
}
}
}
}
}
Why is that the case? Is it so much harder to render a Button than a Text? I like the "touch" gesture of the button that's why I would prefer a Button, but not if it is so much slower.
Normally there is always a competition between drag gesture (means items use drag gesture like: Button, Slider, Toggle, . .) and scrolling functionality (means items you can scroll: ScrollView, List, Picker, . .) and it is better always use Text with on tap gesture because it doesn't compete with scrolling on drag gesture.
Button use drag gesture but Text does not, there you would see better Performance with Text.

How to solve this top bar problem on SwiftUI, elegantly?

I am trying to use the whole iPhone area for my app.
I have this HStack at the top, used to create a custom toolbar.
var body: some View {
VStack (spacing:0) {
MyTopbar()
// other controls
Spacer()
}
.edgesIgnoringSafeArea(.top)
This appears like this on new devices with a notch and old devices without a notch. The notch cuts my menu.
I can solve that by adding a spacer with a frame height before MyTopbar() on the vertical stack but first of all this seems to be a very awful solution. First I have to guess a height for that spacer. Then I have to detect if the device has a notch or not (?).
Is there a better way?
You can think of it as layers (content that respects safe area and content that doesn't).
Something like this perhaps:
struct ContentView: View {
var body: some View {
ZStack {
Color.blue.ignoresSafeArea() // Whatever view fills the whole screen
VStack (spacing:0) {
MyTopbar()
// other controls
Spacer()
}
}
}
}
A possible solution to add clear color with safe area height. No need for much calculation.
var body: some View {
VStack (spacing:0) {
Color.clear.frame(height: Color.clear.frame(height: UIApplication.shared.windows.first?.safeAreaInsets.top ?? 0)
MyTopbar()
// other controls
Spacer()
}
.edgesIgnoringSafeArea(.top)