Files
ios/Xuxucheck/XuXuCheck/Camera/CameraView.swift
T
admin d6aeede2bc 导入 XuXuCheck 工程(iOS 干化学尿液试纸分析 App)
从桌面工程复制而来,作为本仓库的 Xuxucheck/ 子目录。

为控制仓库体积做了两处删减:
- 移除 Frameworks/opencv2.xcframework 下的两个 visionOS 切片
  (xros-arm64、xros-arm64-simulator,合计约 106 MB),并同步删掉该
  xcframework 内 Info.plist 里对应的两条 AvailableLibraries 记录,
  只保留 ios-arm64 与 ios-arm64_x86_64-simulator。
- 移除全部 .DS_Store 与 xcuserdata/。

已用 xcodebuild 从本目录完整编译验证:BUILD SUCCEEDED,
ProcessXCFramework 以 --platform ios 成功选中框架,产物含 3699 个 cv:: 符号
(OpenCV 确实链入),嵌入 framework 3 个,与移除无用 pod 之后的状态一致。

仓库约 206 MB,其中 OpenCV 203 MB、其余源码与依赖约 3 MB。
2026-09-23 14:17:31 +08:00

231 lines
8.2 KiB
Swift

//
// CameraView.swift
// XuXuCheck
//
// Created by henseycai on 2025/6/25.
//
import SwiftUI
struct CameraView: View {
// @Binding var image: CGImage?
// @Binding var array3D: [[[Double]]]
@Environment(CameraViewModel.self) private var cameraViewModel
@Environment(\.dismiss) private var dissmiss
//https://cloverlaun.tistory.com/77
//https://stackoverflow.com/questions/59438285/disable-swipe-back-for-a-navigationlink-swiftui/65354662
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State private var showingNaiveSafariViewFullScreenCover = false
@State var isAnimating = false
//let grayImage = OpenCVWrapper.toGrayscale(uiImage)
var body: some View {
ZStack (alignment: .top){
GeometryReader { geometry in
if let image = cameraViewModel.currentFrame {
let uiImage = UIImage(cgImage: image)
let result = OpenCVWrapper.analysisImage(uiImage)
let wbArr = result.paperWbValue as! [Double]
// let descArr = result.paperTypeDesc as! [Int]
// let wbArr = result.paperWbValue as! [Int]
if(wbArr[3] == 0){
if(cameraViewModel.array3D.count < 16){
let _ = cameraViewModel.array3D.append(result.paperAnalResults as! [[Double]])
// let _ = print(cameraViewModel.array3D.count)
}else{
if(cameraViewModel.isDataSent == false){
let _ = print(cameraViewModel.array3D)
//let _ = cameraViewModel.stopSession()
let _ = cameraViewModel.addRecord(desc: result.paperTypeDesc as! [Int])
}
//following line works just bcs Image come continuously ,so following triggered. thanks God~
// if(cameraViewModel.isSuccess == true){
// let _ = self.presentationMode.wrappedValue.dismiss()
//
// XuXuWebView(currentUrl: cameraViewModel.recordDetailUrl())
// }
}
//let _ = print(wbArr)
// let _ = print(descArr)
}
Image(uiImage: result.srcImg)
.resizable()
.scaledToFit()
.frame(width: geometry.size.width,
height: geometry.size.height)
//.clipped()
/*clipped() This crucial modifier crops any content of the view that extends beyond its defined frame. When combined with .scaledToFill() and .frame(), it effectively "crops" the image to the specified dimensions.*/
Image("scanfan").resizable().frame(width: 60,height: 235)
.offset(x: isAnimating ? geometry.size.width : -50,y:70)
.animation(.easeInOut(duration: 5).repeatForever(autoreverses: false), value: isAnimating)
.onAppear{
isAnimating.toggle()
}
Text("Scan urinal strip in natural light. Put strip on white paper towel.Ensure strip is completely presented in green box.").foregroundStyle(Color.white)
.font(.title2).frame(maxWidth: .infinity, maxHeight:.infinity,alignment: .topLeading)
.padding().background(Color.gray)
.offset(x:0,y:400)
} else {
// ContentUnavailableView("Camera feed interrupted", systemImage: "xmark.circle.fill")
// .frame(width: geometry.size.width,
// height: geometry.size.height)
}
}
.padding(.top,20)
.onChange(of: cameraViewModel.isSuccess){oldState,newState in
if(oldState == false && newState == true){
//https://www.hackingwithswift.com/quick-start/swiftui/how-to-run-some-code-when-state-changes-using-onchange
//https://developer.apple.com/documentation/swiftui/view/onchange(of:perform:)
// showingNaiveSafariViewFullScreenCover = true
// dissmiss()
showingNaiveSafariViewFullScreenCover.toggle()
}
}
.onChange(of: cameraViewModel.isNeedPopErr){oldState,newState in
if(oldState == false && newState == true){
showTopToast.toggle()
}
}
.fullScreenCover(isPresented: $showingNaiveSafariViewFullScreenCover) {
XuXuWebView(currentUrl: cameraViewModel.recordDetailUrl())
}
.present(isPresented: self.$showTopToast, type: .toast, position: .top, duration: 6.0, onTap: {
}, onToastDismiss: {
dissmiss()
}) {
self.createTopToastView()
}
HStack {
Image(systemName: "arrow.backward")
.frame(width: 50, height: 50)
.foregroundColor(.secondary)
.strokeStyle(cornerRadius: 15)
.onTapGesture {
dissmiss()
}
Text("Scanning").font(.headline).frame(maxWidth: .infinity, alignment: .center)
}.padding(.top,50)
}
// .navigationBarTitleDisplayMode(.inline)
// .toolbar{
// ToolbarItem(placement:.principal){
//
//
// Text("Timer").font(.headline)
// }
// }
//
// .navigationBarItems(
// leading: Button(action: {
// dissmiss()
// }, label: {
// Image(systemName: "arrow.backward")
// .foregroundColor(Color(red: 231/255, green: 200/255, blue: 153/255))
// }), trailing: Button(action: {
// // 点击按钮时的操作
// }, label: {
// Text("明细")
// .foregroundColor(Color(red: 231/255, green: 200/255, blue: 153/255))
// }))
}
let topToastColor = Color(hex: "4067CC")
@State var showTopToast = false
func createTopToastView() -> some View {
VStack {
Spacer(minLength: 30)
HStack() {
Image("AppIcon")
.resizable()
.aspectRatio(contentMode: ContentMode.fill)
.frame(width: 50, height: 50)
.cornerRadius(25)
VStack(alignment: .leading, spacing: 2) {
HStack {
Text("Error")
.font(.system(size: 18))
.foregroundColor(.white)
.fontWeight(.bold)
Spacer()
Image(systemName: "info")
.font(.system(size: 12))
.foregroundColor(.white)
}
Text("\(cameraViewModel.xuxuError?.message)" + "!")
.lineLimit(2)
.font(.system(size: 16))
.foregroundColor(.white)
}
}.padding(15)
}
.frame(height: 130)
.background(self.topToastColor)
}
}
#Preview {
CameraView()
// CameraView(image: .constant(nil),array3D: .constant([]))
}