在swiftUI中的一系列视图之间切换

时间:2020-05-13 10:10:24

标签: ios swift view swiftui

Swift 5.x iOS 13.x

我创建了一个骰子视图数组,我想在它们之间切换。为了简短起见,我仅在此处显示两个视图。我创建了一个视图数组,但是我还不太清楚如何在SwiftUI中解决它?或视图实际上是什么样的对象,因为它不是视图,也不是某种视图。

import SwiftUI
import GameplayKit

let dotSize:CGFloat = 24
let diceSize:CGFloat = 128
let fieldSide:CGFloat = 32

let d6 = GKRandomDistribution.d2()

var diceViews:[Any] = [oneDotDice(),twoDotDice()]

struct ContentView: View {
  @State var showInt:Int = 1
  var body: some View {

VStack {
  if showInt == 1 {
    oneDotDice()
      .transition(AnyTransition.opacity)
      .animation(.default)
      .modifier(changeDice(showInt: self.$showInt))
  }
  if showInt == 2 {
    twoDotDice()
      .transition(AnyTransition.opacity)
      .animation(.default)
      .modifier(changeDice(showInt: self.$showInt))
  }
 }
}

struct oneDotDice: View {
  var body: some View {
  Rectangle()
    .fill(Color.clear)
    .frame(width: diceSize, height: diceSize, alignment: .center)
    .border(Color.black)
   .background(Text(1))
  }
}

struct twoDotDice: View {
  var body: some View {
  Rectangle()
    .fill(Color.clear)
    .frame(width: diceSize, height: diceSize, alignment: .center)
    .border(Color.black)
   .background(Text(2))
  }
}

struct changeDice: ViewModifier {
  @Binding var showInt:Int
  func body(content: Content) -> some View {
    content
      .onTapGesture {
        self.showInt = d6.nextInt()
        print("Int ",self.showInt)
      }
    }
  }

如何在主循环中使用我的视图数组使此代码更好?我什至不能在SwiftUI代码中使用switch语句。我需要使用六个嵌套的if / else语句...

2 个答案:

答案 0 :(得分:4)

视图只是一个结构。您可以将其存储在数组或其他属性类型中,然后在其他视图中使用。如果您确实想在一个集合中混合使用不同类型的View,那么AnyView是一个很好的解决方案。 为了动态显示该集合,我喜欢使用ForeEach。您可以不迭代确切的View,而可以迭代数组的索引。因此,SwiftUI将根据其视图的数组索引为其创建键。但这是有代价的。您不能动态更改该数组的内容。在所有父级View生命周期中,它必须恒定。 坦白地说,您实际上可以通过ForeEach迭代确切的View,但是需要使它们可识别(它们必须具有public var id = UUID()属性)。

import SwiftUI

let dotSize:CGFloat = 24
let diceSize:CGFloat = 128
let fieldSide:CGFloat = 32


struct ContentView: View {
  var diceViews:[AnyView] = [AnyView(oneDotDice()), AnyView(twoDotDice())]
  @State var showInt: Int = 1
  var body: some View {
    ZStack{
        ForEach(diceViews.indices){ind in
            ZStack{
                if ind == self.showInt{
                    self.diceViews[ind]
                        .transition(.scale(scale: 0, anchor: .center))
                    .onTapGesture {
                        let newShow = Int.random(in: 0...1)
                        print("new show id \(newShow)")
                          withAnimation(.linear(duration: 0.3)){
                            self.showInt = newShow
                          }
                    }
                }
            }

        }
    }
  }
}
struct oneDotDice: View {
  var text: String = "1"
  var body: some View {
  Rectangle()
    .fill(Color.clear)
    .frame(width: diceSize, height: diceSize)
    .border(Color.black)
   .background(Text(text))
  }
}

struct twoDotDice: View {
  var text: String = "2"
  var body: some View {
  Rectangle()
    .fill(Color.clear)
    .frame(width: diceSize, height: diceSize)
    .border(Color.black)
   .background(Text(text))
  }
}


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

顺便说一句,我改变了您的随机逻辑,不确定它是如何工作的,但是我认为它对您的问题并不是很重要。

答案 1 :(得分:1)

我建议您删除oneDotDicetwoDotDice,以拥有一个可以代表所有可能值的DiceFace。另外,不需要自定义修饰符,不需要View数组(顺便说一句,如果您想要类似的内容,可以使用AnyViewForEach)。

您可以将以下示例复制粘贴到Playground中进行检查:

import SwiftUI
import GameplayKit
import PlaygroundSupport

let dotSize: CGFloat = 24
let diceSize: CGFloat = 128
let fieldSide: CGFloat = 32

let d6 = GKRandomDistribution(forDieWithSideCount: 6)

struct ContentView: View {
    @State var currentValue: Int = 1

    var body: some View {
        DiceFace(dotCount: currentValue)
            .transition(AnyTransition.opacity)
            .animation(.default)
            .onTapGesture(perform: self.roll)
    }

    func roll() {
        self.currentValue = d6.nextInt()
        print("Int: \(self.currentValue)")
    }

    // Your `oneDotDice` and `twoDotDice` can be represented with this View, passing simply a different `dotCount`
    // Also, in Swift types go with an uppercase, and variables with a lowercase.
    struct DiceFace: View {
        let dotCount: Int

        var body: some View {
            Rectangle()
                .fill(Color.clear)
                .frame(width: diceSize, height: diceSize, alignment: .center)
                .border(Color.black)
                .background(Text(String(dotCount)))
        }
    }
}

PlaygroundPage.current.setLiveView(ContentView())
PlaygroundPage.current.needsIndefiniteExecution = true