SwiftUI animation of a ScrollView - swiftui

I have two views (VStacks) one on top of the other in a ZStack
The first VStack that is behind the second view is used like a menu. The second view that is on top, is the main application view.
When i click on a button i'm scaling down the main view and move it a bit to the right side of the screen to show the menu.
I have a ScrollView inside the main view every time i scale down or up the main view, the animation doesn't work smoothly the page flickers and it looks ugly. if i remove the ScrollView it works perfectly fine.
I tried to replace the ScrollView with a List but it didn't solve the problem the flickering remains the same.
Is there any way to fix this glitch?
below is a sample code
struct ContentView: View {
var body: some View {
ZStack {
Menu()
VStack {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing:0) {
Header()
MainAppView()
}
}
TabBar()
}
.scaleEffect(self.openMenu ? 0.5 : 1, anchor: UnitPoint(x: 1.5, y: 0.5))
.animation(.easeInOut(duration: 0.2))
}
}
}
Please click to check animated gif example

What you can try is, to make screenshot of the flickering view and then do the animation with the screenshot which you just add on the real view. After the animation just remove the screenshot. Of course you have to make the screenshot programmatically.

I can't repeat all the behavior, because of lack of code. I think you have this problem, because you're scaling down ScrollView and it recomputes all the content. Try just to make some offset for it, instead of scaling and there should be no flickers:
// replace this
.scaleEffect(self.openMenu ? 0.5 : 1, anchor: UnitPoint(x: 1.5, y: 0.5))
// with this:
.offset(x: self.openMenu ? 300 : 0)

Related

gutters on sides of List in WatchOS

I'm dipping my toe into SwiftUI and WatchOS for the first time. I'm making good progress, but I can't figure out how to get rid of the black "gutters" on either side of my Image controls. I've tried setting all the backgrounds to white, but the gutter persists.
What property on which view do I need to set to change the color of the gutters to match the background?
SwiftUI
struct ContentView: View {
var body: some View {
List {
Image("cat-1").resizable().scaledToFill().background(Color.white)
Image("cat-2").resizable().scaledToFit().padding(5).background(Color.white)
Image("cat-3").resizable().scaledToFit().padding(.top, 5).background(Color.white)
}.background(Color.white).listStyle(CarouselListStyle())
.background(Color.white)
}
}
Try adding a
.listRowPlatterColor(.clear)
put it inside the list like this...
struct ContentView: View {
var body: some View {
List {
Image("cat-1")
.resizable()
.scaledToFit()
.listRowPlatterColor(.clear)
Image("cat-2").resizable().scaledToFit().padding(5).background(Color.white)
Image("cat-1")
.resizable()
.scaledToFit()
.padding(.top, 5)
.background(Color.white)
}
.listStyle(CarouselListStyle())
}
}
I put it in the first item and left it off of the second and third so that you could see the difference. This other question can provide some more details:
How to style rows in SwiftUI List on WatchOS? .
You should then be able to style it however you like.

How to get rid of empty space when trying to insert a view flush to navigation bar, while simultaneously assigning navigation view a background color

To my knowledge, the only way to assign a navigation bar a background color that is separate from the rest of the screen, you set the background color to whatever object you have flush with the navigation view. (In this case, its a divider with the background set to red)
My intention is to then place a view flush to the divider in an attempt to create some sort of "subtitle view". The problem is, as you can see, there is a space between my Stack and my Divider... I'm not sure what is causing this space and I am not sure how to get rid of it.
My first thought was, perhaps there is some safe area being adhered to... That said, I tried ignoring that, but that didn't work.
struct ContentView: View {
var body: some View {
NavigationStack {
VStack {
Divider()
.background(.red)
.navigationTitle("Main Title")
// There is a space here
ZStack {
Rectangle().frame(height: 35)
Text("Subtitle View")
.foregroundColor(.white)
}.edgesIgnoringSafeArea([.top, .bottom])
Spacer()
}.background(.blue)
}
}
}

SwiftUI DragGesture swallowed by ScrollView

