gpt4 book ai didi

SwiftUI withAnimation 导致意外行为

转载 作者:行者123 更新时间:2023-12-05 00:55:32 28 4
gpt4 key购买 nike

我注意到使用 swiftUI 的 withAnimation{} 的 XCode 有一个奇怪的行为。

我创建了以下工作示例:

import SwiftUI

class Block: Hashable, Identifiable {
// Note: this needs to be a class rather than a struct

var id = UUID()

static func == (lhs: Block, rhs: Block) -> Bool {
lhs.id == rhs.id
}

func hash(into hasher: inout Hasher) {
hasher.combine(self.id)
}
}

struct ContentView: View {
@State private var blocks: [Block]

init() {
var blocks = [Block]()

/// generate some blocks here
blocks.append(Block())
blocks.append(Block())
blocks.append(Block())

self._blocks = State(initialValue: blocks)
}

var body: some View {
VStack {
ForEach(blocks, id: \.self) { block in
BlockRow(block: block) { b in
print("COMMENT THIS LINE OUT") // try commenting out this line -> XCODE won't build anymore

withAnimation {
self.blocks.remove(at: self.blocks.firstIndex(of: b)!)
}
}
}
}
}
}

struct BlockRow: View {

@State var block: Block
var onDelete: (Block) -> Void = {_ in}

var body: some View {
Text("<Block>")
.onTapGesture {
print("Tap block \(block.id)")
self.onDelete(block)
}
}
}

上面的例子按预期工作,如果你点击它被删除的 block 之一,下面的 block 将很好地滑动到空闲位置。

但是XCode在这里产生了一个警告,我不明白:

Result of call to 'withAnimation' is unused

事情变得更加困惑:只需在 withAnimation block 之前注释掉 print stamenet /* print("COMMENT THIS LINE OUT") */ XCode 将不再构建项目:

Failed to produce diagnostic for expression; please file a bug report

这是一个错误还是我的方法完全偏离了道路?

最佳答案

问题的根源在于 self.blocks.remove(at:)返回一个您没有处理的值。如果您明确忽略该值,那么无论 print 与否,您的代码都会按预期工作。声明在那里。

withAnimation {
_ = self.blocks.remove(at: self.blocks.firstIndex(of: b)!)
}

Swift 有一个特性,即带有一行代码的闭包会返回该语句的值作为闭包的返回值。所以在这种情况下,如果不忽略 remove(at:) 的返回值它返回一个 BlockBlock然后成为 withAnimation 的返回类型关闭。

withAnimation是这样定义的:

func withAnimation<Result>(_ animation: Animation? = .default, _ body: () throws -> Result) rethrows -> Result

这是一个通用函数,它接受一个闭包。该闭包的返回类型决定了通用占位符的类型,而后者又决定了 withAnimation 的返回类型。自己。

所以,如果你不忽略 remove(at:) 的返回类型, withAnimation<Block>函数将返回 Block .

如果忽略 remove(at:) 的返回值,则该语句成为没有返回的语句(即返回 ()Void )。因此,withAnimation函数变为 withAnimation<Void>它返回 Void .

现在,因为 BlockRow 的关闭删除print语句时只有一行代码,它的返回值是单条语句的返回值,现在是Void :

BlockRow(block: block) { b in
withAnimation {
_ = self.blocks.remove(at: self.blocks.firstIndex(of: b)!)
}
}

这与闭包的类型匹配 BlockRow期待它的onDelete关闭。

在您的原始代码中,print语句导致关闭 BlockRow有 2 行代码,从而避免了 Swift 使用单行代码来确定闭包的返回类型的特性。您确实收到了警告,表明您没有使用 BlockwithAnimation<Block> 返回的功能。 @Asperi 的回答通过分配 Block 修复了该警告。由 withAnimation<Block> 返回至_ .这与我建议的解决方案具有相同的效果,但它处理的是更高一级的问题,而不是问题的根源。


为什么编译器不提示你忽略了remove(at:)的返回值? ?

remove(at:)被明确设计为允许您丢弃返回的结果,这就是为什么您不会收到警告它返回的值您未处理的原因。

@discardableResult mutating func remove(at i: Self.Index) -> Self.Element

但是如您所见,这会导致您遇到令人困惑的结果。您使用的是 remove(at:)好像它返回了 Void ,但它实际上是返回 Block已从您的阵列中删除。然后,这会导致导致您的问题的整个事件链。

关于SwiftUI withAnimation 导致意外行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64192786/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com