SwiftUI 中的自定义模态转换

Posted

技术标签:

【中文标题】SwiftUI 中的自定义模态转换【英文标题】:Custom modal transitions in SwiftUI 【发布时间】:2019-07-12 10:35:04 【问题描述】:

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

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

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

Gif 1 Gif 2

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

最相似的东西是 NavigationLink,但是这迫使我添加 NavigationView,并且卡片像另一个页面一样显示。

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

提前致谢。

我的代码:

Card.swift

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)
    


所以

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

显示:

【问题讨论】:

我认为您可能对 SwiftUI 要求过高。我并不是说这是不可能的,但由于它本身不受支持,因此您最终会诉诸许多“丑陋”的技巧。模态目前非常非常有限。 我怀疑你给的填充。 您是否在 Swift UI 中创建了 App Store 请帮助我或分享源代码? 你找到解决办法了吗? 【参考方案1】:

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)
    

【讨论】:

谢谢!这正是我所需要的,但是当我尝试运行它时,它给了我以下错误:detected '=' on bool, use '==',这可以简单地解决,但是它给了我错误 2:Result values in '? :' expression have mismatching types 'Swift.Optional<_>' and 'Swift.Optional<_>' 看来我漏掉了一个括号。现在应该修好了。 @Palle,对不起我的无知。我真的只是从 SwiftUI(或一般的 Swift)开始,无法理解 ModalView() 指的是什么。您介意说明这与CardView 有何关系吗?另外,我假设 ... 的内容也是CardView 的一个实例? ModalView 在这种情况下只是一个占位符,用于显示您要在另一个视图之上显示的视图。 ... 是原始视图中按钮的内容,所以是的,它可以包含卡片视图。 它给我的错误是您可以说的帮助我理解您的确切问题的最没有帮助的事情。另外,我想出了这个答案的代码,所以这就是我所拥有的一切。【参考方案2】:

iOS/tvOS 14 和 macOS 11 中的 SwiftUI 具有 matchedGeometryEffect(id:in:properties:anchor:isSource:) 以动画化不同层次结构之间的视图转换。

Link to Official Documentation

这是一个最小的例子:

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)
                
            
        
    

【讨论】:

谢谢!我只是要发布一个答案说这件事哈哈,最后 SwiftUI 2 有这样的事情,我们不必诉诸丑陋的解决方法。 我真的很想看到这个 API 的 iOS 13 的反向移植......应该是可能的。 谢谢!你能为这里的菜鸟扩展一下你的例子吗? HackingWithSwift 上有一篇不错的博客文章:hackingwithswift.com/quick-start/swiftui/…

以上是关于SwiftUI 中的自定义模态转换的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 列表中的自定义按钮

SwiftUI:导航链接中的自定义标签显示为灰色

样式视图传递给 SwiftUI 中的自定义视图

自定义模态转换关闭动画运行不同步

将图像名称传递给 SwiftUI 中的自定义图像子类

SwiftUI:自定义模态动画