diff --git a/ExportOptions.plist b/ExportOptions.plist
index 942bad7..1822c54 100644
--- a/ExportOptions.plist
+++ b/ExportOptions.plist
@@ -3,11 +3,15 @@
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
- uploadBitcode
-
- uploadSymbols
+ method
+ debugging
+ signingStyle
+ automatic
+ destination
+ export
+ stripSwiftSymbols
compileBitcode
-
+
diff --git a/ImageViewer/ViewController.swift b/ImageViewer/ViewController.swift
index b792d5c..a39bfcf 100644
--- a/ImageViewer/ViewController.swift
+++ b/ImageViewer/ViewController.swift
@@ -1,11 +1,13 @@
//TODO: Implement support for bonus chapters
//TODO: Implement Anilist support?
//TODO: Properly avoid swallowing of input from UICollectionView used for scrolling
-//TODO: Fix UICollectionView sizeForItemAt method performance either by caching or lazy loading
+//TODO: Convert between state for normal and scrolling page turn
import UIKit
-let preloadCount = 3
+let preloadCount = 2
+// With a whopping 2 cores, and 2 threads because no hyperthreading, it makes sense to only have a queue as an extra thread to do work with to allow the main thread for ui.
+let queue = DispatchQueue(label: "queue", qos: .utility)
enum PageTurn {
case next
@@ -69,6 +71,9 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
var scrollPos: CGPoint!
var hasSetContentOffset = false
var pageCount = 0
+ var pagesAvailable = 0
+ var sizeList: [CGSize] = []
+ var chapterAndPages: [(Int, Int)] = []
var imageView = UIImageView()
var mode = PageTurnMode.leftToRight
@@ -106,12 +111,10 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
let imageLoader = ImageLoader.init()
- let localStateQueue = DispatchQueue(label: "state.local.queue", qos: .background)
-
override func viewDidLoad() {
super.viewDidLoad()
- view.backgroundColor = .clear
+ view.backgroundColor = .white
setup()
}
@@ -249,10 +252,10 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
case .leftToRight:
ReadProgress.leftToRight(chapter: self.currentChapter, page: self.currentPage)
case .rightToLeft:
- ReadProgress.leftToRight(chapter: self.currentChapter, page: self.currentPage)
+ ReadProgress.rightToLeft(chapter: self.currentChapter, page: self.currentPage)
case .scroll: ReadProgress.scroll(scrollingCollectionView.contentOffset)
}
- localStateQueue.async {
+ queue.async {
do {
try JSONEncoder().encode(
LocalState(
@@ -285,6 +288,17 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
case .scroll(let point):
if self.scrollPos == nil {
self.scrollPos = point
+ let centerPoint = CGPoint(
+ x: scrollingCollectionView.bounds.midX
+ + scrollingCollectionView.contentOffset.x,
+ y: scrollingCollectionView.bounds.midY
+ + scrollingCollectionView.contentOffset.y)
+
+ if let indexPath = scrollingCollectionView.indexPathForItem(at: centerPoint) {
+ let (chapter, page) = chapterAndPages[indexPath.item]
+ currentChapter = chapter
+ currentPage = page
+ }
}
self.mode = .scroll
}
@@ -313,13 +327,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
y: scrollingCollectionView.bounds.midY + scrollingCollectionView.contentOffset.y)
if let indexPath = scrollingCollectionView.indexPathForItem(at: centerPoint) {
- var index = 0
- var (chapter, page) = (1, 1)
- while index < indexPath.item {
- (chapter, page) = getChapterAndPageFromTurn(
- chapter: chapter, page: page, turn: .next)
- index += 1
- }
+ let (chapter, page) = chapterAndPages[indexPath.item]
currentChapter = chapter
currentPage = page
}
@@ -330,7 +338,9 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
- if scrollingCollectionView.isHidden == false && mode == .scroll && !hasSetContentOffset {
+ if scrollingCollectionView.isHidden == false && mode == .scroll
+ && !hasSetContentOffset
+ {
scrollingCollectionView.setContentOffset(scrollPos, animated: false)
hasSetContentOffset = true
}
@@ -343,7 +353,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
if let path = getPathFromComicName(name: name) {
currentPath = path
metadata = getMetadata(path: path)!
- pageCount = metadata.chapters.map { $0.pages }.reduce(0, +)
+
globalState.comicName = metadata.title
saveGlobalState()
loadLocalState()
@@ -353,14 +363,80 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
rightView.isHidden = false
setImages(path: path, metadata: metadata)
} else {
- leftView.isHidden = true
- rightView.isHidden = true
- scrollingCollectionView.isHidden = false
- scrollingCollectionView.reloadData()
+ setupScrolling()
}
}
}
+ func setupScrolling() {
+ leftView.isHidden = true
+ rightView.isHidden = true
+ scrollingCollectionView.isHidden = false
+ pagesAvailable = countFiles()
+ var index = 0
+ var (chapter, page) = (1, 1)
+ chapterAndPages = Array(repeating: (0, 0), count: pagesAvailable + 1)
+ chapterAndPages[index] = (chapter, page)
+ while index < pagesAvailable {
+ index += 1
+ (chapter, page) = getChapterAndPageFromTurn(
+ chapter: chapter, page: page, turn: .next)
+ chapterAndPages[index] = (chapter, page)
+ }
+
+ sizeList = Array(repeating: CGSize(), count: pagesAvailable)
+ let binPath = currentPath.appendingPathComponent("size.bin")
+ let decoder = PropertyListDecoder()
+ do {
+ let data = try Data(contentsOf: binPath)
+ sizeList = try decoder.decode([CGSize].self, from: data)
+ } catch {
+ print("Failed to read data:", error)
+ }
+
+ scrollingCollectionView.reloadData()
+ DispatchQueue.main.async {
+ let encoder = PropertyListEncoder()
+ encoder.outputFormat = .binary
+ do {
+ let data = try encoder.encode(self.sizeList)
+ try data.write(to: binPath)
+ } catch {
+ print("Encoding or writing failed:", error)
+ }
+ }
+ }
+
+ func countFiles() -> Int {
+ var count = 0
+ if let enumerator = fileManager.enumerator(
+ at: currentPath, includingPropertiesForKeys: [.isDirectoryKey],
+ options: [.skipsHiddenFiles])
+ {
+ for case let dir as URL in enumerator {
+ do {
+ if let enumerator = fileManager.enumerator(
+ at: dir, includingPropertiesForKeys: [.isRegularFileKey],
+ options: [.skipsHiddenFiles])
+ {
+ for case let file as URL in enumerator {
+ let resourceValues = try file.resourceValues(forKeys: [
+ .isRegularFileKey
+ ])
+ if resourceValues.isRegularFile == true {
+ count += 1
+ }
+ }
+ }
+
+ } catch {
+ print("Error reading file attributes for \(dir):", error)
+ }
+ }
+ }
+ return count
+ }
+
func setupScrollingCollectionView() {
let layout = UICollectionViewFlowLayout()
layout.minimumInteritemSpacing = 0
@@ -368,7 +444,6 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
scrollingCollectionView = UICollectionView(
frame: view.bounds, collectionViewLayout: layout)
scrollingCollectionView.translatesAutoresizingMaskIntoConstraints = false
- scrollingCollectionView.isUserInteractionEnabled = true
scrollingCollectionView.dataSource = self
scrollingCollectionView.delegate = self
scrollingCollectionView.backgroundColor = .white
@@ -383,7 +458,6 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
scrollingCollectionView.trailingAnchor.constraint(equalTo: readerView.trailingAnchor),
scrollingCollectionView.heightAnchor.constraint(equalTo: readerView.heightAnchor),
])
-
}
func setupTapZones() {
@@ -639,16 +713,28 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
@objc func handlePageTurnOption(_ sender: UIButton) {
if let title = sender.currentTitle {
+ let prev = mode
switch title.lowercased() {
case "left to right": mode = .leftToRight
case "right to left": mode = .rightToLeft
- case "scroll":
- if mode != .scroll {
- scrollingCollectionView.reloadData()
- }
- mode = .scroll
+ case "scroll": mode = .scroll
default: break
}
+ if prev == mode { return }
+ if mode == .scroll {
+ setupScrolling()
+ } else {
+ imageLoader.loadImage(
+ at: getImagePath(
+ chapter: currentChapter,
+ volume: Int(metadata.chapters[currentChapter - 1].volume),
+ page: currentPage,
+ path: currentPath)
+ ) {
+ [weak self] image in
+ self?.imageView.image = image
+ }
+ }
}
scrollingCollectionView.isHidden = mode != .scroll
leftView.isHidden = mode == .scroll
@@ -824,7 +910,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
if let chapterTitle = metadata.chapters[currentChapter - 1].title {
text =
- text + "\(chapterTitle)"
+ text + "\(chapterTitle)\n"
}
text =
text + """
@@ -1002,7 +1088,7 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
if collectionView == comicCollectionView {
return comics.count
} else {
- return pageCount
+ return pagesAvailable
}
}
@@ -1024,20 +1110,26 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
if metadata == nil {
return cell
}
- var index = 0
- var (chapter, page) = (1, 1)
- while index < indexPath.item {
- (chapter, page) = getChapterAndPageFromTurn(
- chapter: chapter, page: page, turn: .next)
- index += 1
- }
+ var (chapter, page) = chapterAndPages[indexPath.item]
if let url = getImagePath(
chapter: chapter,
volume: Int(metadata.chapters[chapter - 1].volume),
page: page, path: currentPath)
{
- // cell.imageView.image = UIImage(contentsOfFile: url.path)
imageLoader.loadImage(at: url) { image in cell.imageView.image = image }
+ for _ in 0...preloadCount {
+ (chapter, page) = getChapterAndPageFromTurn(
+ chapter: chapter, page: page, turn: .next)
+ if let url = getImagePath(
+ chapter: chapter,
+ volume: Int(metadata.chapters[chapter - 1].volume),
+ page: page, path: currentPath)
+ {
+ imageLoader.preloadImage(at: url)
+ } else {
+ break
+ }
+ }
}
return cell
} else {
@@ -1066,13 +1158,13 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
if metadata == nil {
return CGSize()
}
- var index = 0
- var (chapter, page) = (1, 1)
- while index < indexPath.item {
- (chapter, page) = getChapterAndPageFromTurn(
- chapter: chapter, page: page, turn: .next)
- index += 1
+ if sizeList[indexPath.item] != CGSize(width: 0, height: 0) {
+ return CGSize(
+ width: readerView.bounds.width,
+ height: sizeList[indexPath.item].height * readerView.bounds.width
+ / sizeList[indexPath.item].width)
}
+ let (chapter, page) = chapterAndPages[indexPath.item]
if let imagePath = getImagePath(
chapter: chapter,
volume: Int(metadata.chapters[chapter - 1].volume),
@@ -1080,11 +1172,15 @@ extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFl
let imageSource = CGImageSourceCreateWithURL(imagePath as CFURL, nil),
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil)
as? [CFString: Any],
- let height = imageProperties[kCGImagePropertyPixelHeight] as? CGFloat
+ let height = imageProperties[kCGImagePropertyPixelHeight] as? CGFloat,
+ let width = imageProperties[kCGImagePropertyPixelWidth] as? CGFloat
{
- return CGSize(width: readerView.bounds.width, height: height)
+ sizeList[indexPath.item] = CGSize(width: width, height: height)
+ return CGSize(
+ width: readerView.bounds.width, height: height * readerView.bounds.width / width
+ )
}
- return CGSize(width: readerView.bounds.width, height: readerView.bounds.height)
+ return CGSize()
} else {
assert(false)
}
@@ -1152,7 +1248,6 @@ class ComicImageCell: UICollectionViewCell {
class ImageLoader {
private let cache = NSCache()
private var loadingTasks: [String: [((UIImage?) -> Void)]] = [:]
- private let queue = DispatchQueue(label: "image.loading.queue", qos: .userInitiated)
init() {
cache.totalCostLimit = 50 * 1024 * 1024