问题描述
我尝试制作带有可替换图像的 UIKit 元素,我可以像使用 swiftUI 元素一样使用它。
我现在卡住了,应该使用 ObservableObject 中的 imageInBlackBox 刷新 UIImageView 中的图像。在选择图像后,我尝试从 updateUIView 和 imagePickerController 设置新的 imageInBlackBox 。但是这两种方法都不会更新 UIImageView,它仅适用于我用于测试的 swiftUI Image 元素。
如何在 imageInBlackBox 更改后刷新 imageView.image?
//Data model
struct imageAdderDataHolder: Identifiable,Hashable {
var id: UUID = UUID()
var isShowingImagePicker:Bool = false
var imageInBlackBox:UIImage = UIImage(systemName: "photo")! // image for replace
var height: CGFloat = 160
var width: CGFloat = 160
}
//Data storage
class imageAdderData: ObservableObject{
init() {}
static let shared = imageAdderData()
@Published var img1: imageAdderDataHolder = imageAdderDataHolder()
}
struct simpleadder: UIViewRepresentable{
@Observedobject var imageData: imageAdderData = .shared
let mainView: UIView = UIView()
var imageView: UIImageView = UIImageView()
func makeUIView(context: Context) -> UIView {
imageView.image = imageData.img1.imageInBlackBox
imageView.frame.size.width = imageData.img1.width
imageView.frame.size.height = imageData.img1.height
imageView.contentMode = .scaleAspectFit
mainView.addSubview(imageView)
return mainView
}
func updateUIView(_ uiView: UIView,context: Context) {
imageView.image = imageData.img1.imageInBlackBox // try to replace image
}
}
// swiftui view for test
struct photoadder: View {
@Observedobject var imageData: imageAdderData = .shared
var body: some View {
vstack{
HStack{
simpleadder()
.frame(width: imageData.img1.width,height: imageData.img1.height)
.border(Color.black,width:1)
.sheet(isPresented: $imageData.img1.isShowingImagePicker,content: {
imagePickerUIView(isPresented: $imageData.img1.isShowingImagePicker)
})
Image(uiImage: imageData.img1.imageInBlackBox) // working element for test
.resizable()
.aspectRatio(contentMode: ContentMode.fit)
.frame(width: imageData.img1.width,width: 1)
}
Button("change image") {
imageData.img1.isShowingImagePicker = true
}
}
}
}
struct imagePickerUIView: UIViewControllerRepresentable {
@Observedobject var imageData: imageAdderData = .shared
@Binding var isPresented: Bool
func makeUIViewController(context:
UIViewControllerRepresentableContext<imagePickerUIView>) ->
UIViewController {
let controller = UIImagePickerController()
controller.delegate = context.coordinator
return controller
}
func makeCoordinator() -> imagePickerUIView.Coordinator {
return Coordinator(parent: self)
}
class Coordinator: NSObject,UIImagePickerControllerDelegate,UINavigationControllerDelegate {
let parent: imagePickerUIView
init(parent: imagePickerUIView) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController,didFinishPickingMediawithInfo info:
[UIImagePickerController.InfoKey : Any]) {
if let selectedImageFromPicker = info[.originalImage] as? UIImage {
// try to replace image
parent.imageData.img1.imageInBlackBox = selectedImageFromPicker
}
self.parent.isPresented = false
}
}
func updateUIViewController(_ uiViewController:
imagePickerUIView.UIViewControllerType,context:
UIViewControllerRepresentableContext<imagePickerUIView>) {
}
}
解决方法
这是一个解决方案 - 在可表示中,所有视图都应该在 makeUIView
内创建,因为结构可以(并且通常会)在父更新时重新创建(因此内部实例也将被重新创建,同时使 UIKit 视图生命-循环持续)。
使用 Xcode 12.4 / iOS 14.4 测试。
固定代码:
struct simpleadder: UIViewRepresentable{
@ObservedObject var imageData: imageAdderData = .shared
func makeUIView(context: Context) -> UIView {
let mainView: UIView = UIView() // << create here !!
let imageView: UIImageView = UIImageView() // << create here !!
imageView.image = imageData.img1.imageInBlackBox
imageView.frame.size.width = imageData.img1.width
imageView.frame.size.height = imageData.img1.height
imageView.contentMode = .scaleAspectFit
mainView.addSubview(imageView)
return mainView
}
func updateUIView(_ uiView: UIView,context: Context) {
// find needed view in run-time
if let imageView = uiView.subviews.first as? UIImageView {
imageView.image = imageData.img1.imageInBlackBox
}
}
}