Как вы можете изменить размер представления назад и вперед в SwiftUI? - PullRequest
0 голосов
/ 27 апреля 2020

Редактировать: С помощью Asperi я решил переписать описание, чтобы лучше прояснить вопрос с помощью простого копирования + вставки кода.

Ожидаемое поведение во всех тестах: Красный прямоугольник будет анимировать свой размер от нуля до размера родительского представления при нажатии кнопки Present в верхнем правом углу. При повторном нажатии Present красный прямоугольник уменьшится с размера родительского представления до нуля.


ИСПЫТАНИЕ # 1 ИЗМЕНЕНИЕ СОСТОЯНИЯ СОБСТВЕННОСТИ

Фактическое поведение:

Работает, как ожидалось.

Код:

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)  
        }  
    }  
}  

ИСПЫТАНИЕ № 2 ANIMATABLE / ПОСМОТРЕТЬ МОДИФИКАТОР С ИСПОЛЬЗОВАНИЕМ ИЗМЕНЕНИЯ СОСТОЯНИЯ СОБСТВЕННОСТИ

Фактическое поведение:

Работает, как ожидалось.

Код:

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 С ПЕРЕХОДОМ

Фактическое поведение:

Красный прямоугольник оживит, как и ожидалось. Однако (!) он будет НЕ оживлять, но сразу исчезать, хотя в журнале отображаются правильные значения.

Журнал анимации входа

(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)  

Анимация журнала

(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)  

Код:

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)  
        }  
    }  
}  

ТЕСТ № 4 ANIMATABLE / ПОСМОТРЕТЬ МОДИФИКАТОР С ПЕРЕХОДОМ НА ПОМОЩЬ

Ожидаемое поведение:

Красный прямоугольник оживит свою непрозрачность от нуля (скрытого) до единицы (видимого), когда кнопка Present в правом верхнем углу постучал. При повторном нажатии Present красный прямоугольник будет скрываться от одного (видимого) до нуля (скрытого).

Фактическое поведение:

Работает, как ожидалось.

код:

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)
        }
    }
}

1 Ответ

1 голос
/ 27 апреля 2020

Это не совсем то, что вы просили, я знаю, но в некоторых случаях это может быть полезно, поэтому я решил опубликовать его.

Примечание: я начал с вашего предыдущего почтового индекса , поэтому может быть немного не согласован с этим постом.

Сначала предложена альтернативная анимация только на основе.

demo1

struct TestReverseTransitions: View {
    @State private var showRedBox = false
    var body: some View {
        VStack {
           Button("Tap") { self.showRedBox.toggle() }
              RedBox()
                 .modifier(SizeAnimation(size: showRedBox ? 
                           CGSize(width: 200.0, height: 200.0) : .zero))
        }.animation(.default)
    }
}

Модификатор анимации используется так же, как в исследовании, представленном ниже.

Исследование переходов:

На самом деле я думаю, что это может быть ошибкой, но может быть ограничение механизма переходов, потому что переходы, основанные на , влияют , но здесь просто изменение физического фрейма вида, в то время как вид фактически уже удален. Так что 50/50 ... возможно, стоит сообщить об обратной связи Apple.

Вот почему ...

demo2

Я использую анимацию модификатор для явного изменения фрейма через анимируемые данные, и, как видно из журнала отладки Demo2, фрейм действительно анимируемый при удалении блока, но внешний вид - нет, однако кнопка перемещается, как и должно быть. Ошибка? Возможно.

Код для этого случая:

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

struct SizeAnimation: 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) // << uncomment for log sizes !!!
        return content.frame(width: size.width, height: size.height)
    }
}

struct TestReverseTransitions: View {
    @State private var showRedBox = false
    var body: some View {
        VStack {
            Button("Tap") { self.showRedBox.toggle() }
            if self.showRedBox {
                RedBox()
                    .transition(
                        .size(from: CGSize.zero, to: CGSize(width: 200.0, height: 200.0))
                    )
            }
        }.animation(.default)
    }
}

struct RedBox: View {
    var body: some View {
        Rectangle().fill(Color.red)
    }
}
...