I am trying to make a transition from bottom edge to top when a view appears.
ZStack{
VStack {
.
.//other components
.
if self.modalShown {
HStack {
GeometryReader { geometry in
BottomSheetView(
isOpen: self.$modalShown,
maxHeight: geometry.size.height * 0.6
) {
ZStack(alignment: .bottom) {
FiltersView(hideControl: self.$modalShown)
.transition(.move(edge: .bottom))
}
.edgesIgnoringSafeArea(.bottom)
}
}
}
.accessibility(identifier: "modalFilterView")
}
}
The problem is that when this view appears, it performs a kind of alpha 0 to alpha 1 transition. And when the view is dismissed it does not perform any transition.
Could be related this undesired behavior with the zIndex?
The problem was that the transition modifier was in the wrong place:
ZStack{
VStack {
.
.//other components
.
if self.modalShown {
HStack {
GeometryReader { geometry in
BottomSheetView(
isOpen: self.$modalShown,
maxHeight: geometry.size.height * 0.6
) {
ZStack(alignment: .bottom) {
FiltersView(hideControl: self.$modalShown)
}
.edgesIgnoringSafeArea(.bottom)
}
}
}
.transition(.move(edge: .bottom))
.accessibility(identifier: "modalFilterView")
}
}
Related
I found the height was wrong when I'm dragging,how can i fix it?
The height in the tabview will be subtracted from the safe height when dragging.
work with tabview with background
work with tabview without background
work with backgroun without tabview
struct TabViewTest: View {
#State var offset: CGFloat = 0
#State var startY: CGFloat = 0
var body: some View {
ZStack(alignment: .top) {
VStack {
// work without tabview
// GeometryReader { proxy in
// ZStack {
// Color.green
// .frame(height: 100)
// Text("row height:\(proxy.size.height)")
// }
// }
// work with tabview without background
// TabView {
// ForEach(0..<5) { i in
// GeometryReader { proxy in
// Text("row\(i) height:\(proxy.size.height)")
// }
// }
// }
// work with tabview
TabView {
ForEach(0..<5) { i in
GeometryReader { proxy in
ZStack {
Color.green
.frame(height: 100)
Text("row\(i) height:\(proxy.size.height)")
}
}
}
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.frame(height: 150)
.background(.gray)
.offset(y: offset)
Spacer()
}
Color.brown
.frame(height: 300)
.offset(y: 200 + offset)
.gesture(
DragGesture()
.onChanged({ value in
offset = value.translation.height + startY
})
.onEnded({ value in
startY = offset
})
)
}
}
}
I hope the Tabview has a correct height,please help me.
I created an application with move transition, which sends a View up from below with SwiftUI. However, when View disappears, transition animation is over, but the View remains for about 1 second.
This is my code.
struct ContentView: View {
#State var animation: Bool = false
var body: some View {
VStack {
// This is the Button
Button(action: {
withAnimation(.spring(dampingFraction: 1, blendDuration: 0.5)) {
animation.toggle()
}
}) {
Image(systemName: "star.fill")
.resizable()
.frame(width: 100, height: 100)
.foregroundColor(.accentColor)
}
// This condition sends up the View
if animation {
SecondView()
.transition(.move(edge: .bottom))
}
}
.padding()
}
}
struct SecondView: View {
var body: some View {
VStack {
Text("Hello, world!")
.font(.largeTitle)
.fontWeight(.bold)
Spacer()
}
}
}
And this happened.
I think what you need to do is to apply the animation directly to your button by using the animation modifier so your code may look like this:
Button(action: {
withAnimation {
animation.toggle()
}
}) {
Image(systemName: "star.fill")
.resizable()
.frame(width: 100, height: 100)
.foregroundColor(.accentColor)
}
.animation(.spring(dampingFraction: 1, blendDuration: 0.5), value: animation)
if animation {
SecondView()
.transition(.move(edge: .bottom))
}
I'm trying to animate in a view and make it appear as if it's a sort of drawer opening from another view. This is all fine except if the first view is not opaque. It appears that you can see the animating view the moment it begins animating. Is there a way to clip this so it appears that the view is growing from the top of the bottom view?
Even without opacity this is an issue if where you're animating in from isn't a covered (demoed in second gif)
Sample Code:
struct ContentView: View {
#State private var showingSecondView: Bool = false
var body: some View {
VStack(spacing: 0) {
Spacer()
if showingSecondView {
ZStack {
Color.green.opacity(0.25)
Text("Second View")
}
.frame(width: 300, height: 300)
.transition(.move(edge: .bottom))
}
ZStack {
Color.black.opacity(1)
Text("First View")
}
.frame(width: 300, height: 300)
Button("Animate In / Out") {
showingSecondView.toggle()
}
.padding()
}
.animation(.easeInOut, value: showingSecondView)
}
}
It is possible to do by clipping exact container of 'drawer'. Here is a demo of possible approach.
Tested with Xcode 13.2 / iOS 15.2 (Simulator slow animation is ON for better demo)
var body: some View {
VStack(spacing: 0) {
Spacer()
VStack {
if showingSecondView {
ZStack {
Color.green.opacity(0.25)
Text("Second View")
}
.transition(.move(edge: .bottom))
} else {
Color.clear // << replacement for transition visibility
}
}
.frame(width: 300, height: 300)
.animation(.easeInOut, value: showingSecondView) // << animate drawer !!
.clipped() // << clip drawer area
ZStack {
Color.black.opacity(0.2)
Text("First View")
}
.frame(width: 300, height: 300)
Button("Animate In / Out") {
showingSecondView.toggle()
}
.padding()
}
}
Here a way for you:
struct ContentView: View {
#State private var isSecondViewPresented: Bool = false
var body: some View {
VStack(spacing: 0) {
Spacer()
ZStack {
Color.green.opacity(0.25).cornerRadius(20)
Text("Second View")
}
.frame(width: 300, height: 300)
.offset(y: isSecondViewPresented ? 0 : 300)
.clipShape(RoundedRectangle(cornerRadius: 20))
ZStack {
Color.black.opacity(0.1).cornerRadius(20)
Text("First View")
}
.frame(width: 300, height: 150)
Button("Animate In / Out") {
isSecondViewPresented.toggle()
}
.padding()
}
.animation(.easeInOut, value: isSecondViewPresented)
}
}
I have a header that is fixed in place using an offset relative to the scroll position. Strangely enough though, when the contents of the scroll view has a dynamic opacity to the buttons, the offset is very jumpy:
This is the scroll view code, the HeaderView is "fixed" in place by pinning the offset to the scroll view's offset. The opacity seems to be causing the performance issue is on the MyButtonStyle style on the last line of code:
struct ContentView: View {
#State private var isPresented = false
#State private var offsetY: CGFloat = 0
#State private var headerHeight: CGFloat = 200
var body: some View {
GeometryReader { screenGeometry in
ZStack {
Color(.label)
.ignoresSafeArea()
ScrollView {
VStack(spacing: 0.0) {
Color.clear
.frame(height: headerHeight)
.overlay(
HeaderView(isPresented: $isPresented)
.offset(y: offsetY != 0 ? headerHeight + screenGeometry.safeAreaInsets.top - offsetY : 0)
)
VStack(spacing: 16) {
VStack(alignment: .leading) {
ForEach(0...10, id: \.self) { index in
Button("Button \(index)") {}
.buttonStyle(MyButtonStyle(icon: Image(systemName: "alarm")))
}
}
Spacer()
}
.frame(maxWidth: .infinity, minHeight: screenGeometry.size.height)
.padding()
.background(
GeometryReader { geometry in
Color.white
.cornerRadius(32)
.onChange(of: geometry.frame(in: .global).minY) { newValue in
offsetY = newValue
}
}
)
}
}
}
.alert(isPresented: $isPresented) { Alert(title: Text("Button tapped")) }
}
}
}
struct HeaderView: View {
#Binding var isPresented: Bool
var body: some View {
VStack {
Image(systemName: "bell")
.resizable()
.frame(width: 100, height: 100)
.foregroundColor(Color(.systemBackground))
Button(action: { isPresented = false }) {
Text("Press")
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(16)
}
}
.padding()
}
}
struct MyButtonStyle: ButtonStyle {
let icon: Image
func makeBody(configuration: Configuration) -> some View {
Content(
configuration: configuration,
icon: icon
)
}
struct Content: View {
let configuration: Configuration
let icon: Image
var body: some View {
HStack(spacing: 18) {
Label(
title: { configuration.label },
icon: { icon.padding(.trailing, 8) }
)
Spacer()
Image(systemName: "chevron.right")
.accessibilityHidden(true)
}
.padding(18)
.foregroundColor(.white)
.background(Color.green)
.cornerRadius(8)
.opacity(configuration.isPressed ? 0.5 : 1) // <-- Comment this out and jumpiness goes away!!
}
}
}
Is there a performance improvement that can be done to use the opacity on the button press and make the jumpiness go away? Or a different way to approach this sticky offset because not sure if this is actually the source of the issue and I use opacity in a lot of places in my app (not just button presses)? The purpose of doing it this way is so the button can be tapped instead of putting it in the background of the scroll view. Thanks for any help or insight!
I'm trying to animate an image inside a "row" by it's y axis so that it appears that you would the view would slowly scroll vertically through the entire image. And when done, it backtracks. I hope that makes sense. I'm trying to do it in this code:
var body: some View {
ZStack {
HStack {
GeometryReader { geometry in
WebImage(url: self.url)
.renderingMode(.original)
.resizable()
.placeholder {
ImageStore.shared.image(name: "exploras-icon")
}
.aspectRatio(contentMode: .fill)
.animate {
// animate by y offset within bounds of HStack
}
}
}
.frame(height: 140)
.clipped()
}
}
Any help/guidance much appreciated!
Here is a demo of possible approach. Tested with Xcode 11.4 / iOS 13.4
struct TestAutoScrollImage: View {
#State private var isActive = false
var body: some View {
GeometryReader { gp in
HStack {
Image("large_image")
}
.frame(width: gp.size.width, height: gp.size.height, alignment: self.isActive ? .bottom : .top)
}
.edgesIgnoringSafeArea([.top, .bottom])
.animation(Animation.linear(duration: 5).repeatForever())
.onAppear {
self.isActive = true
}
}
}