Swiftui Transitions: Scale from Some Frame - Like iOS Homescreen Is Doing When Opening an App

swiftUI transitions: Scale from some frame - like iOS Homescreen is doing when opening an App

Here is a demo of idea how such effect could be done with combined transitions... (positions and sizes are hardcoded for demo simplicity - they can be read with geometry reader, alignment guides or anchor preferences, and actually do not affect the transition usage idea, also animation can be configured)

Demo:

SwiftUI view from button

struct TestRisingView: View {

let screen = UIScreen.main.bounds

@State var showingView = false
@State var btFrame: CGRect = .zero

var body: some View {
GeometryReader { g in
ZStack {
Rectangle().fill(Color.clear)

self.activatingButton(frame: CGRect(x: 80, y: 30, width: 60, height: 40))
self.activatingButton(frame: CGRect(x: self.screen.maxX - 80, y: 30, width: 60, height: 40))
self.activatingButton(frame: CGRect(x: self.screen.maxX - 80, y: self.screen.maxY - 60, width: 60, height: 40))
self.activatingButton(frame: CGRect(x: 80, y: self.screen.maxY - 60, width: 60, height: 40))

if self.showingView {
self.topView
.zIndex(1)
.transition(
AnyTransition.scale(scale: 0.12).combined(with:
AnyTransition.offset(x: self.btFrame.origin.x - g.size.width/2.0,
y: self.btFrame.origin.y - g.size.height/2.0))
)
}
}
}
}

func activatingButton(frame: CGRect) -> some View {
Button(action: {
withAnimation {
self.btFrame = frame
self.showingView.toggle()
}
}) {
Text("Tap")
.padding()
.background(Color.yellow)
}
.position(frame.origin)
}

var topView: some View {
Rectangle()
.fill(Color.green)
.frame(width: 300, height: 400)
}
}

How can I solve MatchedGeometryEffect fade effect issue in SwiftUI?

It is important a place of applied modifiers, here you insert/remove not a circle but VStack, so the fix is to move transition there.

Tested with Xcode 13.3 / iOS 15.4

demo

Main part is:

if update {
VStack {
Circle()
.matchedGeometryEffect(id: "Circle", in: namespaceOfCircle)
.frame(width: 100, height: 100)
Spacer()
}
.transition(.scale(scale: 1)) // << here !!

Complete findings and code is here

SwiftUI - unexpected behavior with custom transition modifying frame size

SwiftUI doesn't know how to animate your ShapeClipModifier so first thing you have to conform it to AnimatableModifier and add an animatableData to it:

var animatableData: CGFloat {
get { percent }
set { percent = newValue }
}

Another thing is not obvious at all - you need to wrap your content in your ShapeClipModifier.body(content:) into something like Color.clear , so:

func body(content: Content) -> some View {
let diff: CGFloat = biggerFrame - normalFrame
let currentFrame = normalFrame + percent * diff
return Color.clear.overlay(
content.frame(
width: currentFrame,
height: currentFrame,
alignment: .center
)
)
}

If you're interested in more details head on to SwiftUI Lab.

It works for me™️ /p>

SwiftUI WatchOS: Animation behaves strangely when placed inside a list?

Ok, if you change the animation method slightly it works,

Note the value argument, it seems this is the new way to animate

.animation(animate ? Animation.easeInOut(duration: 1.5).repeatForever(autoreverses: true) : nil, value: animate)
import SwiftUI

struct HeartRatePulseView: View {
@State var animate = false

@Environment(\.scenePhase) private var scenePhase

func circlesColor() -> Color {
Color.blue
}

var body: some View {
VStack(spacing: -3) {
ZStack {
ZStack {
GeometryReader { geometry in
ZStack {
Circle().fill(circlesColor().opacity(0.25)).frame(width: geometry.size.width, height: geometry.size.height).scaleEffect(self.animate ? 1 : 0.01)
Circle().fill(circlesColor().opacity(0.35)).frame(width: geometry.size.width * 0.79, height: geometry.size.height * 0.79).scaleEffect(self.animate ? 1 : 0.01)
Circle().fill(circlesColor()).frame(width: geometry.size.width * 0.60, height: geometry.size.height * 0.60)
}

.frame(width: geometry.size.width, height: geometry.size.height)
}
}
.onAppear { self.animate = true }
.onChange(of: scenePhase, perform: { newValue in
if newValue == .active {
self.animate = true
} else {
self.animate = false
}
})
.animation(animate ? Animation.easeInOut(duration: 1.5).repeatForever(autoreverses: true) : nil, value: animate)

}
}
.frame(height: 145)
}
}

struct HeartRatePulseView_Previews: PreviewProvider {
static var previews: some View {
List {
HeartRatePulseView()
HeartRatePulseView()
}
.listStyle(.carousel)
}
}

Why does an animation in SwiftUI jump around

You just need two small changes (see comments inlined in the code):

return RoundedRectangle(cornerRadius: Constants.cornerRadius)
.fill(Color.loadingColor)
.opacity(opacity)
.transition(.opacity)
.padding(.leading, 31)
.animation(repeatedAnimation, value: opacity) //<-- Add value: parameter
.onAppear {
DispatchQueue.main.async { //<-- wrap in DispatchQueue.main.async
self.opacity = Constants.maxOpacity
}
}

In terms of why: For the animation modifier, use of the animation without the value parameter is deprecated and we should expect to use value:. For the DispatchQueue, my best guess is that the NavigationView has an implicit animation attached and we need to start this animation on the next run loop to make sure that it's separate.

SwiftUI: How do I transition gracefully when parent view changes?

The .transition needs to be applied to the container of the views that you want to switch over, so you'll need to embed your switch inside a Group, VStack, or similar.

Also, you need to have either a .animation(.default) on the container, or change appState.userFlow inside a withAnimation {} block to actually animate the change.

enum UserFlow {
case onboarding
case login
case home
}
struct ContentView: View {
@State var userFlow = UserFlow.home
var body: some View {

/// container
Group {

switch userFlow {
case .onboarding:
Text("Not Yet Implemented")
case .login:
Text("Login page")
case .home:
Text("Home page")
}

}
.animation(.default) /// also need Animation
.transition(.opacity)

/// for testing
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
userFlow = .login
}
}
}
}

