SwiftUI中的自定义模式转换

iAl*_*x11 6 ios swift swiftui

我正在尝试使用SwiftUI重新创建iOS 11/12 App Store 。假设“故事”是在点击卡片时显示的视图。

我已经完成了卡片的制作,但是现在遇到的问题是如何制作动画以显示“故事”。

由于我不擅长解释,因此这里有一个gif:

Gif 1 Gif 2

我曾考虑过将整个卡片做成PresentationLink,但是“故事”显示为模态,因此它不会覆盖整个屏幕,也不会做我想要的动画。

最相似的是NavigationLink,但这使我不得不添加NavigationView,卡的显示就像另一页一样。

我实际上不在乎它是PresentationLink还是NavigationLink或其他任何东西,只要它可以动画并显示“故事”即可。

提前致谢。

我的代码:

卡速

struct Card: View {
    var icon: UIImage = UIImage(named: "flappy")!
    var cardTitle: String = "Welcome to \nCards!"
    var cardSubtitle: String = ""
    var itemTitle: String = "Flappy Bird"
    var itemSubtitle: String = "Flap That!"
    var cardCategory: String = ""
    var textColor: UIColor = UIColor.white
    var background: String = ""
    var titleColor: Color = .black
    var backgroundColor: Color = .white

    var body: some View {
        VStack {
            if background != "" {
                Image(background)
                    .resizable()
                    .frame(width: 380, height: 400)
                    .cornerRadius(20)

            } else {
                RoundedRectangle(cornerRadius: 20)
                    .frame(width: 400, height: 400)
                    .foregroundColor(backgroundColor)
            }

            VStack {
                HStack {
                    VStack(alignment: .leading) {
                        if cardCategory != "" {
                            Text(verbatim: cardCategory.uppercased())
                                .font(.headline)
                                .fontWeight(.heavy)
                                .opacity(0.3)
                                .foregroundColor(titleColor)
                            //.opacity(1)
                        }

                        HStack {
                            Text(verbatim: cardTitle)
                                .font(.largeTitle)
                                .fontWeight(.heavy)
                                .lineLimit(3)
                                .foregroundColor(titleColor)
                        }

                    }

                    Spacer()
                }.offset(y: -390)
                    .padding(.bottom, -390)

                HStack {
                    if cardSubtitle != "" {
                        Text(verbatim: cardSubtitle)
                            .font(.system(size: 17))
                            .foregroundColor(titleColor)
                    }
                    Spacer()
                }
                .offset(y: -50)
                    .padding(.bottom, -50)
            }
            .padding(.leading)


        }.padding(.leading).padding(.trailing)
    }

}
Run Code Online (Sandbox Code Playgroud)

所以

Card(cardSubtitle: "Welcome to this library I made :p", cardCategory: "CONNECT", background: "flBackground", titleColor: .white)
Run Code Online (Sandbox Code Playgroud)

显示: 卡1

Pal*_*lle 9

SwiftUI 现在不进行自定义模态转换,因此我们必须使用一种解决方法。

我能想到的一种方法是使用ZStack. 可以使用GeometryReader. 然后,可以使用框架和位置修改器控制目标形状。

一开始,目标将被设置为与源的位置和大小完全匹配。然后紧接着,目的地将在动画块中设置为全屏大小。

struct ContentView: View {
    @State var isPresenting = false
    @State var isFullscreen = false
    @State var sourceRect: CGRect? = nil

    var body: some View {
        ZStack {
            GeometryReader { proxy in
                Button(action: {
                    self.isFullscreen = false
                    self.isPresenting = true
                    self.sourceRect = proxy.frame(in: .global)
                }) { ... }
            }

            if isPresenting {
                GeometryReader { proxy in
                    ModalView()
                    .frame(
                        width: self.isFullscreen ? nil : self.sourceRect?.width ?? nil, 
                        height: self.isFullscreen ? nil : self.sourceRect?.height ?? nil)
                    .position(
                        self.isFullscreen ? proxy.frame(in: .global).center : 
                            self.sourceRect?.center ?? proxy.frame(in: .global).center)
                    .onAppear {
                        withAnimation {
                            self.isFullscreen = true
                        }
                    }
                }
            }
        }
        .edgesIgnoringSafeArea(.all)
    }
}

extension CGRect {
    var center : CGPoint {
        return CGPoint(x:self.midX, y:self.midY)
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 它给了我错误是你能说的最没有帮助的事情来帮助我理解你的确切问题。另外,我为这个答案想出了这个代码,所以这就是我所拥有的一切。 (2认同)

Pal*_*lle 7

iOS/tvOS 14 和 macOS 11 中的 SwiftUI 必须matchedGeometryEffect(id:in:properties:anchor:isSource:)为不同层次结构之间的视图转换设置动画。

官方文档链接

这是一个最小的例子:

struct SomeView: View {
    @State var isPresented = false
    @Namespace var namespace
 
    var body: some View {
        VStack {
            Button(action: {
                withAnimation {
                    self.isPresented.toggle()
                }
            }) {
                Text("Toggle")
            }

            SomeSourceContainer {
                MatchedView()
                .matchedGeometryEffect(id: "UniqueViewID", in: namespace, properties: .frame, isSource: !isPresented)
            }

            if isPresented {
                SomeTargetContainer {
                    MatchedTargetView()
                    .matchedGeometryEffect(id: "UniqueViewID", in: namespace, properties: .frame, isSource: isPresented)
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)