@@ -29,16 +29,16 @@ extension Result {
29
29
// MARK: - ModelTableViewCell
30
30
class ModelTableViewCell : UITableViewCell {
31
31
static let identifier = " ModelTableViewCell "
32
-
32
+
33
33
override init ( style: UITableViewCell . CellStyle , reuseIdentifier: String ? ) {
34
34
super. init ( style: . default, reuseIdentifier: reuseIdentifier)
35
35
setupCell ( )
36
36
}
37
-
37
+
38
38
required init ? ( coder: NSCoder ) {
39
39
fatalError ( " init(coder:) has not been implemented " )
40
40
}
41
-
41
+
42
42
private func setupCell( ) {
43
43
backgroundColor = . clear
44
44
selectionStyle = . default
@@ -47,17 +47,17 @@ class ModelTableViewCell: UITableViewCell {
47
47
textLabel? . adjustsFontSizeToFitWidth = true
48
48
textLabel? . minimumScaleFactor = 0.7
49
49
textLabel? . textColor = . white
50
-
50
+
51
51
let selectedBGView = UIView ( )
52
52
selectedBGView. backgroundColor = UIColor ( white: 1.0 , alpha: 0.3 )
53
53
selectedBGView. layer. cornerRadius = 5
54
54
selectedBackgroundView = selectedBGView
55
55
}
56
-
56
+
57
57
func configure( with modelName: String , isRemote: Bool , isDownloaded: Bool ) {
58
58
textLabel? . text = modelName
59
59
textLabel? . textColor = ( isRemote && !isDownloaded) ? . lightGray : . white
60
-
60
+
61
61
if isRemote && !isDownloaded {
62
62
let config = UIImage . SymbolConfiguration ( pointSize: 14 )
63
63
imageView? . image = UIImage ( systemName: " icloud.and.arrow.down " , withConfiguration: config)
@@ -66,7 +66,7 @@ class ModelTableViewCell: UITableViewCell {
66
66
imageView? . image = nil
67
67
}
68
68
}
69
-
69
+
70
70
override func layoutSubviews( ) {
71
71
super. layoutSubviews ( )
72
72
selectedBackgroundView? . frame = bounds. insetBy ( dx: 2 , dy: 1 )
@@ -98,7 +98,7 @@ class ViewController: UIViewController, YOLOViewDelegate {
98
98
activityIndicator. startAnimating ( )
99
99
view. isUserInteractionEnabled = false
100
100
modelTableView. isUserInteractionEnabled = false
101
-
101
+
102
102
if showOverlay {
103
103
showLoadingOverlay ( )
104
104
}
@@ -109,7 +109,7 @@ class ViewController: UIViewController, YOLOViewDelegate {
109
109
hideLoadingOverlay ( )
110
110
}
111
111
}
112
-
112
+
113
113
private func showLoadingOverlay( ) {
114
114
guard loadingOverlayView == nil else { return }
115
115
let overlay = UIView ( frame: view. bounds)
@@ -141,7 +141,7 @@ class ViewController: UIViewController, YOLOViewDelegate {
141
141
private var currentModelName : String = " "
142
142
143
143
private var isLoadingModel = false
144
-
144
+
145
145
// MARK: - Constants
146
146
private struct Constants {
147
147
static let defaultTaskIndex = 2 // Detect
@@ -199,41 +199,44 @@ class ViewController: UIViewController, YOLOViewDelegate {
199
199
super. viewWillAppear ( animated)
200
200
view. overrideUserInterfaceStyle = . dark
201
201
}
202
-
202
+
203
203
// MARK: - Setup Methods
204
204
private func setupDownloadProgressViews( ) {
205
205
downloadProgressView. isHidden = true
206
206
downloadProgressLabel. textAlignment = . center
207
207
downloadProgressLabel. textColor = . systemGray
208
208
downloadProgressLabel. font = . systemFont( ofSize: 14 )
209
209
downloadProgressLabel. isHidden = true
210
-
210
+
211
211
[ downloadProgressView, downloadProgressLabel] . forEach {
212
212
$0. translatesAutoresizingMaskIntoConstraints = false
213
213
view. addSubview ( $0)
214
214
}
215
-
215
+
216
216
NSLayoutConstraint . activate ( [
217
217
downloadProgressView. centerXAnchor. constraint ( equalTo: view. centerXAnchor) ,
218
- downloadProgressView. topAnchor. constraint ( equalTo: activityIndicator. bottomAnchor, constant: 8 ) ,
218
+ downloadProgressView. topAnchor. constraint (
219
+ equalTo: activityIndicator. bottomAnchor, constant: 8 ) ,
219
220
downloadProgressView. widthAnchor. constraint ( equalToConstant: Constants . progressViewWidth) ,
220
221
downloadProgressView. heightAnchor. constraint ( equalToConstant: 2 ) ,
221
222
downloadProgressLabel. centerXAnchor. constraint ( equalTo: downloadProgressView. centerXAnchor) ,
222
- downloadProgressLabel. topAnchor. constraint ( equalTo: downloadProgressView. bottomAnchor, constant: 8 )
223
+ downloadProgressLabel. topAnchor. constraint (
224
+ equalTo: downloadProgressView. bottomAnchor, constant: 8 ) ,
223
225
] )
224
226
}
225
-
227
+
226
228
private func setupLabels( ) {
227
229
labelName. textColor = . white
228
230
labelFPS. textColor = . white
229
231
labelVersion. textColor = . white
230
-
232
+
231
233
labelName. overrideUserInterfaceStyle = . dark
232
234
labelFPS. overrideUserInterfaceStyle = . dark
233
235
labelVersion. overrideUserInterfaceStyle = . dark
234
-
236
+
235
237
if let version = Bundle . main. infoDictionary ? [ " CFBundleShortVersionString " ] as? String ,
236
- let build = Bundle . main. infoDictionary ? [ " CFBundleVersion " ] as? String {
238
+ let build = Bundle . main. infoDictionary ? [ " CFBundleVersion " ] as? String
239
+ {
237
240
labelVersion. text = " v \( version) ( \( build) ) "
238
241
}
239
242
}
@@ -253,20 +256,22 @@ class ViewController: UIViewController, YOLOViewDelegate {
253
256
254
257
private func getModelFiles( in folderName: String ) -> [ String ] {
255
258
guard let folderURL = Bundle . main. url ( forResource: folderName, withExtension: nil ) ,
256
- let fileURLs = try ? FileManager . default. contentsOfDirectory (
257
- at: folderURL, includingPropertiesForKeys: nil , options: [ . skipsHiddenFiles]
258
- ) else { return [ ] }
259
-
260
- let modelFiles = fileURLs
259
+ let fileURLs = try ? FileManager . default. contentsOfDirectory (
260
+ at: folderURL, includingPropertiesForKeys: nil , options: [ . skipsHiddenFiles]
261
+ )
262
+ else { return [ ] }
263
+
264
+ let modelFiles =
265
+ fileURLs
261
266
. filter { [ " mlmodel " , " mlpackage " ] . contains ( $0. pathExtension) }
262
267
. map { $0. lastPathComponent }
263
-
268
+
264
269
return folderName == " DetectModels " ? reorderDetectionModels ( modelFiles) : modelFiles. sorted ( )
265
270
}
266
271
267
272
private func reorderDetectionModels( _ fileNames: [ String ] ) -> [ String ] {
268
273
let officialOrder : [ Character : Int ] = [ " n " : 0 , " m " : 1 , " s " : 2 , " l " : 3 , " x " : 4 ]
269
-
274
+
270
275
let ( official, custom) = fileNames. reduce ( into: ( [ String] ( ) , [ String] ( ) ) ) { result, fileName in
271
276
let baseName = ( fileName as NSString ) . deletingPathExtension. lowercased ( )
272
277
if baseName. hasPrefix ( " yolo " ) , let lastChar = baseName. last, officialOrder [ lastChar] != nil {
@@ -275,14 +280,15 @@ class ViewController: UIViewController, YOLOViewDelegate {
275
280
result. 1 . append ( fileName)
276
281
}
277
282
}
278
-
279
- return custom. sorted ( ) + official. sorted { fileA, fileB in
280
- let baseA = ( fileA as NSString ) . deletingPathExtension. lowercased ( )
281
- let baseB = ( fileB as NSString ) . deletingPathExtension. lowercased ( )
282
- let indexA = baseA. last. flatMap { officialOrder [ $0] } ?? Int . max
283
- let indexB = baseB. last. flatMap { officialOrder [ $0] } ?? Int . max
284
- return indexA < indexB
285
- }
283
+
284
+ return custom. sorted ( )
285
+ + official. sorted { fileA, fileB in
286
+ let baseA = ( fileA as NSString ) . deletingPathExtension. lowercased ( )
287
+ let baseB = ( fileB as NSString ) . deletingPathExtension. lowercased ( )
288
+ let indexA = baseA. last. flatMap { officialOrder [ $0] } ?? Int . max
289
+ let indexB = baseB. last. flatMap { officialOrder [ $0] } ?? Int . max
290
+ return indexA < indexB
291
+ }
286
292
}
287
293
288
294
private func reloadModelEntriesAndLoadFirst( for taskName: String ) {
@@ -330,14 +336,14 @@ class ViewController: UIViewController, YOLOViewDelegate {
330
336
private func loadModel( entry: ( name: String , url: URL ? , isLocal: Bool ) , forTask task: String ) {
331
337
guard !isLoadingModel else { return }
332
338
isLoadingModel = true
333
-
339
+
334
340
yoloView. resetLayers ( )
335
341
yoloView. setInferenceFlag ( ok: false )
336
342
setLoadingState ( true , showOverlay: true )
337
343
resetDownloadProgress ( )
338
-
344
+
339
345
let yoloTask = convertTaskNameToYOLOTask ( task)
340
-
346
+
341
347
if entry. isLocal {
342
348
loadLocalModel ( entry: entry, task: task, yoloTask: yoloTask)
343
349
} else if let remoteURL = entry. url {
@@ -346,29 +352,35 @@ class ViewController: UIViewController, YOLOViewDelegate {
346
352
finishLoadingModel ( success: false , modelName: entry. name)
347
353
}
348
354
}
349
-
350
- private func loadLocalModel( entry: ( name: String , url: URL ? , isLocal: Bool ) , task: String , yoloTask: YOLOTask ) {
355
+
356
+ private func loadLocalModel(
357
+ entry: ( name: String , url: URL ? , isLocal: Bool ) , task: String , yoloTask: YOLOTask
358
+ ) {
351
359
guard let folderURL = tasks. first ( where: { $0. name == task } ) ? . folder,
352
- let folderPathURL = Bundle . main. url ( forResource: folderURL, withExtension: nil ) else {
360
+ let folderPathURL = Bundle . main. url ( forResource: folderURL, withExtension: nil )
361
+ else {
353
362
finishLoadingModel ( success: false , modelName: entry. name)
354
363
return
355
364
}
356
-
365
+
357
366
let modelURL = folderPathURL. appendingPathComponent ( entry. name + " .mlpackage " )
358
367
downloadProgressLabel. text = " Loading \( entry. name) "
359
368
downloadProgressLabel. isHidden = false
360
-
369
+
361
370
yoloView. setModel ( modelPathOrName: modelURL. path, task: yoloTask) { [ weak self] result in
362
371
self ? . finishLoadingModel ( success: result. isSuccess, modelName: entry. name)
363
372
}
364
373
}
365
-
366
- private func loadRemoteModel( url: URL , entry: ( name: String , url: URL ? , isLocal: Bool ) , yoloTask: YOLOTask ) {
374
+
375
+ private func loadRemoteModel(
376
+ url: URL , entry: ( name: String , url: URL ? , isLocal: Bool ) , yoloTask: YOLOTask
377
+ ) {
367
378
let downloader = YOLOModelDownloader ( )
368
379
downloadProgressView. isHidden = false
369
380
downloadProgressLabel. isHidden = false
370
-
371
- downloader. download ( from: url, task: yoloTask,
381
+
382
+ downloader. download (
383
+ from: url, task: yoloTask,
372
384
progress: { [ weak self] progress in
373
385
DispatchQueue . main. async {
374
386
self ? . downloadProgressView. progress = Float ( progress)
@@ -403,14 +415,14 @@ class ViewController: UIViewController, YOLOViewDelegate {
403
415
self . setLoadingState ( false )
404
416
self . isLoadingModel = false
405
417
self . resetDownloadProgress ( )
406
-
418
+
407
419
self . modelTableView. reloadData ( )
408
420
if let ip = self . selectedIndexPath {
409
421
self . modelTableView. selectRow ( at: ip, animated: false , scrollPosition: . none)
410
422
}
411
-
423
+
412
424
self . yoloView. setInferenceFlag ( ok: success)
413
-
425
+
414
426
if success {
415
427
self . currentModelName = modelName
416
428
self . labelName. text = processString ( modelName)
@@ -501,11 +513,11 @@ class ViewController: UIViewController, YOLOViewDelegate {
501
513
super. viewDidLayoutSubviews ( )
502
514
layoutModelTableView ( )
503
515
}
504
-
516
+
505
517
private func layoutModelTableView( ) {
506
518
let isLandscape = view. bounds. width > view. bounds. height
507
519
let tableViewWidth = view. bounds. width * ( isLandscape ? 0.2 : 0.4 )
508
-
520
+
509
521
if isLandscape {
510
522
modelTableView. frame = CGRect (
511
523
x: segmentedControl. frame. maxX + 20 , y: 20 , width: tableViewWidth, height: 200 )
@@ -516,10 +528,10 @@ class ViewController: UIViewController, YOLOViewDelegate {
516
528
width: tableViewWidth,
517
529
height: 200 )
518
530
}
519
-
531
+
520
532
updateTableViewBackground ( )
521
533
}
522
-
534
+
523
535
private func updateTableViewBackground( ) {
524
536
tableViewBGView. frame = CGRect (
525
537
x: modelTableView. frame. minX - 1 ,
0 commit comments