BUG: Failed to transition to new size for View.

Expected behavior on all tests: The red rectangle will animate it's size from zero to the size of the parent view when the Present button in the top right corner is tapped. When the button is tapped again, the red rectangle will shrink from the size of the parent view to zero.


TEST #1 PROPERTY STATE CHANGE

Actual behavior:

Works as expected.


Code:

struct ContentView: View {
    @State private var presentRedBox = false
   
    var body: some View {
        NavigationView {
            GeometryReader { proxy in
                ZStack {
                    // ------
                    Rectangle().fill(Color.red)
                        .frame(
                            width: self.presentRedBox ? proxy.size.width : 0.0,
                            height: self.presentRedBox ? proxy.size.height : 0.0
                        )
                    // ------
                }
            }.animation(.default)
            .navigationBarItems(trailing: Button("Present") { self.presentRedBox.toggle() })
            .navigationBarTitle(Text(""), displayMode: .inline)
        }
    }
}


TEST #2 ANIMATABLE/VIEW MODIFIER USING PROPERTY STATE CHANGE

Actual behavior:

Works as expected.


Code:

extension AnyTransition {
    static func sizeTransition(from: CGSize, to: CGSize) -> AnyTransition {
        .modifier(
            active: SizeTransition(size: from),
            identity: SizeTransition(size: to)
        )
    }
}

struct SizeTransition: AnimatableModifier {
    var size: CGSize
   
    var animatableData: AnimatablePair<cgfloat, cgfloat=""> {
        get { AnimatablePair(size.width, size.height) }
        set {
            size.width = newValue.first
            size.height = newValue.second
        }
    }
   
    func body(content: Content) -> some View {
        print(size)
        return content.frame(
            width: size.width,
            height: size.height
        )
    }
}

struct ContentView: View {
    @State private var presentRedBox = false
   
    var body: some View {
        NavigationView {
            GeometryReader { proxy in
                ZStack {
                    // ------
                    Rectangle().fill(Color.red)
                        .modifier(
                            SizeTransition(
                                size: self.presentRedBox ? proxy.size : .zero
                            )
                        )
                    // ------
                }
            }.animation(.default)
            .navigationBarItems(trailing: Button("Present") { self.presentRedBox.toggle() })
            .navigationBarTitle(Text(""), displayMode: .inline)
        }
    }
}


TEST #3 ANIMATABLE/VIEW MODIFIER WITH TRANSITION

Actual behavior:

The red box will animate in as expected. However(!) it will NOT animate out but disappear immediately, although the log shows the correct values.


Log animate in:

(0.0, 0.0)
(1.8118343353271484, 3.3873424530029297)
(7.392631530761719, 13.821006774902344)
(16.9350643157959, 31.66120719909668)
(30.5800838470459, 57.17146110534668)
(48.38059616088867, 90.45067977905273)
(70.25803184509277, 131.35197257995605)
(95.95654678344727, 179.39702224731445)
(124.99998664855957, 233.6956272125244)
(156.67254066467285, 292.90953254699707)
(190.03098106384277, 355.27531242370605)
(223.97296714782715, 418.73206901550293)
(257.33140754699707, 481.0978488922119)
(289.00356674194336, 540.3110160827637)
(318.04700660705566, 594.6096210479736)
(343.7447319030762, 642.6531944274902)
(365.6217727661133, 683.5537490844727)
(383.42189025878906, 716.8322296142578)
(397.06651496887207, 742.3417453765869)
(406.60855293273926, 760.1812076568604)
(412.18856048583984, 770.613395690918)
(414.0, 774.0)


Log animate out:

(413.61268043518066, 773.2758808135986)
(410.07547760009766, 766.6628494262695)
(402.6749496459961, 752.8270797729492)
(391.2381649017334, 731.4452648162842)
(375.6612854003906, 702.3232727050781)
(355.94628524780273, 665.4647941589355)
(332.24832916259766, 621.1599197387695)
(304.9215717315674, 570.070764541626)
(274.5523223876953, 513.2934722900391)
(241.9665470123291, 452.3722400665283)
(208.19354438781738, 389.231409072876)
(174.37908554077148, 326.0130729675293)
(141.67486381530762, 264.870397567749)
(111.12004852294922, 207.74617767333984)
(83.55758285522461, 156.21635055541992)
(59.59075355529785, 111.40880012512207)
(39.58871841430664, 74.01369094848633)
(23.71967124938965, 44.34547233581543)
(11.994667053222656, 22.42481231689453)
(4.315790176391602, 8.06865119934082)
(0.5136623382568359, 0.9603252410888672)
(0.0, 0.0)


Code:

extension AnyTransition {
    static func sizeTransition(from: CGSize, to: CGSize) -> AnyTransition {
        .modifier(
            active: SizeTransition(size: from),
            identity: SizeTransition(size: to)
        )
    }
}

struct SizeTransition: AnimatableModifier {
    var size: CGSize
   
