条件子视图的 SwiftUI 转换

Posted

技术标签:

【中文标题】条件子视图的 SwiftUI 转换【英文标题】:SwiftUI Transitions of Conditional Child Views 【发布时间】:2020-02-14 17:24:32 【问题描述】:

我正在尝试理解和试验 swiftUI 视图转换,并且可以使用一些见解来了解为什么以下内容无法按我预期的方式工作:

struct ContentView: View 
@State private var showOverlay: Bool = false

var body: some View 
    ZStack 
        VStack 
            Button(action: 
                withAnimation(.easeInOut) 
                    self.showOverlay = true
                
            ) 
                Text("Hello, World!")
            
        
        .zIndex(0)
        .animation(nil)
        .blur(radius: showOverlay ? 3 : 0)

        if showOverlay 
            HStack 
                Button(action: 
                    withAnimation(.easeInOut) 
                        self.showOverlay = false
                    
                ) 
                    Text("Close")
                
                .frame(width: 80, height: 80)
                .background(Color.red)
                .transition(.move(edge: .top))

                Button(action: 
                    withAnimation(.easeInOut) 
                        self.showOverlay = false
                    
                ) 
                    Text("Close")
                
                .frame(width: 80, height: 80)
                .background(Color.red)
                .transition(.move(edge: .bottom))
            
            .zIndex(1.0)
        
    

有两个按钮的条件 HStack。 当@State 参数showOverlay 更改时,它在withAnimation 块内完成。 单个按钮上的转换不受支持。

如果我删除 HStack,并将 zIndex(1) 放在每个按钮上,则会发生删除转换,但不会发生插入转换。

我希望两个按钮在它们的组添加到视图时以指定的方式转换。

问题: 为什么包装在 HStack 中会导致内部转换被忽略? 为什么边缘过渡只在 1 个方向起作用?

【问题讨论】:

【参考方案1】:

过渡适用于出现/消失的视图。在提供的代码快照中出现/消失了HStack,因此应该对其应用过渡。

注意:转场必须在模拟器或真机上进行测试,其中大部分在预览版中不起作用。

下面是代码的修改部分,它给出了有效的行为。使用 Xcode 11.2 / ios 13.2 测试

if showOverlay 
    HStack 
        Button(action: 
            withAnimation(.easeInOut) 
                self.showOverlay = false
            
        ) 
            Text("Close")
        
        .frame(width: 80, height: 80)
        .background(Color.red)

        Button(action: 
            withAnimation(.easeInOut) 
                self.showOverlay = false
            
        ) 
            Text("Close")
        
        .frame(width: 80, height: 80)
        .background(Color.red)
    
    .transition(.move(edge: .top)) // << only in this place needed
    .zIndex(1.0)

【讨论】:

我明白了。所以在这种情况下,我不能对每个按钮应用单独的唯一转换?在我的示例中,一个按钮从顶部开始动画,另一个从底部开始。 (我的实际使用比发布的示例更复杂)【参考方案2】:

为了得到两个不同的动画,你可以把条件逻辑拉到离按钮更近的地方,即HStack里面:

HStack 
  if showOverlay 
    Button(action: 
      withAnimation(.easeInOut) 
        self.showOverlay = false
      
    ) 
      Text("Close")
    
    .frame(width: 80, height: 80)
    .background(Color.red)
    .transition(.move(edge: .top))

    Button(action: 
      withAnimation(.easeInOut) 
        self.showOverlay = false
      
    ) 
      Text("Close")
    
    .frame(width: 80, height: 80)
    .background(Color.red)
    .transition(.move(edge: .bottom))
  

.zIndex(1.0)

showOverlay == false 时,这会给您留下一个空的HStack,但这应该不是什么大问题,至少在这个例子中是这样。

【讨论】:

以上是关于条件子视图的 SwiftUI 转换的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI:如何更改子视图?

如何全屏显示 SwiftUI 子视图?

SwiftUI 将数据传递给子视图

在 SwiftUI 中拥抱子视图

SwiftUI 强制子视图重绘

SwiftUI - 更新父视图的状态时如何保留子视图的状态?