diff --git a/ImageViewer.xcodeproj/project.pbxproj b/ImageViewer.xcodeproj/project.pbxproj index 044788c..a450fc8 100644 --- a/ImageViewer.xcodeproj/project.pbxproj +++ b/ImageViewer.xcodeproj/project.pbxproj @@ -192,6 +192,7 @@ SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -245,6 +246,7 @@ SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/ImageViewer/AppDelegate.swift b/ImageViewer/AppDelegate.swift index ca1ba55..4b3773f 100644 --- a/ImageViewer/AppDelegate.swift +++ b/ImageViewer/AppDelegate.swift @@ -2,12 +2,11 @@ import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + _: UIApplication, + didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = ViewController() diff --git a/ImageViewer/ViewController.swift b/ImageViewer/ViewController.swift index 3813a72..f5590e1 100644 --- a/ImageViewer/ViewController.swift +++ b/ImageViewer/ViewController.swift @@ -1,8 +1,8 @@ -//TODO: Anilist support? -//TODO: Properly avoid swallowing of input from UICollectionView used for scrolling -//TODO: Convert between state for normal and scrolling page turn -//TODO: Support reading with scrolling and landscape mode -//FIXME: Update comicCollectionView when switching between landscape and portrait mode +// TODO: Anilist support? +// TODO: Properly avoid swallowing of input from UICollectionView used for scrolling +// TODO: Convert between state for normal and scrolling page turn +// TODO: Support reading with scrolling and landscape mode +// FIXME: Update comicCollectionView when switching between landscape and portrait mode import Foundation import UIKit @@ -103,8 +103,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { var imageView = UIImageView() var mode = PageTurnMode.leftToRight var metadata: Metadata! - var currentPage: Int! = nil - var progress = ProgressIndices.init(v: 0, c: 0, i: 0) + var currentPage: Int! + var progress = ProgressIndices(v: 0, c: 0, i: 0) var currentPath: URL! var changedOrientation = false @@ -136,7 +136,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { var comics: [Comic] = [] @IBOutlet var comicCollectionView: UICollectionView! - let imageLoader = ImageLoader.init() + let imageLoader = ImageLoader() override func viewDidLoad() { super.viewDidLoad() @@ -198,7 +198,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { comicCollectionView.delegate = self comicCollectionView.backgroundColor = .white comicCollectionView.register( - ComicImageCell.self, forCellWithReuseIdentifier: "ComicImageCell") + ComicImageCell.self, forCellWithReuseIdentifier: "ComicImageCell" + ) homeView.addSubview(comicCollectionView) } @@ -232,13 +233,13 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { metadata = try JSONDecoder().decode( Metadata.self, from: - Data( - try String( - contentsOfFile: dir.appendingPathComponent( - "metadata.json" - ) - .path - ).utf8) + Data( + String( + contentsOfFile: dir.appendingPathComponent( + "metadata.json" + ) + .path + ).utf8) ) loadLocalState() comics.append( @@ -304,11 +305,11 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { return } - var newMetadata = Metadata.init( + var newMetadata = Metadata( title: "", original_language: "", - last_volume: MetaValue.init(main: 0, bonus: nil), - last_chapter: MetaValue.init(main: 0, bonus: nil), + last_volume: MetaValue(main: 0, bonus: nil), + last_chapter: MetaValue(main: 0, bonus: nil), chapter_count: 0, publication_demographic: "", status: "", @@ -351,19 +352,23 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { newMetadata.chapter_count += 1 assert(volume > currentVolume) newMetadata.volumes.append( - VolumeMetadata.init( - volume: MetaValue.init(main: volume, bonus: nil), name: nil, + VolumeMetadata( + volume: MetaValue(main: volume, bonus: nil), name: nil, chapters: [ - ChapterMetadata.init( - chapter: MetaValue.init( - main: chapter.0, bonus: chapter.1), + ChapterMetadata( + chapter: MetaValue( + main: chapter.0, bonus: chapter.1 + ), name: "", images: [ - ImageMetadata.init( + ImageMetadata( doublePage: doublePage, fileName: fileName, - firstPage: page) - ]) - ])) + firstPage: page + ), + ] + ), + ] + )) } else if chapter != currentChapter { newMetadata.chapter_count += 1 if chapter.0 == currentChapter.0 { @@ -372,37 +377,43 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { assert(chapter.0 == currentChapter.0 + 1) } newMetadata.volumes[newMetadata.volumes.count - 1].chapters.append( - ChapterMetadata.init( - chapter: MetaValue.init(main: chapter.0, bonus: chapter.1), + ChapterMetadata( + chapter: MetaValue(main: chapter.0, bonus: chapter.1), name: "", images: [ - ImageMetadata.init( + ImageMetadata( doublePage: doublePage, fileName: fileName, - firstPage: page) - ])) + firstPage: page + ), + ] + )) } else { newMetadata.volumes[newMetadata.volumes.count - 1].chapters[ newMetadata.volumes[newMetadata.volumes.count - 1].chapters.count - 1 ].images.append( - ImageMetadata.init( - doublePage: doublePage, fileName: fileName, firstPage: page) + ImageMetadata( + doublePage: doublePage, fileName: fileName, firstPage: page + ) ) } } else { newMetadata.chapter_count += 1 newMetadata.volumes.append( - VolumeMetadata.init( - volume: MetaValue.init(main: volume, bonus: nil), name: nil, + VolumeMetadata( + volume: MetaValue(main: volume, bonus: nil), name: nil, chapters: [ - ChapterMetadata.init( - chapter: MetaValue.init(main: chapter.0, bonus: chapter.1), + ChapterMetadata( + chapter: MetaValue(main: chapter.0, bonus: chapter.1), name: "", images: [ - ImageMetadata.init( + ImageMetadata( doublePage: doublePage, fileName: fileName, - firstPage: page) - ]) - ])) + firstPage: page + ), + ] + ), + ] + )) } currentVolume = volume currentChapter = chapter @@ -434,20 +445,23 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { if screenSize.width > screenSize.height { scrollOffset.y *= (screenSize.height / screenSize.width) } - var newProgress: ReadProgress = - ReadProgress.leftToRight( - volumeIndex: progress.v, chapterIndex: progress.c, - imageIndex: progress.i) + var newProgress = + ReadProgress.leftToRight( + volumeIndex: progress.v, chapterIndex: progress.c, + imageIndex: progress.i + ) switch mode { - case .leftToRight: - newProgress = ReadProgress.leftToRight( - volumeIndex: progress.v, chapterIndex: progress.c, - imageIndex: progress.i) - case .rightToLeft: newProgress = ReadProgress.rightToLeft( - volumeIndex: progress.v, chapterIndex: progress.c, - imageIndex: progress.i) - case .scroll: newProgress = ReadProgress.scroll(scrollOffset) + case .leftToRight: + newProgress = ReadProgress.leftToRight( + volumeIndex: progress.v, chapterIndex: progress.c, + imageIndex: progress.i + ) + case .rightToLeft: newProgress = ReadProgress.rightToLeft( + volumeIndex: progress.v, chapterIndex: progress.c, + imageIndex: progress.i + ) + case .scroll: newProgress = ReadProgress.scroll(scrollOffset) } queue.async { do { @@ -465,23 +479,23 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { func loadLocalState() { do { - let json = Data( - try String( + let json = try Data( + String( contentsOfFile: currentPath.appendingPathComponent("state.json").path ).utf8) let local = try JSONDecoder().decode(LocalState.self, from: json) switch local.progress { - case .leftToRight(let volumeIndex, let chapterIndex, let imageIndex): + case let .leftToRight(volumeIndex, chapterIndex, imageIndex): progress.v = volumeIndex progress.c = chapterIndex progress.i = imageIndex mode = .leftToRight - case .rightToLeft(let volumeIndex, let chapterIndex, let imageIndex): + case let .rightToLeft(volumeIndex, chapterIndex, imageIndex): progress.v = volumeIndex progress.c = chapterIndex progress.i = imageIndex mode = .rightToLeft - case .scroll(let point): + case let .scroll(point): if scrollPos == nil { scrollPos = point let screenSize = UIScreen.main.bounds.size @@ -491,13 +505,13 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { } if let indexPath = scrollingCollectionView.indexPathForItem(at: scrollOffset) { var theProgress = ProgressIndices(v: 0, c: 0, i: 0) - for _ in 0...indexPath.item { + for _ in 0 ... indexPath.item { theProgress = getProgressIndicesFromTurn( - turn: .next, progress: theProgress) + turn: .next, progress: theProgress + ) } progress = theProgress } - } mode = .scroll } @@ -521,11 +535,12 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { setupTapZones() } - func scrollViewDidScroll(_ scrollView: UIScrollView) { + func scrollViewDidScroll(_: UIScrollView) { if scrollingCollectionView.isHidden { return } let centerPoint = CGPoint( x: scrollingCollectionView.bounds.midX + scrollingCollectionView.contentOffset.x, - y: scrollingCollectionView.bounds.midY + scrollingCollectionView.contentOffset.y) + y: scrollingCollectionView.bounds.midY + scrollingCollectionView.contentOffset.y + ) if let indexPath = scrollingCollectionView.indexPathForItem(at: centerPoint) { let (chapter, page) = chapterAndPages[indexPath.item] @@ -544,7 +559,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { { return } - if !hasSetContentOffset && scrollPos != nil { + if !hasSetContentOffset, scrollPos != nil { let screenSize = UIScreen.main.bounds.size var offset = scrollPos! if screenSize.width > screenSize.height { @@ -597,17 +612,17 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { var count = 0 if let enumerator = fileManager.enumerator( at: currentPath, includingPropertiesForKeys: [.isDirectoryKey], - options: [.skipsHiddenFiles]) - { + options: [.skipsHiddenFiles] + ) { for case let dir as URL in enumerator { do { if let enumerator = fileManager.enumerator( at: dir, includingPropertiesForKeys: [.isRegularFileKey], - options: [.skipsHiddenFiles]) - { + options: [.skipsHiddenFiles] + ) { for case let file as URL in enumerator { let resourceValues = try file.resourceValues(forKeys: [ - .isRegularFileKey + .isRegularFileKey, ]) if resourceValues.isRegularFile == true { count += 1 @@ -628,14 +643,16 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { layout.minimumInteritemSpacing = 0 layout.minimumLineSpacing = 0 scrollingCollectionView = UICollectionView( - frame: view.bounds, collectionViewLayout: layout) + frame: view.bounds, collectionViewLayout: layout + ) scrollingCollectionView.translatesAutoresizingMaskIntoConstraints = false scrollingCollectionView.dataSource = self scrollingCollectionView.delegate = self scrollingCollectionView.backgroundColor = .white scrollingCollectionView.isHidden = true scrollingCollectionView.register( - ScrollingImageCell.self, forCellWithReuseIdentifier: "ScrollingImageCell") + ScrollingImageCell.self, forCellWithReuseIdentifier: "ScrollingImageCell" + ) readerView.addSubview(scrollingCollectionView) NSLayoutConstraint.activate([ @@ -949,8 +966,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { } func gestureRecognizer( - _ gestureRecognizer: UIGestureRecognizer, - shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer + _: UIGestureRecognizer, + shouldRecognizeSimultaneouslyWith _: UIGestureRecognizer ) -> Bool { return true } @@ -965,13 +982,13 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { } } - func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + func scrollViewDidEndDecelerating(_: UIScrollView) { if scrollingCollectionView.isHidden { return } updateInfo() saveLocalState() } - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + func scrollViewDidEndDragging(_: UIScrollView, willDecelerate decelerate: Bool) { if scrollingCollectionView.isHidden { return } if !decelerate { updateInfo() @@ -1044,8 +1061,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { let scaling = UIView.ContentMode.scaleAspectFit var newProgress = progress newProgress = getProgressIndicesFromTurn(turn: turn, progress: newProgress) - if newProgress.v == progress.v && newProgress.c == progress.c && newProgress.i == progress.i - { + if newProgress.v == progress.v, newProgress.c == progress.c, newProgress.i == progress.i { return } progress = newProgress @@ -1058,7 +1074,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { return } - for _ in 0...preloadCount { + for _ in 0 ... preloadCount { newProgress = getProgressIndicesFromTurn(turn: .next, progress: newProgress) if let path = getImagePath(progress: newProgress) { imageLoader.preloadImage(at: path, scaling: scaling) @@ -1106,7 +1122,8 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { metadata.volumes[progress.v].chapters[lastChapterIndex].images.count - 1 let lastImage = metadata.volumes[progress.v].chapters[lastChapterIndex].images[ - lastImageIndex] + lastImageIndex + ] var lastPageString = "" if lastImage.doublePage { lastPageString = String(lastImage.firstPage + 1) @@ -1150,37 +1167,39 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { switch turn { case .next: if metadata.volumes[v].chapters[c].images.count > i + 1 { - return ProgressIndices.init(v: v, c: c, i: i + 1) + return ProgressIndices(v: v, c: c, i: i + 1) } if metadata.volumes[v].chapters.count > c + 1 { - return ProgressIndices.init(v: v, c: c + 1, i: 0) + return ProgressIndices(v: v, c: c + 1, i: 0) } if metadata.volumes.count > v + 1 { - return ProgressIndices.init(v: v + 1, c: 0, i: 0) + return ProgressIndices(v: v + 1, c: 0, i: 0) } case .previous: if i > 0 { - return ProgressIndices.init(v: v, c: c, i: i - 1) + return ProgressIndices(v: v, c: c, i: i - 1) } if c > 0 { - return ProgressIndices.init( - v: v, c: c - 1, i: metadata.volumes[v].chapters[c - 1].images.count - 1) + return ProgressIndices( + v: v, c: c - 1, i: metadata.volumes[v].chapters[c - 1].images.count - 1 + ) } if v > 0 { - return ProgressIndices.init( + return ProgressIndices( v: v - 1, c: metadata.volumes[v - 1].chapters.count - 1, i: metadata.volumes[v - 1].chapters[ metadata.volumes[v - 1].chapters.count - 1 - ].images.count - 1) + ].images.count - 1 + ) } } - return ProgressIndices.init(v: v, c: c, i: i) + return ProgressIndices(v: v, c: c, i: i) } func getMetadata(path: URL) -> Metadata? { do { - let json = Data( - try String(contentsOfFile: path.appendingPathComponent("metadata.json").path) + let json = try Data( + String(contentsOfFile: path.appendingPathComponent("metadata.json").path) .utf8) let metadata = try JSONDecoder().decode(Metadata.self, from: json) return metadata @@ -1193,14 +1212,14 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { return nil } - func setImages(path: URL) { + func setImages(path _: URL) { let scaling: UIView.ContentMode! - if mode == .scroll { - scaling = UIView.ContentMode.scaleAspectFill - } else { scaling = UIView.ContentMode.scaleAspectFit } + if mode == .scroll { + scaling = UIView.ContentMode.scaleAspectFill + } else { scaling = UIView.ContentMode.scaleAspectFit } imageLoader.loadImage( at: - getImagePath(progress: progress), + getImagePath(progress: progress), scaling: scaling ) { [weak self] image in self?.imageView.image = image @@ -1208,7 +1227,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate { } var newProgress = progress - for _ in 0...preloadCount { + for _ in 0 ... preloadCount { newProgress = getProgressIndicesFromTurn(turn: .next, progress: newProgress) imageLoader.preloadImage(at: getImagePath(progress: newProgress), scaling: scaling) } @@ -1245,8 +1264,8 @@ func getGlobalState() -> GlobalState { let fileManager = FileManager.default if let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first { do { - let json = Data( - try String( + let json = try Data( + String( contentsOfFile: documentsURL.appendingPathComponent("state.json").path ).utf8) return try JSONDecoder().decode(GlobalState.self, from: json) @@ -1268,9 +1287,8 @@ func getDocumentsURL() -> URL? { } extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { - func collectionView( - _ collectionView: UICollectionView, numberOfItemsInSection section: Int + _ collectionView: UICollectionView, numberOfItemsInSection _: Int ) -> Int { @@ -1287,7 +1305,8 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl if collectionView == comicCollectionView { let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: "ComicImageCell", for: indexPath) + withReuseIdentifier: "ComicImageCell", for: indexPath + ) as! ComicImageCell cell.imageView.image = comics[indexPath.item].cover return cell @@ -1295,7 +1314,8 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl // let scaling = UIView.ContentMode.scaleAspectFill let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: "ScrollingImageCell", for: indexPath) + withReuseIdentifier: "ScrollingImageCell", for: indexPath + ) as! ScrollingImageCell if metadata == nil { return cell @@ -1303,20 +1323,21 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl // TODO: Fix scrolling again return cell } else { - assert(false) + assertionFailure() } // Xcode profiling sucks: return collectionView.dequeueReusableCell( - withReuseIdentifier: "ScrollingImageCell", for: indexPath) + withReuseIdentifier: "ScrollingImageCell", for: indexPath + ) as! ScrollingImageCell } func collectionView( _ collectionView: UICollectionView, - layout collectionViewLayout: UICollectionViewLayout, - sizeForItemAt indexPath: IndexPath + layout _: UICollectionViewLayout, + sizeForItemAt _: IndexPath ) -> CGSize { if collectionView == comicCollectionView { let spacing: CGFloat = 8 @@ -1331,7 +1352,7 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl // TODO: Fix scrolling again return CGSize() } else { - assert(false) + assertionFailure() } // Xcode profiling sucks: return CGSize() @@ -1367,7 +1388,8 @@ class ScrollingImageCell: UICollectionViewCell { ]) } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } } @@ -1390,7 +1412,8 @@ class ComicImageCell: UICollectionViewCell { ]) } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } } @@ -1403,7 +1426,7 @@ enum Rotation { class ImageLoader { private let cache = NSCache() private let horCache = NSCache() - private var loadingTasks: [String: [((UIImage?) -> Void)]] = [:] + private var loadingTasks: [String: [(UIImage?) -> Void]] = [:] var size: [String: CGSize] = [:] init() { @@ -1421,9 +1444,9 @@ class ImageLoader { ) { let screenSize = UIScreen.main.bounds.size let rotation: Rotation! - if screenSize.width > screenSize.height { - rotation = Rotation.horizontal - } else { rotation = Rotation.vertical } + if screenSize.width > screenSize.height { + rotation = Rotation.horizontal + } else { rotation = Rotation.vertical } if rotation == .vertical { if let cached = cache.object(forKey: path.path as NSString) { @@ -1455,9 +1478,11 @@ class ImageLoader { if completion == nil || rotation == .vertical { let vertical = CGSize( width: min(screenSize.width, screenSize.height), - height: max(screenSize.height, screenSize.width)) + height: max(screenSize.height, screenSize.width) + ) let vScaledSize = aspectSize( - for: image.size, in: vertical, scaling: scaling) + for: image.size, in: vertical, scaling: scaling + ) let vScaleImage = resizeImage(image, to: vScaledSize) guard let cost = imageByteSize(vScaleImage) else { return } self.cache.setObject(vScaleImage, forKey: path.path as NSString, cost: cost) @@ -1469,9 +1494,11 @@ class ImageLoader { if completion == nil || rotation == .horizontal { let horizontal = CGSize( width: max(screenSize.width, screenSize.height), - height: min(screenSize.height, screenSize.width)) + height: min(screenSize.height, screenSize.width) + ) let hScaledSize = aspectSize( - for: image.size, in: horizontal, scaling: scaling) + for: image.size, in: horizontal, scaling: scaling + ) let hScaleImage = resizeImage(image, to: hScaledSize) guard let cost = imageByteSize(hScaleImage) else { return } self.horCache.setObject(hScaleImage, forKey: path.path as NSString, cost: cost)