Result:

Text "Home page" fades to "Login page"

SwiftUI transition is not working when in HSack

.transition only works if you insert something conditionally into the view.

I'm not totally sure what you want to achieve, but if you want the whole view to slide from the bottom, this would work.

Sample Image

struct ContentView: View {

@State private var showText = false
let bottomPadding: CGFloat = 150

var body: some View {

ZStack {
if showText { // conditional view to make .transition work

// VisualEffectViewSUI(effect: UIBlurEffect(style: .regular))
// .edgesIgnoringSafeArea(.all)

VStack(alignment: .center) {
Spacer()
HStack {
VStack(alignment: .leading, spacing: 15) {
ForEach(0 ..< 3) { item in
HStack(alignment: .center) {
Image(systemName: "\(item).circle")
.resizable()
.scaledToFit()
.frame(width: 24)

Spacer()
.frame(width: 15)

Text("Item \(item)")
.foregroundColor(.white)
}
}
}
}
}
.frame(maxWidth: .infinity)
.padding(.bottom, bottomPadding)

.background(
LinearGradient(gradient: Gradient(colors: [.blue.opacity(0), .blue]),
startPoint: .top, endPoint: .bottom)
)
.edgesIgnoringSafeArea(.all)
.transition(.move(edge: .bottom)) // here for whole modal view

}

Button("Show modal") {
withAnimation {
showText.toggle()
}
}

}
}
}

Why does SwiftUI opacity animation fail in Gesture's onEnded method?

use linear if its just for change opacity

struct FadeTestView: View {
@State var fade: Bool = false
var body: some View {
VStack{
Rectangle().fill(.blue).frame(width: 100, height: 100, alignment: .center)

.opacity(fade ? 0 : 1)

Button(action: {
withAnimation(.linear(duration: 2)){ fade.toggle() }
}){
Circle().fill(.yellow).frame(width: 50, height: 50, alignment: .center)
}

Rectangle().fill(.purple).frame(width: 50, height: 50, alignment: .center)
.onTapGesture() {
withAnimation(.linear(duration: 2)){
fade.toggle()
}
}

}
}

}

Sample Image



Related Topics



Leave a reply



Submit