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:
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
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:
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.
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()
}
}
}
}
}
Related Topics
How to Use Coreaudio's Audioconverter to Encode Aac in Real-Time
Not Receiving Any Push Notification in Iphone
Strong Reference to a Weak References Inside Blocks
Sfspeechrecognizer - Detect End of Utterance
How to Wait for a Function to End on iOS/Swift, Before Starting the Second One
How to Port "Method_Getimplementation" and "Method_Setimplementation" to Monotouch
How to Properly Use Class Extensions in Swift
Uitextfield Not Getting Keyboard Input
Could Not Attach to Pid:"####" Unable to Attach
iOS 6 Streaming Player Com.Apple.Coremedia.Networkbuffering Bug
Skmutablepayment Property: Applicationusername
Find Size Contributed by Each External Library on iOS
Endless Scrolling Background in Spritekit
Uialertcontroller Handle Dismiss Upon Click Outside (Ipad)
To Change the Color of Unselected Uitabbar Icon in iOS 7
How to Make Your Push Notification Open a Certain View Controller