运行 代码的 GCD 替代品,在 SwiftUI 下有延迟
Alternatives to GCD to run code with a delay under SwiftUI
Swift5,iOS13
我是运行这段代码,有效。
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()
}
})
}
谢谢...
我认为没有其他方法(至少更容易使用 GCD),但您可以使用以下可重用函数。
假设 slice 是一个 State 或一个 Bool 值类型的 Binding 你可以这样做:
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)
好的,所以我接受了另一个答案,因为我认为它比这个更好,但我想我还是会post这个,让它成为同一问题的另一种解决方案。
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()
}
}
}
如果我要使用 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")
}
}
}
或者如 user3441734 所说,您也可以在 RunLoop
上使用 schedule
:
struct ContentView: View {
var body: some View {
Button(action: {
RunLoop.main.schedule(after: .init(Date() + 1)) {
print("bar")
}
}) {
Text("foo")
}
}
}
Swift5,iOS13
我是运行这段代码,有效。
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()
}
})
}
谢谢...
我认为没有其他方法(至少更容易使用 GCD),但您可以使用以下可重用函数。
假设 slice 是一个 State 或一个 Bool 值类型的 Binding 你可以这样做:
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)
好的,所以我接受了另一个答案,因为我认为它比这个更好,但我想我还是会post这个,让它成为同一问题的另一种解决方案。
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()
}
}
}
如果我要使用 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")
}
}
}
或者如 user3441734 所说,您也可以在 RunLoop
上使用 schedule
:
struct ContentView: View {
var body: some View {
Button(action: {
RunLoop.main.schedule(after: .init(Date() + 1)) {
print("bar")
}
}) {
Text("foo")
}
}
}