【发布时间】:2020-08-31 12:12:10
【问题描述】:
我的模型有一个结构,它需要符合仅是 NSObject 的协议。
我正在寻找一种可行的替代方法来将模型转换为类。要求是:
- 保持模型为值类型
- 在调用
photoLibraryDidChange时更新模型
如果PHPhotoLibraryChangeObserver 不需要实现是 NSObject,这将是理想的实现
struct Model: PHPhotoLibraryChangeObserver {
var images:[UIImages] = []
fileprivate var allPhotos:PHFetchResult<PHAsset>?
mutating func photoLibraryDidChange(_ changeInstance: PHChange) {
let changeResults = changeInstance.changeDetails(for: allPhotos)
allPhotos = changeResults?.fetchResultAfterChanges
updateImages()
}
mutating func updateImages() {
// update self.images
...
}
}
我无法将模型传递给实现观察者协议的外部类,因为所有更改都发生在副本上(它的值类型...)
有什么想法吗?最佳做法?
编辑:重新提出问题
编辑 2:进展
我已经实现了一个委托作为我模型的引用类型 var 并将数据推送到里面。现在photoLibraryDidChange不再被调用。
这是精简后的实现:
class PhotoKitAdapter:NSObject, PHPhotoLibraryChangeObserver {
var allPhotos: PHFetchResult<PHAsset>?
var images:[UIImage] = []
override init(){
super.init()
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
DispatchQueue.main.async {
if let changeResults = changeInstance.changeDetails(for: self.allPhotos!) {
self.allPhotos = changeResults.fetchResultAfterChanges
//this neve gets executed. It used to provide high quality images
self.updateImages()
}
}
}
func startFetching(){
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
allPhotos = PHAsset.fetchAssets(with: allPhotosOptions)
PHPhotoLibrary.shared().register(self)
//this gets executed and fetches the thumbnails
self.updateImages()
}
fileprivate func appendImage(_ p: PHAsset) {
let pm = PHImageManager.default()
if p.mediaType == .image {
pm.requestImage(for: p, targetSize: CGSize(width: 1024, height: 768), contentMode: .default, options: nil){
image, _ in
if let im = image {
self.images.append(im)
}
}
}
}
fileprivate func updateImages() {
self.images = []
if let ap = allPhotos {
for index in 0..<min(ap.count, 10) {
let p = ap[index]
appendImage(p)
}
}
}
}
struct Model {
private var pkAdapter = PhotoKitAdapter()
var images:[UIImage] {
pkAdapter.images
}
func startFetching(){
pkAdapter.startFetching()
}
// example model implementation
mutating func select(_ image:UIImage){
// read directly from pkAdapter.images and change other local variables
}
}
我在photoLibraryDidChange 中设置了一个断点,但它并没有去那里。我还检查了pkAdapter 始终是同一个对象,并且不会在“更改时复制”时重新初始化。
**编辑:添加模型视图**
这是负责模型管理的模型视图的相关部分
class ModelView:ObservableObject {
@Published var model = Model()
init() {
self.model.startFetching()
}
var images:[UIImage] {
self.model.images
}
...
}
编辑:解决了更新问题
这是模拟器中的一个错误......在真实设备上它可以工作
【问题讨论】:
-
我正在尝试将 PhotoKit 必须更改的唯一可变数据推送到充当委托的引用类型属性。模型的其余部分可以异步处理它,因此它可以访问委托中的数据,而无需将 self(模型)传递给 PhotoKit。
-
我写下了到目前为止的结果。似乎可行,除了
photoLibraryDidChange没有被调用...肯定有一个愚蠢的错误,我只是看不到它 -
可能取决于你的结构是如何被传递的?请记住,它是一个结构,因此每个分配都会创建一个副本。能否详细介绍一下 Model 是如何维护的等等?
-
我调试并检查了适配器类没有被多次实例化。 Swift 在更改时复制应该将指向引用类型属性的指针转移到新值。情况似乎如此。我还明确要求了适配器对象的 id,在模型上进行了几次突变后,它是相同的。这样就排除了多个副本和丢失的参考。
-
模拟器错误。在真实设备上它可以工作......真是浪费时间