UIViewRepresentable无法在模式显示的SwiftUI视图中工作
为了定制UIViewRepresentable无法在模式显示的SwiftUI视图中工作,swiftui,combine,observableobject,Swiftui,Combine,Observableobject,为了定制UISlider,我在UIViewRepresentable中使用它。它公开了一个@Binding var值:Double,以便我的视图模型(observeobject)视图可以观察到更改并相应地更新视图 问题是当@Binding值更改时,视图不会更新。在下面的示例中,我有两个滑块。一个本机滑块和一个自定义SwiftUISlider 两者都将绑定值传递给应更新视图的视图模型。本机滑块会更新视图,但不会更新自定义视图。在日志中,我可以看到正确调用了$sliderValue.sink{…,但
UISlider
,我在UIViewRepresentable
中使用它。它公开了一个@Binding var值:Double
,以便我的视图模型(observeobject
)视图可以观察到更改并相应地更新视图
问题是当@Binding
值更改时,视图不会更新。在下面的示例中,我有两个滑块。一个本机滑块
和一个自定义SwiftUISlider
两者都将绑定值传递给应更新视图的视图模型。本机滑块
会更新视图,但不会更新自定义视图。在日志中,我可以看到正确调用了$sliderValue.sink{…
,但视图没有更新
我注意到,当presenting视图具有@Environment(\.presentationMode)var presentationMode:Binding
属性时,就会发生这种情况。如果我对其进行注释,它会按预期工作
一个完整的示例代码来重现这一点
import SwiftUI
import Combine
struct ContentView: View {
@State var isPresentingModal = false
// comment this out
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
var body: some View {
VStack {
Button("Show modal") {
isPresentingModal = true
}
.padding()
}
.sheet(isPresented: $isPresentingModal) {
MyModalView(viewModel: TempViewModel())
}
}
}
class TempViewModel: ObservableObject {
@Published var sliderText = ""
@Published var sliderValue: Double = 0
private var cancellable = Set<AnyCancellable>()
init() {
$sliderValue
.print("view model")
.sink { [weak self] value in
guard let self = self else { return }
print("updating view \(value)")
self.sliderText = "\(value) C = \(String(format: "%.2f" ,value * 9 / 5 + 32)) F"
}
.store(in: &cancellable)
}
}
struct MyModalView: View {
@ObservedObject var viewModel: TempViewModel
var body: some View {
VStack(alignment: .leading) {
Text("SwiftUI Slider")
Slider(value: $viewModel.sliderValue, in: -100...100, step: 0.5)
.padding(.bottom)
Text("UIViewRepresentable Slider")
SwiftUISlider(minValue: -100, maxValue: 100, value: $viewModel.sliderValue)
Text(viewModel.sliderText)
}
.padding()
}
}
struct SwiftUISlider: UIViewRepresentable {
final class Coordinator: NSObject {
var value: Binding<Double>
init(value: Binding<Double>) {
self.value = value
}
@objc func valueChanged(_ sender: UISlider) {
let index = Int(sender.value + 0.5)
sender.value = Float(index)
print("value changed \(sender.value)")
self.value.wrappedValue = Double(sender.value)
}
}
var minValue: Int = 0
var maxValue: Int = 0
@Binding var value: Double
func makeUIView(context: Context) -> UISlider {
let slider = UISlider(frame: .zero)
slider.minimumTrackTintColor = .systemRed
slider.maximumTrackTintColor = .systemRed
slider.maximumValue = Float(maxValue)
slider.minimumValue = Float(minValue)
slider.addTarget(
context.coordinator,
action: #selector(Coordinator.valueChanged(_:)),
for: .valueChanged
)
adapt(slider, context: context)
return slider
}
func updateUIView(_ uiView: UISlider, context: Context) {
adapt(uiView, context: context)
}
func makeCoordinator() -> SwiftUISlider.Coordinator {
Coordinator(value: $value)
}
private func adapt(_ slider: UISlider, context: Context) {
slider.value = Float(value)
}
}
struct PresentationMode_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
导入快捷界面
进口联合收割机
结构ContentView:View{
@状态变量isPresentingModal=false
//评论一下
@环境(\.presentationMode)变量presentationMode:绑定
var body:一些观点{
VStack{
按钮(“显示模式”){
isPresentingModal=true
}
.padding()
}
.sheet(isPresented:$isPresentingModal){
MyModalView(视图模型:TempViewModel())
}
}
}
类TempViewModel:ObserveObject{
@已发布的var sliderText=“”
@已发布的变量sliderValue:Double=0
private var cancelable=Set()
init(){
$sliderValue
.print(“视图模型”)
.sink{[弱自我]值在
guard let self=self-else{return}
打印(“更新视图\(值)”)
self.sliderText=“\(值)C=\(字符串(格式:“%.2f”,值*9/5+32))F”
}
.store(位于:&可取消)
}
}
结构MyModalView:视图{
@观察对象变量视图模型:TempViewModel
var body:一些观点{
VStack(对齐:。前导){
文本(“快速用户界面滑块”)
滑块(值:$viewModel.sliderValue,in:-100…100,步长:0.5)
.padding(.bottom)
文本(“UIViewRepresentable滑块”)
SwiftUISlider(最小值:-100,最大值:100,值:$viewModel.sliderValue)
文本(viewModel.sliderText)
}
.padding()
}
}
结构SwiftUISlider:UIViewRepresentable{
最终类协调器:NSObject{
var值:绑定
init(值:绑定){
自我价值=价值
}
@objc func值已更改(\发送方:UISlider){
让索引=Int(sender.value+0.5)
sender.value=Float(索引)
打印(“值已更改\(sender.value)”)
self.value.wrappedValue=Double(sender.value)
}
}
var minValue:Int=0
var maxValue:Int=0
@绑定变量值:双精度
func makeUIView(上下文:context)->UISlider{
let slider=UISlider(帧:.0)
slider.minimumTrackTintColor=.systemRed
slider.maximumTrackTintColor=.systemRed
slider.maximumValue=浮动(maxValue)
slider.minimumValue=浮动(minValue)
slider.addTarget(
背景协调员,
操作:#选择器(Coordinator.valueChanged(#:),
对于:。值已更改
)
自适应(滑块,上下文:上下文)
返回滑块
}
func updateUIView(uiView:UISlider,context:context){
自适应(uiView,上下文:上下文)
}
func makeCoordinator()->SwiftUISlider.Coordinator{
协调员(价值:$value)
}
private func adapt(uslider:UISlider,context:context){
slider.value=浮动(值)
}
}
结构表示模式预览:PreviewProvider{
静态var预览:一些视图{
ContentView()
}
}
我发现了问题。在UIViewRepresentable
的updateUIView
中,我还需要将绑定传递给SwiftUISlider
的新实例:
func updateUIView(_ uiView: UISlider, context: Context) {
uiView.value = Float(value)
// the _value is the Binding<Double> of the new View struct, we pass it to the coordinator
context.coordinator.value = _value
}
func updateUIView(uiView:UISlider,context:context){
uiView.value=浮动(值)
//_值是新视图结构的绑定,我们将其传递给协调器
context.coordinator.value=\u值
}
SwiftUI.View
可以在任何时候重新创建,当它发生时调用updateiview
。一个新的视图结构有一个新的var值:Binding
,因此我们将它分配给我们的协调器这里发生的是包含@环境(\.presentationMode)
会使ContentView
的主体在显示模型后立即重新计算。(我不知道发生这种情况的确切原因;可能是因为显示工作表
时显示模式发生了变化)
但当这种情况发生时,它会启动MyModalView
两次,并使用TempViewModel
的两个独立实例
在第一个MyModalView
上,创建了带有SwiftUISlider
的视图层次结构。这是创建协调器的地方,并存储绑定(绑定到TempViewModel
的第一个实例)
在第二个MyModelView
上,视图层次结构是相同的,因此它不调用makeUIView
(仅在视图第一次出现时调用),只调用updateUIView
。正如您正确指出的,将绑定更新到现在,TempViewModel
的第二个实例解决了这个问题
因此,一个解决方案就是您在另一个答案中所做的——基本上将绑定重新分配给新对象的属性(顺便说一句,该属性也会释放旧对象)
// option 1
struct ContentView: View {
@State var isPresentingModal = false
@StateObject var tempViewModel = TempViewModel()
@Environment(\.presentationMode) var presentationMode
var body: some View {
// ...
.sheet(isPresented: $isPresentingModal) {
MyModalView(viewModel: tempViewModel)
}
}
}
// option 2
struct ContentView: View {
@State var isPresentingModal = false
@StateObject var tempViewModel = TempViewModel()
@Environment(\.presentationMode) var presentationMode
var body: some View {
// ...
.sheet(isPresented: $isPresentingModal) {
MyModalView()
}
}
}
struct MyModalView: View {
@StateObject var viewModel = TempViewModel()
// ...
}