I want to register a certain drag gesture on any SwiftUI view, including ScrollViews simultaneously with any other gestures (i.e. without influencing existing gestures). However, when adding a DragGesture on a ScrollView as follows, it seems like the gesture is immediately swallowed by the ScrollView.
ScrollView {
Rectangle()
.frame(width: 500, height: 500)
}
.simultaneousGesture(
DragGesture()
.onChanged { value in
print("changed:", value.translation)
}
.onEnded { value in
print("ended:", value.translation)
}
)
When I drag the Rectangle, this code prints:
changed: (3.0, 16.666671752929688)
for example.
So onChanged is only called once, onEnded is never called.
Is there a way to make DragGestures work with ScrollViews as well?
Note:
I tried to find a workaround with GestureState and the updating(_:) modifier as well, but it didn't work either.

SwiftUI Buttons in a ScrollView is not tappable sometimes

I have a horizontal ScrollView on top of a MapView.
The ScorllView is a collection of Buttons. It is weird that the buttons in the ScrollView are sometime tapable and sometimes not. First tap always works but after that I have to scroll a bit, tap around different areas in the button, make some secret prayers and then it works!
I tried disabling/removing all other components in the view, but still unable to figure out the root cause.
Has anyone experience this ?
I stuck with a same issue with horizontal ScrollView on top and List. While debugging I added empty .onTapGesture to ScrollView and it somehow fix my issue.
VStack(spacing: 0) {
ScrollView(.horizontal) {
HStack {
Button("one") {}
Button("two") {}
Button("three") {}
}
}
.onTapGesture { // <---- fix
}
List {
}
}
I also faced the same issue for Horizontal Scroll view in Swiftui dark theme "CameraTimerItem" buttons not clickable (Problem with dark theme only). Then I put a onTapGesture without any action. It's starts to work normally. I think it's a error of SwiftUI.
VStack (alignment:.center){
ScrollView(.horizontal, showsIndicators: false) {
HStack{
ForEach(timeSlots,id: \.self) { item in
CameraTimerItem(cellTitle: item)
}
}
.frame(width: AppUtils.width, alignment: .center)
}
.onTapGesture {
// <---- This is the solution
}
}
To anyone else having this issue, instead of adding an empty .onTapGesture view modifier, check that any HStacks in the ScrollView hierarchy have a .contentShape(Rectangle()) modifier. By default, HStacks don't accept taps in between their child views, and depending on your child view's layout this can cause taps to be missed even when it looks like they should be landing. .contentShape(Rectangle()) makes the entire frame of the HStack tappable.

Project ARKit + SwiftUI + Different layout preview/simulator

I cannot understand why in the simulator the layout is different from the layout displayed in xcode/preview.
struct ContentView : View {
var body: some View {
ZStack(alignment: .bottom) {
ARViewContainer()
Text("hello")
}
}
}
here the screenshots:
TLDR; I don't know why your simulator and preview don't match, but I do know why it's appearing the way that it is on the device. Are you getting any errors in the debug?
Any container views in SwiftUI will only take up the required space that they need. they will also distribute according to your settings. For example, you have a ZStack that contains a bottom alignment. You also have a ARViewContainter() that takes up a portion of that stack. They are aligned behind each other on the Z axis where the text is in front and the other container is behind. A quick way to prove this and test it is to include a background shape behind everything for example.
var body: some View {
ZStack {
Rectangle().edgesIgnoringSafeArea(.all)
.foregroundColor(Color.red)
//Your Views
}
}
This will force the ZStack to take up all available space and your other views should then align as expected. Basically, your Text is aligning to the bottom of the maximum provided space, which is being provided by your ARViewContainer()
Further Reading and Understanding
Views only take up the space required for what's in them, unless otherwise specified.
ZStacks operate on the Z axis, forward/backwards.
In your case you have a view with a set size called ARViewContainer() which takes up the width of the screen and a portion of the height. Since it's the largest view you have, the ZStack inherits that size.
Your text is smaller than the ZStack so the ZStack does NOT inherit the size. You do however have a .bottom assignment. So your text is over your ARViewContainer() and aligned to the .bottom edge of that container.
Finally the ZStack is centered in the remaining space available, giving it the impression that your .bottom isn't doing anything, when in reality it is.
Reproducing the Issue
Here is a code snippet that reproduces your issue and makes it a bit clearer and easier to understand.
var body: some View {
ZStack(alignment: .bottom) {
Rectangle().foregroundColor(Color.yellow).frame(width: 100, height: 100, alignment: .center)
Text("Test")
}
}