如何在SwiftUI中来回转换视图的大小?

问题描述 投票:0回答:1

我正在尝试在不使用缩放的情况下将新插入的视图从紧凑尺寸转换为全屏尺寸。

步骤1:让我们从一个简单的示例开始。下面的代码将使一个红色和一个蓝色框。当您点击红色框时,蓝色框的大小将扩大,直到其填充父容器,从而填满整个屏幕。当您点击蓝色框时,它会缩小到其原始大小。该动画正是我们想要的,但是在插入视图时会中断,因此请继续阅读。

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

                Rectangle()
                    .fill(Color.blue)
                    .frame(
                        width: self.fullScreenMode ? proxy.size.width : 80.0,
                        height: self.fullScreenMode ? proxy.size.height : 80.0
                    )
                .zIndex(1)
                    .onTapGesture {
                        withAnimation {
                            self.fullScreenMode = false
                        }
                }

            }
        }.edgesIgnoringSafeArea(.all)
    }
}

第2步:我们将蓝色框包装在IF-STATEMENT中,以便仅在必要时才插入它。这将破坏我们需要的动画,并将动画切换为简单的淡入,淡出新插入的蓝色框。

(...)
if self.fullScreenMode {
    Rectangle()
        .fill(Color.blue)
        .frame(
            width: self.fullScreenMode ? proxy.size.width : 80.0,
            height: self.fullScreenMode ? proxy.size.height : 80.0
        )
    .zIndex(1)
    .onTapGesture {
        withAnimation {
            self.fullScreenMode = false
        }
    }
}
(...)

[第3步:人们可能会认为我们应该进行自定义转换以帮助我们。因此,让我们这样做。

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

现在让我们修改代码以添加修饰符。

(...)
if self.fullScreenMode {
    Rectangle()
        .fill(Color.blue)
        .animation(.easeInOut(duration: 2))
        .transition(.fullScreenBoxTransition(from: CGSize(width: 80.0, height: 80.0), to: proxy.size))
        .zIndex(1)
        .onTapGesture {
            self.fullScreenMode = false
        }
}
(...)

现在,当您单击红色框时,您将看到蓝色框动画显示为全屏(是!)。但是,当我们单击蓝色框时,它会立即消失而没有任何动画(嘘!)。我尝试了很多事情,我完全迷失了。

尝试完整的代码。

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
                    }
                }
            }
        }.edgesIgnoringSafeArea(.all)
    }
}
xcode animation swiftui transition anytransition
1个回答
1
投票

我知道这并不是您所要求的解决方案,但是在某些情况下可能有用,所以我决定将其发布。

注意:我是从您以前的邮政编码开始的,因此可能与本文不符。

最初建议的替代方案-仅基于动画。

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

可动画修饰语的使用与下面提供的调查相同。

过渡调查:

实际上,我认为这可能是一个错误,但是可能是过渡引擎的限制,因为基于effects>]的过渡,但这只是物理视图框架的更改,而视图实际上已经被删除了。所以50/50 ...也许值得向苹果报告反馈。

这是为什么...

demo2

我使用animatable修饰符通过可动画化的数据显式地更改框架,正如在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)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.