我很难实现一个自定义视图,它可以将绑定作为参数并实现该值的双向更新。
因此,基本上,我正在实现我的自定义滑块,并希望它的初始化器是这样的:
MySlider(value: <Binding<Float>)
我在挣扎的是:
以下是我目前的实现,这并不完美。
struct MySlider: View {
@Binding var selection: Float?
@State private var selectedValue: Float?
init(selection: Binding<Float?>) {
self._selection = selection
// https://stackoverflow.com/a/58137096
_selectedValue = State(wrappedValue: selection.wrappedValue)
}
var body: some View {
HStack(spacing: 3) {
ForEach(someValues) { (v) in
Item(value: v,
isSelected: v == self.selection)
.onTapGesture {
// No idea how to do that other way so I don't have to set it twice
self.selection = v
self.selectedValue = v
}
}
}
}
}编辑1:我想我的问题是底层模型对象来自Core,而不属于任何能够观察其变化的SwiftUI视图。--模型对象属于UIKit ViewController,而我只传递一个绑定到SwiftUI视图,这是不够的。我现在的解决方案是将模型对象也传递给SwiftUI视图,以便它可以将其标记为@ObservedObject。
struct MySlider<T>: View where T: ObservableObject {
@ObservedObject var object: T
@Binding var selection: Float?
var body: some View {
return ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 3) {
ForEach(values) { (v) in
Item(value: v,
isSelected: v == self.selection)
.onTapGesture {
self.selection = v
}
}
}
}
}
}发布于 2020-02-18 18:56:22
@Binding的定义本质上是与底层数据项的双向连接,这是一个属于另一个视图的@State变量。如苹果所述
使用绑定在视图和其底层模型之间创建双向连接。
如果是绑定,如果值发生变化,SwiftUI将自动更新视图;因此(回答第一个问题),您不需要执行任何类型的订阅来更新自定义视图--这将自动发生。
类似地(关于第二个问题),因为绑定是来自另一个视图的状态,所以您不应该将它声明为视图的状态,我也不认为它是可能的。对于您的视图来说,状态应该是一个纯粹的内部值(Apple强烈建议所有@State属性都声明为private)。
这一切都与苹果在发布SwiftUI时强调的“真理的单一来源”概念有关:绑定已经是@State的父视图是拥有这些信息的东西,所以你的视图也不应该声明为一个状态。
对于您的代码,我认为您需要做的就是取出第二个状态属性,因为它不是必需的。只需确保传递的绑定是父视图中任何自定义视图中的@State属性,并使用$语法传递它以创建该绑定。如果需要的话,这篇文章会更详细地介绍这个想法。
struct MySlider: View {
@Binding var selection: Float?
init(selection: Binding<Float?>) {
self._selection = selection
}
var body: some View {
HStack(spacing: 3) {
ForEach(someValues) { (v) in
Item(value: v, isSelected: v == self.selection)
.onTapGesture {
self.selection = v
}
}
}
}
}发布于 2020-08-24 09:53:14
我的回答可能有点晚,但我偶然发现了一个类似的问题。我就是这样解决这个问题的。
struct Item : Identifiable {
var id : Int
var isSelected : Bool
}
class MySliderObserver : ObservableObject {
@Published var values = [Item]()
init() {
values.append(Item(id: 0, isSelected: false))
values.append(Item(id: 1, isSelected: false))
values.append(Item(id: 2, isSelected: false))
}
func toggleSelectForItem(id:Int) -> Void {
guard let index = values.firstIndex(where: { $0.id == id }) else {
return
}
var item = values[index]
item.isSelected.toggle()
values[index] = item
}
}
struct MySlider: View {
@EnvironmentObject var observer : MySliderObserver
var body: some View {
return ScrollView(.horizontal, showsIndicators: false) {
VStack(spacing: 3) {
ForEach(self.observer.values) { v in
Text("Selected: \(v.isSelected ? "true" : "false")").onTapGesture {
self.observer.toggleSelectForItem(id: v.id)
}
}
}
}
}
}在SceneDelegate中,您需要设置如下所示的EnvironmentObject:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let contentView = MySlider()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(MySliderObserver()))
self.window = window
window.makeKeyAndVisible()
}
}https://stackoverflow.com/questions/60285177
复制相似问题