导入 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。
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
//
|
||||
// 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([]))
|
||||
}
|
||||
Reference in New Issue
Block a user