如何处理 SwiftUI 中的加载屏幕?
How to handle loading screens in SwiftUI?
我正在尝试在 SwiftUI 上实现一个简单的三屏应用程序。第一个屏幕允许用户 select 一张照片,第二个屏幕必须在分析照片时显示加载屏幕,第三个显示分析结果。目前,我的应用程序根本不显示加载屏幕,只是跳转到第三个屏幕。
ContentView.swift:
struct ContentView: View {
@State var image: Image = Image("MyImageName")
@State var tiles: [Tile] = []
@State var isSelectionView: Bool = true
@State var isLoadingView: Bool = false
@State var isAdjustmentView: Bool = false
var body: some View {
if (isSelectionView) {
SelectionView(image: $image, isSelectionView: $isSelectionView, isLoadingView: $isLoadingView)
}
if (isLoadingView) {
LoadingMLView(image: $image, tiles: $tiles, isLoadingView: $isLoadingView, isAdjustmentView: $isAdjustmentView)
}
if (isAdjustmentView) {
AdjustmentView(tiles: $tiles)
}
}}
SelectionView.swift:
struct SelectionView: View {
@State var showCaptureImageView = false
@State var showPopover = false
@State var sourceType: UIImagePickerController.SourceType = .camera
@State var imageSelected = false
@Binding var image: Image
@Binding var isSelectionView: Bool
@Binding var isLoadingView: Bool
var body: some View {
VStack() {
if (!showCaptureImageView && !imageSelected) {
Spacer()
Button(action: {
showPopover.toggle()
}) {
Text("Choose photo")
.frame(width: 100, height: 100)
.foregroundColor(Color.white)
.background(Color.blue)
.clipShape(Circle())
}
.confirmationDialog("Select location", isPresented: $showPopover) {
Button("Camera") {
sourceType = .camera
showPopover.toggle()
showCaptureImageView.toggle()
}
Button("Photos") {
sourceType = .photoLibrary
showPopover.toggle()
showCaptureImageView.toggle()
}
}
} else if (showCaptureImageView) {
CaptureImageView(isShown: $showCaptureImageView, image: $image, sourceType: $sourceType)
} else {
ActivityIndicator()
.frame(width: 200, height: 200)
.foregroundColor(.blue)
}
}
.onChange(of: image) { image in
showCaptureImageView.toggle()
imageSelected.toggle()
isSelectionView.toggle()
isLoadingView.toggle()
}
}}
LoadingMLView.swift:
struct LoadingMLView: View {
@Binding var image: Image
@Binding var tiles: [Tile]
@Binding var isLoadingView: Bool
@Binding var isAdjustmentView: Bool
var body: some View {
ActivityIndicator()
.frame(width: 200, height: 200)
.foregroundColor(.blue)
.onAppear {
do {
let model = try VNCoreMLModel(for: MyModel().model)
let request = VNCoreMLRequest(model: model, completionHandler: myResultsMethod)
let uiImage: UIImage = image.asUIImage()
guard let ciImage = CIImage(image: uiImage) else {
fatalError("Unable to create \(CIImage.self) from \(image).")
}
let handler = VNImageRequestHandler(ciImage: ciImage)
do {
try handler.perform([request])
} catch {
print("Something went wrong!")
}
func myResultsMethod(request: VNRequest, error: Error?) {
guard let results = request.results as? [VNRecognizedObjectObservation]
else { fatalError("error") }
for result in results {
let tile = Tile(name: result.labels[0].identifier)
tiles.append(tile)
}
isLoadingView.toggle()
isAdjustmentView.toggle()
}
} catch {
print("Error:", error)
}
}
}}
无论我如何更改屏幕之间的切换,它似乎都没有反应,并且视图不会立即切换。我究竟做错了什么?在图像 selected 之后,是否有正确的方法来显示加载屏幕?
似乎您缺少一个 else
来避免呈现您不想要的屏幕:
var body: some View {
if (isSelectionView) {
SelectionView(image: $image, isSelectionView: $isSelectionView, isLoadingView: $isLoadingView)
} else if (isLoadingView) {
LoadingMLView(image: $image, tiles: $tiles, isLoadingView: $isLoadingView, isAdjustmentView: $isAdjustmentView)
} else if (isAdjustmentView) {
AdjustmentView(tiles: $tiles)
}
}}
但是,一般来说,您想要的也称为“状态机”——基本上您的应用可以处于 3 种可能状态之一:选择、加载、调整,因此您应该有一个枚举 @State
表示当前状态,而不是 3 个布尔值:
struct ContentView: View {
// ...
enum AppState { case selection, loading, adjustment }
@State private var state = AppState.selection
var body: some View {
switch state {
case .selection:
SelectionView(image: $image, state: $state)
case .loading:
LoadingMLView(image: $image, tiles: $tiles, state: $state)
case .adjustment:
AdjustmentView(tiles: $tiles)
}
}
当您想切换到下一个屏幕时,只需更改state
值...
原来,主要的性能问题是由图像类型转换引起的:
let uiImage: UIImage = image.asUIImage()
首先将图像作为 UIImage 传递后,应用程序开始快速运行,因此无需首先处理加载。
我正在尝试在 SwiftUI 上实现一个简单的三屏应用程序。第一个屏幕允许用户 select 一张照片,第二个屏幕必须在分析照片时显示加载屏幕,第三个显示分析结果。目前,我的应用程序根本不显示加载屏幕,只是跳转到第三个屏幕。
ContentView.swift:
struct ContentView: View {
@State var image: Image = Image("MyImageName")
@State var tiles: [Tile] = []
@State var isSelectionView: Bool = true
@State var isLoadingView: Bool = false
@State var isAdjustmentView: Bool = false
var body: some View {
if (isSelectionView) {
SelectionView(image: $image, isSelectionView: $isSelectionView, isLoadingView: $isLoadingView)
}
if (isLoadingView) {
LoadingMLView(image: $image, tiles: $tiles, isLoadingView: $isLoadingView, isAdjustmentView: $isAdjustmentView)
}
if (isAdjustmentView) {
AdjustmentView(tiles: $tiles)
}
}}
SelectionView.swift:
struct SelectionView: View {
@State var showCaptureImageView = false
@State var showPopover = false
@State var sourceType: UIImagePickerController.SourceType = .camera
@State var imageSelected = false
@Binding var image: Image
@Binding var isSelectionView: Bool
@Binding var isLoadingView: Bool
var body: some View {
VStack() {
if (!showCaptureImageView && !imageSelected) {
Spacer()
Button(action: {
showPopover.toggle()
}) {
Text("Choose photo")
.frame(width: 100, height: 100)
.foregroundColor(Color.white)
.background(Color.blue)
.clipShape(Circle())
}
.confirmationDialog("Select location", isPresented: $showPopover) {
Button("Camera") {
sourceType = .camera
showPopover.toggle()
showCaptureImageView.toggle()
}
Button("Photos") {
sourceType = .photoLibrary
showPopover.toggle()
showCaptureImageView.toggle()
}
}
} else if (showCaptureImageView) {
CaptureImageView(isShown: $showCaptureImageView, image: $image, sourceType: $sourceType)
} else {
ActivityIndicator()
.frame(width: 200, height: 200)
.foregroundColor(.blue)
}
}
.onChange(of: image) { image in
showCaptureImageView.toggle()
imageSelected.toggle()
isSelectionView.toggle()
isLoadingView.toggle()
}
}}
LoadingMLView.swift:
struct LoadingMLView: View {
@Binding var image: Image
@Binding var tiles: [Tile]
@Binding var isLoadingView: Bool
@Binding var isAdjustmentView: Bool
var body: some View {
ActivityIndicator()
.frame(width: 200, height: 200)
.foregroundColor(.blue)
.onAppear {
do {
let model = try VNCoreMLModel(for: MyModel().model)
let request = VNCoreMLRequest(model: model, completionHandler: myResultsMethod)
let uiImage: UIImage = image.asUIImage()
guard let ciImage = CIImage(image: uiImage) else {
fatalError("Unable to create \(CIImage.self) from \(image).")
}
let handler = VNImageRequestHandler(ciImage: ciImage)
do {
try handler.perform([request])
} catch {
print("Something went wrong!")
}
func myResultsMethod(request: VNRequest, error: Error?) {
guard let results = request.results as? [VNRecognizedObjectObservation]
else { fatalError("error") }
for result in results {
let tile = Tile(name: result.labels[0].identifier)
tiles.append(tile)
}
isLoadingView.toggle()
isAdjustmentView.toggle()
}
} catch {
print("Error:", error)
}
}
}}
无论我如何更改屏幕之间的切换,它似乎都没有反应,并且视图不会立即切换。我究竟做错了什么?在图像 selected 之后,是否有正确的方法来显示加载屏幕?
似乎您缺少一个 else
来避免呈现您不想要的屏幕:
var body: some View {
if (isSelectionView) {
SelectionView(image: $image, isSelectionView: $isSelectionView, isLoadingView: $isLoadingView)
} else if (isLoadingView) {
LoadingMLView(image: $image, tiles: $tiles, isLoadingView: $isLoadingView, isAdjustmentView: $isAdjustmentView)
} else if (isAdjustmentView) {
AdjustmentView(tiles: $tiles)
}
}}
但是,一般来说,您想要的也称为“状态机”——基本上您的应用可以处于 3 种可能状态之一:选择、加载、调整,因此您应该有一个枚举 @State
表示当前状态,而不是 3 个布尔值:
struct ContentView: View {
// ...
enum AppState { case selection, loading, adjustment }
@State private var state = AppState.selection
var body: some View {
switch state {
case .selection:
SelectionView(image: $image, state: $state)
case .loading:
LoadingMLView(image: $image, tiles: $tiles, state: $state)
case .adjustment:
AdjustmentView(tiles: $tiles)
}
}
当您想切换到下一个屏幕时,只需更改state
值...
原来,主要的性能问题是由图像类型转换引起的:
let uiImage: UIImage = image.asUIImage()
首先将图像作为 UIImage 传递后,应用程序开始快速运行,因此无需首先处理加载。