【问题标题】:Alternatives to GCD to run code with a delay under SwiftUI在 SwiftUI 下延迟运行代码的 GCD 替代方案
【发布时间】:2020-03-25 08:25:24
【问题描述】:

Swift 5、iOS 13

我正在运行这段代码,它可以工作。

var body: some View {
...
Button(action: {
  self.animateTLeft() 
  quest = quest + "1"
}) { Wedge(startAngle: .init(degrees: 180), endAngle: .init(degrees: 270)) 
         .fill(Color.red) 
         .frame(width: 200, height: 200)
         .offset(x: 95, y: 95)
         .scaleEffect(self.tLeft ? 1.1 : 1.0)
}.onReceive(rPublisher) { _ in
  self.animateTLeft() 
}
...
}

private func animateTLeft() {
withAnimation(.linear(duration: 0.25)){
  self.tLeft.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: {
  withAnimation(.linear(duration: 0.25)){
    self.tLeft.toggle()
  }
})
}

如果可能的话,我想尝试一些使用 GCD 的替代方法。所以我尝试使用没有编译的计时器。我尝试使用 perform,它也没有编译。于是我尝试了编译操作里面! :)。但可悲的是,它只能工作一次。有没有 GCD 的替代品。

private func animateTLeft() {

//perform(#selector(animate), with: nil, afterDelay: 0.25)
//Timer.scheduledTimer(timeInterval: 0.15, target: self, selector: #selector(animateRed), userInfo: nil, repeats: false)

let queue = OperationQueue()
let operation1 = BlockOperation(block: {
  withAnimation(.linear(duration: 1)){
    self.tLeft.toggle()
  }
})
let operation2 = BlockOperation(block: {
  withAnimation(.linear(duration: 1)){
    self.tLeft.toggle()
  }
})
operation2.addDependency(operation1)
queue.addOperations([operation1,operation2], waitUntilFinished: true)

}

我为什么要这样做,因为我有四个切片要制作动画,但我需要更少的代码。我有红色、绿色、黄色和蓝色切片。我编写了一个通用例程来为它们设置动画,提供颜色作为参数。这是我的代码。

private func animateSlice(slice: inout Bool) {
withAnimation(.linear(duration: 0.25)){
  slice.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: {
  withAnimation(.linear(duration: 0.25)){
    slice.toggle()
  }
})
}

但这不会与 inout 参数一起编译,并给出红色错误消息“Escaping Closure captures 'inout' parameter 'slice'”,这不好。我可以将其复制,而不是 inout 参数。编译,但当然不起作用,因为它没有改变愚蠢的值。

也试过这个,但它不会编译。也许有人可以让它工作。

private func animateSliceX(slice: String) {
var ptr = UnsafeMutablePointer<Bool>.allocate(capacity: 1)
switch slice {
case "red": ptr = &tLeft
case "green": ptr = &tRight
case "yellow": ptr = &bLeft
default: ptr = &bRight
}
withAnimation(.linear(duration: 0.25)){
  ptr.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: {
  withAnimation(.linear(duration: 0.25)){
    ptr.toggle()
  }
})
}

谢谢...

【问题讨论】:

标签: swift swiftui grand-central-dispatch nsoperationqueue nsoperation


【解决方案1】:

如果我要使用 GCD,我可能会使用 Combine 方法,例如:

struct ContentView: View {
    var body: some View {
        Button(action: {
            DispatchQueue.main.schedule(after: .init(.now() + 1)) {
                print("bar")
            }
        }) {
            Text("foo")
        }
    }
}

如果不想使用 GCD,可以使用Timer

struct ContentView: View {
    var body: some View {
        Button(action: {
            Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
                print("bar")
            }
        }) {
            Text("foo")
        }
    }
}

或者正如用户3441734所说,你也可以在RunLoop上使用schedule

struct ContentView: View {
    var body: some View {
        Button(action: {
            RunLoop.main.schedule(after: .init(Date() + 1)) {
                print("bar")
            }
        }) {
            Text("foo")
        }
    }
}

【讨论】:

    【解决方案2】:

    我认为没有其他方法(至少更容易使用该 GCD),但您可以使用以下可重用函数。

    假设切片是一个状态或一个值类型为 Bool 的绑定,你可以这样做:

    private func animateSlice(slice: Binding<Bool>) {
        DispatchQueue.main.asyncAfter(wallDeadline: .now() + 0.25) {
            withAnimation(Animation.linear(duration: 0.25)) {
                slice.wrappedValue.toggle()
            }
        }
    }
    

    使用上面的函数编译是因为 Swift 不会考虑将 Binding 包装的值更改为突变。因此,您可以将其传递给您的 asyncAfter 块。

    要使用该功能,您需要一个绑定:

    self.animateSlice(slice: $self.tLeft)
    

    【讨论】:

    • 我确实提出了另一个版本 [有效],但它是 12 行代码,而我在 8 行中做了。这意味着你赢了 :) 出色的编码.. 将发布我的代码在 medium.com 上更好的编程。将电子邮件添加到您的个人资料 Filipe,以便我给您一些功劳。
    【解决方案3】:

    好的,所以我接受了另一个答案,因为我认为它比这个更好,但我想我还是会发布这个,因为它是同一问题的另一种解决方案。

    private func animate(slice: String) {
        animateAction(slice: slice)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.25, execute: {
          self.animateAction(slice: slice)
        })
      }
    
     private func animateAction(slice: String) {
        withAnimation(.linear(duration: 0.25)){
          switch slice {
            case "red": self.tLeft.toggle()
            case "green": self.tRight.toggle()
            case "yellow": self.bLeft.toggle()
        // blue is the only other slice
            default: self.bRight.toggle()
          }
        }
      }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多