如何让SwiftUI可复用列表支持Text兼容的OrderedSet元素类型
如何让SwiftUI组件仅接受可用于Text视图的OrderedSet元素类型
问题描述
我需要创建一个可复用SwiftUI组件,用于展示OrderedSet集合,同时支持删除、移动和添加操作。要求传入的OrderedSet元素类型可以直接用于Text的字符串插值,比如Text("hello \(字符串、整数或浮点数这类泛型类型) world")。
原本尝试用ExpressibleByStringInterpolation协议约束元素类型,但发现它仅用于创建可在SwiftUI Text视图中使用的自定义类型,无法满足需求。当前代码中Text("\(item)")报错:No exact matches in call to instance method 'appendInterpolation'。
如何调整才能让OrderedSet只接受可用于Text视图的元素类型?以下是简化示例代码:
import SwiftUI import OrderedCollections struct ReusableListView<Element: Hashable>: View { @Binding var set: OrderedSet<Element> @State private var multiSelection: Set<Element> = [] init(_ set: Binding<OrderedSet<Element>>) { self._set = set } var body: some View { Section { List(selection: $multiSelection) { ForEach(set, id: \.self) { item in Text("\(item)") } .onDelete { indexSet in set.elements.remove(atOffsets: indexSet) } } } } } @main struct MyApp: App { @State private var setWithStrings: OrderedSet<String> = ["hello", "world"] @State private var setWithInts: OrderedSet<Int> = [1, 2, 3] var body: some Scene { WindowGroup { ReusableListView($setWithStrings) ReusableListView($setWithInts) } } }
解决方案
自定义一个TextSupport协议,要求遵循该协议的类型提供一个可直接用于Text的字符串属性,然后让需要支持的基础类型(如String、Int)扩展遵循该协议,最后在组件的泛型约束中添加该协议即可。完整可运行代码如下:
import SwiftUI import OrderedCollections protocol TextSupport { var text: String { get } } extension String: TextSupport { var text: String { self } } extension Int: TextSupport { var text: String { self.formatted() } } struct ReusableListView<Element: Hashable & TextSupport>: View { @Binding var set: OrderedSet<Element> @State private var multiSelection: Set<Element> = [] init(_ set: Binding<OrderedSet<Element>>) { self._set = set } var body: some View { Section { List(selection: $multiSelection) { ForEach(set, id: \.self) { item in Text(item.text) } .onDelete { indexSet in set.elements.remove(atOffsets: indexSet) } } } } } @main struct MyApp: App { @State private var setWithStrings: OrderedSet<String> = ["hello", "world"] @State private var setWithInts: OrderedSet<Int> = [1, 2, 3] var body: some Scene { WindowGroup { ReusableListView($setWithStrings) ReusableListView($setWithInts) } } }
内容的提问来源于stack exchange,提问作者thiezn
相关产品推荐
相关产品推荐

