以下是我的Swift应用程序中ContentView
的缩写。错误Cannot use mutating member on immutable value: 'self' is immutable
出现在我的Button动作关闭内的第self.classifyImage(self.image)
行上。如何将image
设置为可变的?还是有更好的方法来完成我要完成的工作?本质上,我想通过这里的UIImage
函数在ContentView
中传递classifyImage
变量,由Vision CoreML模型处理。
struct ContentView: View {
@State private var image = UIImage()
private lazy var classificationRequest: VNCoreMLRequest = {
do {
let model = try VNCoreMLModel(for: SqueezeNet().model)
let request = VNCoreMLRequest(model: model) { request, _ in
if let classifications =
request.results as? [VNClassificationObservation] {
print("Classification results: \(classifications)")
}
}
request.imageCropAndScaleOption = .centerCrop
return request
} catch {
fatalError("Failed to load Vision ML model: \(error)")
}
}()
private mutating func classifyImage(_ image: UIImage) {
guard let orientation = CGImagePropertyOrientation(
rawValue: UInt32(image.imageOrientation.rawValue)) else {
return
}
guard let ciImage = CIImage(image: image) else {
fatalError("Unable to create \(CIImage.self) from \(image).")
}
DispatchQueue.global(qos: .userInitiated).async {
let handler =
VNImageRequestHandler(ciImage: ciImage, orientation: orientation)
do {
try handler.perform([self.classificationRequest])
} catch {
print("Failed to perform classification.\n\(error.localizedDescription)")
}
}
}
var body: some View {
Button(action: {
self.classifyImage(self.image). // <-- error
}) {
// Button text here
}
// blah blah
}
}
答案 0 :(得分:1)
您不能从自身内部将View更改为struct(因此,不能创建任何延迟的创建,更改功能等)。如果您需要在某个地方更改image
,请直接将其指定为状态。
此处是固定的(可编译)代码部分。经过Xcode 12测试。
struct ContentView: View {
@State private var image = UIImage()
private let classificationRequest: VNCoreMLRequest = {
do {
let model = try VNCoreMLModel(for: SqueezeNet().model)
let request = VNCoreMLRequest(model: model) { request, _ in
if let classifications =
request.results as? [VNClassificationObservation] {
print("Classification results: \(classifications)")
}
}
request.imageCropAndScaleOption = .centerCrop
return request
} catch {
fatalError("Failed to load Vision ML model: \(error)")
}
}()
private func classifyImage(_ image: UIImage) {
guard let orientation = CGImagePropertyOrientation(
rawValue: UInt32(image.imageOrientation.rawValue)) else {
return
}
guard let ciImage = CIImage(image: image) else {
fatalError("Unable to create \(CIImage.self) from \(image).")
}
DispatchQueue.global(qos: .userInitiated).async {
let handler =
VNImageRequestHandler(ciImage: ciImage, orientation: orientation)
do {
try handler.perform([self.classificationRequest])
} catch {
print("Failed to perform classification.\n\(error.localizedDescription)")
}
}
}
var body: some View {
Button(action: {
self.classifyImage(self.image) // <-- error
}) {
// Button text here
}
// blah blah
}
}
答案 1 :(得分:0)
问题是您正在处理结构。在结构上更改值在语义上与为其分配新值相同。因此,当通过var contentView = ContentView()
定义ContentView结构时,该功能将起作用。如果使用let contentView = ContentView()
,则会出现错误。区别在于,通过使用mutating功能swift为图像分配新值会自动创建新的Struct并将其分配给var contentView = ...
。
另一种更好的方法是,如果您使用这样的ViewModel:
import Combine
import UIKit
....
class ContentViewModel: ObservableObject {
var image: uiImage
@Published var classification: String
init(_ image: UIImage) {
self.image = image
}
func classifyImage() {
// classify your image and assign the result to the published var classification. This way your view will be automatically updated on a change
}
}
然后您可以像这样在视图中使用模型:
struct ContentView: View {
@ObservedObject private var viewModel = ContentViewModel(UIImage())
var body: some View {
Button(action: {
self.viewModel.classifyImage()
}) {
// Button text here
}
// blah blah
}
}
由于您将逻辑封装到视图模型中,并且不会用处理代码污染视图,因此这是一种更清洁的方法。