在 swiftUI 中是不是可以使用按钮在屏幕上添加新视图?

Posted

技术标签:

【中文标题】在 swiftUI 中是不是可以使用按钮在屏幕上添加新视图?【英文标题】:In swiftUI is it possible to add a new view onto the screen with a button?在 swiftUI 中是否可以使用按钮在屏幕上添加新视图? 【发布时间】:2020-05-19 16:12:13 【问题描述】:

每次点击按钮时,我都希望出现一个新的 cardView。我想知道这在 swiftUI 中是否可行,如果可以,我需要采取什么行动。如果我能够将一些参数传递给 cardView 结构会更好,但任何帮助都会很棒!


struct ContentView: View 
    var body: some View 
        ZStack 
            VStack 
                TextButton(action: print("Button tapped"), text: "new card")
                CardView()
            
        
    


struct CardView: View 
    var body: some View 
        ZStack 
            Rectangle()
                .fill(Color(#colorLiteral(red: 0.7450980544, green: 0.1568627506, blue: 0.07450980693, alpha: 1)))
                .frame(width: 100, height: 100 * 1.618)
                .cornerRadius(16)
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.1), radius: 1, x: 0, y: 1)
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.2), radius: 10, x: 0, y: 10)

            VStack 
                Text("Card")
                    .font(.system(size: 10) )
                    .foregroundColor(.white)
                    .bold()
            
        
    



struct TextButton: View 
    let action: () -> Void
    let text: String

    var body: some View 
        Button(action: action, label: 
            Text(text)
                .padding(.horizontal, 16)
                .padding(.vertical, 16)
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(.infinity)
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.1), radius: 1, x: 0, y: 1)
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.2), radius: 10, x: 0, y: 10)
        )
    

struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    
 ```

【问题讨论】:

【参考方案1】:

在 SwiftUI 中,View 反映的是 State 数据,所以你不直接修改 View,而是修改 State 数据,基于 State 构建 View。这是 SwiftUI 背后的核心原则,它允许您将 View 关注点与驱动它的数据(在某些术语中为 ViewModel)分开。

所以,假设我们有一个Card 的数据模型(使其符合Identifiable - 这将在以后需要):

struct Card: Identifiable 
  let id = UUID()
  let name: String

让我们在你的视图中定义一个卡片数组作为状态变量:

@State private var cards: [Card] = [Card(name: "foo")]

然后主体可以使用ForEachList 视图显示这些卡片:

var body = some View 
  VStack() 
    Button("Add Card") 
       self.cards.append(Card(name: "I'm an added card"))
    
    ForEach(cards)  card in
       CardView(for: card) // if your CardView had parameters
    
  

发生的情况是按钮的闭包向cards 状态变量添加了一个新的Card 实例。就是这样。它不会直接更改视图中的任何内容。 View 看到变化(这就是 SwiftUI 在幕后所做的)并重新渲染自己。

您需要Card 以符合Identifiable 的原因是让ForEach 知道如何唯一标识每张卡。在不符合可识别的情况下,您可以使用这样的关键路径:

ForEach(cards, id: \.self)  card in
  // ...

【讨论】:

非常感谢!您提供了非常清晰和连贯的解释。惊人的东西。问题解决了。【参考方案2】:

看看这个:

import SwiftUI

struct ContentView: View 

    @State var cards: [CardView] = [CardView()]



    var body: some View 
        VStack 
            TextButton(action: 
                print("Button tapped")
                self.cards.append(CardView())

            , text: "new card")
            ZStack 
                ForEach(cards, id: \.self)  card in
                    card
                        .rotationEffect(card.angle)

                

            
        
    


struct CardView: View, Hashable, Identifiable 

    var id = UUID()

    var angle : Angle 
        let angle = Angle(degrees: Double.random(in: 0...30))
        print(angle.degrees)
        return angle
    
    var body: some View 
        ZStack 
            Rectangle()
                .fill(Color(#colorLiteral(red: 0.7450980544, green: 0.1568627506, blue: 0.07450980693, alpha: 1)))
                .frame(width: 100, height: 100 * 1.618)
                .cornerRadius(16)
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.1), radius: 1, x: 0, y: 1)
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.2), radius: 10, x: 0, y: 10)

            VStack 
                Text("Card \(angle.degrees)")
                    .font(.system(size: 10) )
                    .foregroundColor(.white)
                    .bold()
            
        
    



struct TextButton: View 
    let action: () -> Void
    let text: String

    var body: some View 
        Button(action: action, label: 
            Text(text)
                .padding(.horizontal, 16)
                .padding(.vertical, 16)
                .foregroundColor(.white)
                .background(Color.blue)
                .cornerRadius(.infinity)
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.1), radius: 1, x: 0, y: 1)
                .shadow(color: Color(#colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)).opacity(0.2), radius: 10, x: 0, y: 10)
        )
    

struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

【讨论】:

以上是关于在 swiftUI 中是不是可以使用按钮在屏幕上添加新视图?的主要内容,如果未能解决你的问题,请参考以下文章

在按钮单击 SwiftUI 时导航到新屏幕

SwiftUI+Firestore 中的多选列表

SWIFTUI 按钮或 NavigationLink?

将 SwiftUI 视图渲染到屏幕外并将视图另存为 UIImage 以共享

使用 swiftUI 中的按钮导航到新屏幕

在 SwiftUI 中,如何增加按钮的高度?