    var animatableData: AnimatablePair<cgfloat, cgfloat=""> {
        get { AnimatablePair(size.width, size.height) }
        set {
            size.width = newValue.first
            size.height = newValue.second
        }
    }
   
    func body(content: Content) -> some View {
        print(size)
        return content.frame(
            width: size.width,
            height: size.height
        )
    }
}

struct ContentView: View {
    @State private var presentRedBox = false
   
    var body: some View {
        NavigationView {
            GeometryReader { proxy in
                ZStack {
                    // ------
                    if self.presentRedBox {
                        Rectangle().fill(Color.red)
                            .transition(
                                .modifier(
                                    active: SizeTransition(size: .zero),
                                    identity: SizeTransition(size: proxy.size)
                                )
                            )
                    }
                    // ------
                }
            }.animation(.default)
            .navigationBarItems(trailing: Button("Present") { self.presentRedBox.toggle() })
            .navigationBarTitle(Text(""), displayMode: .inline)
        }
    }
}


TEST #4 ANIMATABLE/VIEW MODIFIER WITH TRANSITION FOR OPACITY

Expected behavior:

The red rectangle will animate it's opacity from zero (hidden) to one (visible) when the

Present
button in the top right corner is tapped. When
Present
is tapped again, the red rectangle will hide from one (visible) to zero (hidden).


Actual behavior:

Works as expected.


Code:

extension AnyTransition {
    static func sizeTransition(from: CGSize, to: CGSize) -> AnyTransition {
        .modifier(
            active: SizeTransition(size: from),
            identity: SizeTransition(size: to)
        )
    }
}

struct SizeTransition: AnimatableModifier {
    var size: CGSize
   
    var animatableData: AnimatablePair<cgfloat, cgfloat=""> {
        get { AnimatablePair(size.width, size.height) }
        set {
            size.width = newValue.first
            size.height = newValue.second
        }
    }
   
    func body(content: Content) -> some View {
        print(size)
        return content.opacity(Double(size.width))
    }
}

struct ContentView: View {
    @State private var presentRedBox = false
   
    var body: some View {
        NavigationView {
            GeometryReader { proxy in
                ZStack {
                    // ------
                    if self.presentRedBox {
                        Rectangle().fill(Color.red)
                            .transition(
                                .modifier(
                                    active: SizeTransition(size: .zero),
                                    identity: SizeTransition(size: CGSize(width: 1.0, height: 1.0))
                                )
                            )
                    }
                    // ------
                }
            }.animation(.default)
            .navigationBarItems(trailing: Button("Present") { self.presentRedBox.toggle() })
            .navigationBarTitle(Text(""), displayMode: .inline)
        }
    }
}

Replies

Isn't it a thread you have deleted ? I did post an answer but it was deleted with your post as well (a bit unpleasant).


I'll try to find what I had posted and that worked, not sure I will get it fully OK now and did not check how you changed your code from previous post (and cannot spend time to rebuild completely). Just noticed some var has changed name.


Here is what I tried ; it works OK. Just shows blue box to start with. If don't want to, you should create a var for initialDisplay (but I find it better to show blue box at start).


Main difference is the addition of an else clause for

if self.presentRedBox {



import SwiftUI

struct FullScreenBoxTransition: ViewModifier {
    let width: CGFloat
    let height: CGFloat
    func body(content: Content) -> some View {
        content.frame(width: self.width, height: self.height)
    }
}
extension AnyTransition {
    static func fullScreenBoxTransition(from: CGSize, to: CGSize) -> AnyTransition {
        AnyTransition.modifier(
            active: FullScreenBoxTransition(width: from.width, height: from.height),
            identity: FullScreenBoxTransition(width: to.width, height: to.height)
        )
    }
}

struct ContentView: View {
    @State private var fullScreenMode: Bool = false
    var body: some View {
        GeometryReader { proxy in
            ZStack {
                Rectangle()
                    .fill(Color.red)
                    .frame(width: 80.0, height: 80.0)
                    .offset(y: -160.0)
                    .onTapGesture {
                        withAnimation {
                            self.fullScreenMode = true
                        }
                }
                if self.fullScreenMode {
                    Rectangle()
                        .fill(Color.blue)
                        .animation(.easeInOut(duration: 4))
                        .transition(.fullScreenBoxTransition(from: CGSize(width: 80.0, height: 80.0), to: proxy.size))
                        .zIndex(1)
                        .onTapGesture {
                            self.fullScreenMode = false
                    }
                } else {
                    Rectangle()
                        .fill(Color.blue)
                        .animation(.easeInOut(duration: 4))
                        .transition(.fullScreenBoxTransition(from: proxy.size, to: CGSize(width: 80.0, height: 80.0)))
                        .zIndex(1)
                }
            }
        }.edgesIgnoringSafeArea(.all)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


Did you finally succeed to transition ?

If so, thanks to confirm by closing the thread on correct answer.

If no, what is the problem ?