scrolling wip with perf problems
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
//TODO: Implement support for bonus chapters
|
//TODO: Implement support for bonus chapters
|
||||||
//TODO: Implement scrolling support
|
|
||||||
//TODO: Implement Anilist support?
|
//TODO: Implement Anilist support?
|
||||||
//TODO: Evict the oldest image from cache
|
//TODO: Properly avoid swallowing of input from UICollectionView used for scrolling
|
||||||
|
//TODO: Fix UICollectionView sizeForItemAt method performance either by caching or lazy loading
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@@ -12,6 +12,12 @@ enum PageTurn {
|
|||||||
case previous
|
case previous
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ReadProgress: Codable {
|
||||||
|
case leftToRight(chapter: Int, page: Int)
|
||||||
|
case rightToLeft(chapter: Int, page: Int)
|
||||||
|
case scroll(CGPoint)
|
||||||
|
}
|
||||||
|
|
||||||
enum PageTurnMode: Codable {
|
enum PageTurnMode: Codable {
|
||||||
case leftToRight
|
case leftToRight
|
||||||
case rightToLeft
|
case rightToLeft
|
||||||
@@ -29,9 +35,7 @@ struct GlobalState: Codable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct LocalState: Codable {
|
struct LocalState: Codable {
|
||||||
var chapter: Int
|
var progress: ReadProgress
|
||||||
var page: Int
|
|
||||||
var turnMode: PageTurnMode = .leftToRight
|
|
||||||
var backgroundColor: String = "black"
|
var backgroundColor: String = "black"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +57,7 @@ struct Metadata: Decodable {
|
|||||||
struct ChapterMetadata: Decodable {
|
struct ChapterMetadata: Decodable {
|
||||||
var chapter: Float
|
var chapter: Float
|
||||||
var volume: Float
|
var volume: Float
|
||||||
var title: String
|
var title: String?
|
||||||
var pages: Int
|
var pages: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +65,11 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
var homeView = UIView()
|
var homeView = UIView()
|
||||||
var readerView = UIView()
|
var readerView = UIView()
|
||||||
|
|
||||||
|
@IBOutlet var scrollingCollectionView: UICollectionView!
|
||||||
|
var scrollPos: CGPoint!
|
||||||
|
var hasSetContentOffset = false
|
||||||
|
var pageCount = 0
|
||||||
|
|
||||||
var imageView = UIImageView()
|
var imageView = UIImageView()
|
||||||
var mode = PageTurnMode.leftToRight
|
var mode = PageTurnMode.leftToRight
|
||||||
var metadata: Metadata!
|
var metadata: Metadata!
|
||||||
@@ -71,6 +80,9 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
var leftTap: UITapGestureRecognizer!
|
var leftTap: UITapGestureRecognizer!
|
||||||
var rightTap: UITapGestureRecognizer!
|
var rightTap: UITapGestureRecognizer!
|
||||||
var topTap: UITapGestureRecognizer!
|
var topTap: UITapGestureRecognizer!
|
||||||
|
let leftView = UIView()
|
||||||
|
let rightView = UIView()
|
||||||
|
let topView = UIView()
|
||||||
|
|
||||||
let topBarView = UIView()
|
let topBarView = UIView()
|
||||||
let bottomBarView = UIView()
|
let bottomBarView = UIView()
|
||||||
@@ -90,7 +102,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
let documentsURL = getDocumentsURL().unsafelyUnwrapped
|
let documentsURL = getDocumentsURL().unsafelyUnwrapped
|
||||||
|
|
||||||
var comics: [Comic] = []
|
var comics: [Comic] = []
|
||||||
var collectionView: UICollectionView!
|
@IBOutlet var comicCollectionView: UICollectionView!
|
||||||
|
|
||||||
let imageLoader = ImageLoader.init()
|
let imageLoader = ImageLoader.init()
|
||||||
|
|
||||||
@@ -133,6 +145,7 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
])
|
])
|
||||||
loadComics()
|
loadComics()
|
||||||
setupImageView()
|
setupImageView()
|
||||||
|
setupScrollingCollectionView()
|
||||||
setupGestures()
|
setupGestures()
|
||||||
setupBar()
|
setupBar()
|
||||||
setupHomeView()
|
setupHomeView()
|
||||||
@@ -147,13 +160,14 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
layout.minimumInteritemSpacing = spacing
|
layout.minimumInteritemSpacing = spacing
|
||||||
layout.minimumLineSpacing = spacing
|
layout.minimumLineSpacing = spacing
|
||||||
|
|
||||||
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
|
comicCollectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
|
||||||
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
comicCollectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
collectionView.dataSource = self
|
comicCollectionView.dataSource = self
|
||||||
collectionView.delegate = self
|
comicCollectionView.delegate = self
|
||||||
collectionView.backgroundColor = .white
|
comicCollectionView.backgroundColor = .white
|
||||||
collectionView.register(ImageCell.self, forCellWithReuseIdentifier: "ImageCell")
|
comicCollectionView.register(
|
||||||
homeView.addSubview(collectionView)
|
ComicImageCell.self, forCellWithReuseIdentifier: "ComicImageCell")
|
||||||
|
homeView.addSubview(comicCollectionView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadComics() {
|
func loadComics() {
|
||||||
@@ -230,13 +244,20 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
|
|
||||||
func saveLocalState() {
|
func saveLocalState() {
|
||||||
let color = readerView.backgroundColor ?? .black
|
let color = readerView.backgroundColor ?? .black
|
||||||
|
let progress: ReadProgress =
|
||||||
|
switch self.mode {
|
||||||
|
case .leftToRight:
|
||||||
|
ReadProgress.leftToRight(chapter: self.currentChapter, page: self.currentPage)
|
||||||
|
case .rightToLeft:
|
||||||
|
ReadProgress.leftToRight(chapter: self.currentChapter, page: self.currentPage)
|
||||||
|
case .scroll: ReadProgress.scroll(scrollingCollectionView.contentOffset)
|
||||||
|
}
|
||||||
localStateQueue.async {
|
localStateQueue.async {
|
||||||
do {
|
do {
|
||||||
try JSONEncoder().encode(
|
try JSONEncoder().encode(
|
||||||
LocalState(
|
LocalState(
|
||||||
chapter: self.currentChapter, page: self.currentPage, turnMode: self.mode,
|
progress: progress, backgroundColor: self.convertColorToString(color: color)
|
||||||
backgroundColor: self.convertColorToString(
|
)
|
||||||
color: color))
|
|
||||||
).write(
|
).write(
|
||||||
to: self.currentPath.appendingPathComponent("state.json"))
|
to: self.currentPath.appendingPathComponent("state.json"))
|
||||||
} catch {
|
} catch {
|
||||||
@@ -252,9 +273,21 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
contentsOfFile: currentPath.appendingPathComponent("state.json").path
|
contentsOfFile: currentPath.appendingPathComponent("state.json").path
|
||||||
).utf8)
|
).utf8)
|
||||||
let local = try JSONDecoder().decode(LocalState.self, from: json)
|
let local = try JSONDecoder().decode(LocalState.self, from: json)
|
||||||
currentPage = local.page
|
switch local.progress {
|
||||||
currentChapter = local.chapter
|
case .leftToRight(let chapter, let page):
|
||||||
mode = local.turnMode
|
self.currentChapter = chapter
|
||||||
|
self.currentPage = page
|
||||||
|
self.mode = .leftToRight
|
||||||
|
case .rightToLeft(let chapter, let page):
|
||||||
|
self.currentChapter = chapter
|
||||||
|
self.currentPage = page
|
||||||
|
self.mode = .rightToLeft
|
||||||
|
case .scroll(let point):
|
||||||
|
if self.scrollPos == nil {
|
||||||
|
self.scrollPos = point
|
||||||
|
}
|
||||||
|
self.mode = .scroll
|
||||||
|
}
|
||||||
readerView.backgroundColor = convertStringToColor(str: local.backgroundColor)
|
readerView.backgroundColor = convertStringToColor(str: local.backgroundColor)
|
||||||
} catch {
|
} catch {
|
||||||
print("failed to load local state")
|
print("failed to load local state")
|
||||||
@@ -273,6 +306,36 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
setupTapZones()
|
setupTapZones()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
if scrollingCollectionView.isHidden { return }
|
||||||
|
let centerPoint = CGPoint(
|
||||||
|
x: scrollingCollectionView.bounds.midX + scrollingCollectionView.contentOffset.x,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
currentChapter = chapter
|
||||||
|
currentPage = page
|
||||||
|
}
|
||||||
|
if scrollingCollectionView.isHidden == false {
|
||||||
|
saveLocalState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLayoutSubviews() {
|
||||||
|
super.viewDidLayoutSubviews()
|
||||||
|
if scrollingCollectionView.isHidden == false && mode == .scroll && !hasSetContentOffset {
|
||||||
|
scrollingCollectionView.setContentOffset(scrollPos, animated: false)
|
||||||
|
hasSetContentOffset = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func readComic(name: String) {
|
func readComic(name: String) {
|
||||||
readerView.isHidden = false
|
readerView.isHidden = false
|
||||||
homeView.isHidden = true
|
homeView.isHidden = true
|
||||||
@@ -280,18 +343,50 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
if let path = getPathFromComicName(name: name) {
|
if let path = getPathFromComicName(name: name) {
|
||||||
currentPath = path
|
currentPath = path
|
||||||
metadata = getMetadata(path: path)!
|
metadata = getMetadata(path: path)!
|
||||||
|
pageCount = metadata.chapters.map { $0.pages }.reduce(0, +)
|
||||||
globalState.comicName = metadata.title
|
globalState.comicName = metadata.title
|
||||||
saveGlobalState()
|
saveGlobalState()
|
||||||
loadLocalState()
|
loadLocalState()
|
||||||
setImages(path: path, metadata: metadata)
|
if mode != .scroll {
|
||||||
|
scrollingCollectionView.isHidden = true
|
||||||
|
leftView.isHidden = false
|
||||||
|
rightView.isHidden = false
|
||||||
|
setImages(path: path, metadata: metadata)
|
||||||
|
} else {
|
||||||
|
leftView.isHidden = true
|
||||||
|
rightView.isHidden = true
|
||||||
|
scrollingCollectionView.isHidden = false
|
||||||
|
scrollingCollectionView.reloadData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupTapZones() {
|
func setupScrollingCollectionView() {
|
||||||
let leftView = UIView()
|
let layout = UICollectionViewFlowLayout()
|
||||||
let rightView = UIView()
|
layout.minimumInteritemSpacing = 0
|
||||||
let topView = UIView()
|
layout.minimumLineSpacing = 0
|
||||||
|
scrollingCollectionView = UICollectionView(
|
||||||
|
frame: view.bounds, collectionViewLayout: layout)
|
||||||
|
scrollingCollectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
scrollingCollectionView.isUserInteractionEnabled = true
|
||||||
|
scrollingCollectionView.dataSource = self
|
||||||
|
scrollingCollectionView.delegate = self
|
||||||
|
scrollingCollectionView.backgroundColor = .white
|
||||||
|
scrollingCollectionView.isHidden = true
|
||||||
|
scrollingCollectionView.register(
|
||||||
|
ScrollingImageCell.self, forCellWithReuseIdentifier: "ScrollingImageCell")
|
||||||
|
readerView.addSubview(scrollingCollectionView)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
scrollingCollectionView.topAnchor.constraint(equalTo: readerView.topAnchor),
|
||||||
|
scrollingCollectionView.leadingAnchor.constraint(equalTo: readerView.leadingAnchor),
|
||||||
|
scrollingCollectionView.trailingAnchor.constraint(equalTo: readerView.trailingAnchor),
|
||||||
|
scrollingCollectionView.heightAnchor.constraint(equalTo: readerView.heightAnchor),
|
||||||
|
])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTapZones() {
|
||||||
leftView.translatesAutoresizingMaskIntoConstraints = false
|
leftView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
rightView.translatesAutoresizingMaskIntoConstraints = false
|
rightView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
topView.translatesAutoresizingMaskIntoConstraints = false
|
topView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
@@ -370,6 +465,10 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
info.textColor = .white
|
info.textColor = .white
|
||||||
bottomBarView.addSubview(info)
|
bottomBarView.addSubview(info)
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
|
info.topAnchor.constraint(equalTo: bottomBarView.topAnchor),
|
||||||
|
info.bottomAnchor.constraint(equalTo: bottomBarView.bottomAnchor),
|
||||||
|
info.leadingAnchor.constraint(equalTo: bottomBarView.leadingAnchor),
|
||||||
|
info.trailingAnchor.constraint(equalTo: bottomBarView.trailingAnchor),
|
||||||
info.centerXAnchor.constraint(equalTo: bottomBarView.centerXAnchor),
|
info.centerXAnchor.constraint(equalTo: bottomBarView.centerXAnchor),
|
||||||
info.centerYAnchor.constraint(equalTo: bottomBarView.centerYAnchor),
|
info.centerYAnchor.constraint(equalTo: bottomBarView.centerYAnchor),
|
||||||
])
|
])
|
||||||
@@ -543,10 +642,17 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
switch title.lowercased() {
|
switch title.lowercased() {
|
||||||
case "left to right": mode = .leftToRight
|
case "left to right": mode = .leftToRight
|
||||||
case "right to left": mode = .rightToLeft
|
case "right to left": mode = .rightToLeft
|
||||||
case "scroll": mode = .scroll
|
case "scroll":
|
||||||
|
if mode != .scroll {
|
||||||
|
scrollingCollectionView.reloadData()
|
||||||
|
}
|
||||||
|
mode = .scroll
|
||||||
default: break
|
default: break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
scrollingCollectionView.isHidden = mode != .scroll
|
||||||
|
leftView.isHidden = mode == .scroll
|
||||||
|
rightView.isHidden = mode == .scroll
|
||||||
togglePageTurnDropdown()
|
togglePageTurnDropdown()
|
||||||
saveLocalState()
|
saveLocalState()
|
||||||
}
|
}
|
||||||
@@ -574,16 +680,23 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
backgroundColorDropdownView.isHidden.toggle()
|
backgroundColorDropdownView.isHidden.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func gestureRecognizer(
|
||||||
|
// _ gestureRecognizer: UIGestureRecognizer,
|
||||||
|
// shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer
|
||||||
|
// ) -> Bool {
|
||||||
|
// if gestureRecognizer == leftTap || gestureRecognizer == rightTap,
|
||||||
|
// otherGestureRecognizer == topTap
|
||||||
|
// {
|
||||||
|
// return true
|
||||||
|
// }
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
|
||||||
func gestureRecognizer(
|
func gestureRecognizer(
|
||||||
_ gestureRecognizer: UIGestureRecognizer,
|
_ gestureRecognizer: UIGestureRecognizer,
|
||||||
shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer
|
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
if gestureRecognizer == leftTap || gestureRecognizer == rightTap,
|
return true
|
||||||
otherGestureRecognizer == topTap
|
|
||||||
{
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleBar() {
|
func toggleBar() {
|
||||||
@@ -596,6 +709,20 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
||||||
|
if scrollingCollectionView.isHidden { return }
|
||||||
|
updateInfo()
|
||||||
|
saveLocalState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||||
|
if scrollingCollectionView.isHidden { return }
|
||||||
|
if !decelerate {
|
||||||
|
updateInfo()
|
||||||
|
}
|
||||||
|
saveLocalState()
|
||||||
|
}
|
||||||
|
|
||||||
func hideBar() {
|
func hideBar() {
|
||||||
topBarView.isHidden = true
|
topBarView.isHidden = true
|
||||||
bottomBarView.isHidden = true
|
bottomBarView.isHidden = true
|
||||||
@@ -690,14 +817,26 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateInfo() {
|
func updateInfo() {
|
||||||
info.text = """
|
if metadata == nil { return }
|
||||||
\(metadata.title)
|
if currentChapter == nil || currentPage == nil { return }
|
||||||
\(metadata.chapters[currentChapter - 1].title)
|
|
||||||
Volume \(Int(metadata.chapters[currentChapter - 1].volume)) of \(Int(metadata.last_volume))
|
var text = "\(metadata.title)\n"
|
||||||
Chapter \(currentChapter!) of \(Int(metadata.last_chapter))
|
|
||||||
Page \(currentPage!) of \(metadata.chapters[currentChapter - 1].pages)
|
if let chapterTitle = metadata.chapters[currentChapter - 1].title {
|
||||||
Image size: \(Int(imageView.image!.size.width))x\(Int(imageView.image!.size.height))
|
text =
|
||||||
"""
|
text + "\(chapterTitle)"
|
||||||
|
}
|
||||||
|
text =
|
||||||
|
text + """
|
||||||
|
Volume \(Int(metadata.chapters[currentChapter - 1].volume)) of \(Int(metadata.last_volume))
|
||||||
|
Chapter \(currentChapter!) of \(Int(metadata.last_chapter))
|
||||||
|
Page \(currentPage!) of \(metadata.chapters[currentChapter - 1].pages)
|
||||||
|
"""
|
||||||
|
if let image = imageView.image {
|
||||||
|
text =
|
||||||
|
text + "\nImage size: \(Int(image.size.width))x\(Int(image.size.height))"
|
||||||
|
}
|
||||||
|
info.text = text
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImagePath(chapter: Int, volume: Int, page: Int, path: URL) -> URL! {
|
func getImagePath(chapter: Int, volume: Int, page: Int, path: URL) -> URL! {
|
||||||
@@ -706,21 +845,6 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
if fileManager.fileExists(atPath: modernPath.path) {
|
if fileManager.fileExists(atPath: modernPath.path) {
|
||||||
return modernPath
|
return modernPath
|
||||||
}
|
}
|
||||||
//TODO: Remove this
|
|
||||||
let newPath = path.appendingPathComponent(String(format: "volume_%04d", volume))
|
|
||||||
.appendingPathComponent(
|
|
||||||
String(format: "chapter_%04d_image_%04d.png", chapter, page - 1))
|
|
||||||
if fileManager.fileExists(atPath: newPath.path) {
|
|
||||||
return newPath
|
|
||||||
}
|
|
||||||
let alternatePath = path.appendingPathComponent(String(format: "volume_%04d", volume))
|
|
||||||
.appendingPathComponent(
|
|
||||||
String(format: "chapter%03d_image_%03d.png", chapter, page - 1))
|
|
||||||
if fileManager.fileExists(atPath: alternatePath.path) {
|
|
||||||
return alternatePath
|
|
||||||
}
|
|
||||||
//
|
|
||||||
print("Did not find image at path")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,12 +946,10 @@ class ViewController: UIViewController, UIGestureRecognizerDelegate {
|
|||||||
currentPage = Int(path_meta[3].components(separatedBy: ".")[0])
|
currentPage = Int(path_meta[3].components(separatedBy: ".")[0])
|
||||||
if currentPage == nil {
|
if currentPage == nil {
|
||||||
print("unable to set currentPage")
|
print("unable to set currentPage")
|
||||||
|
assert(false)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// TODO: Remove this
|
|
||||||
assert(path_meta[2] == "image")
|
|
||||||
currentPage = Int(path_meta[3].components(separatedBy: ".")[0])! + 1
|
|
||||||
}
|
}
|
||||||
|
saveLocalState()
|
||||||
updateInfo()
|
updateInfo()
|
||||||
} catch {
|
} catch {
|
||||||
print("failed to set images")
|
print("failed to set images")
|
||||||
@@ -872,40 +994,139 @@ func getDocumentsURL() -> URL? {
|
|||||||
|
|
||||||
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
extension ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int)
|
func collectionView(
|
||||||
|
_ collectionView: UICollectionView, numberOfItemsInSection section: Int
|
||||||
|
)
|
||||||
-> Int
|
-> Int
|
||||||
{
|
{
|
||||||
return comics.count
|
if collectionView == comicCollectionView {
|
||||||
|
return comics.count
|
||||||
|
} else {
|
||||||
|
return pageCount
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
|
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath)
|
||||||
-> UICollectionViewCell
|
-> UICollectionViewCell
|
||||||
{
|
{
|
||||||
let cell =
|
if collectionView == comicCollectionView {
|
||||||
collectionView.dequeueReusableCell(withReuseIdentifier: "ImageCell", for: indexPath)
|
let cell =
|
||||||
as! ImageCell
|
collectionView.dequeueReusableCell(
|
||||||
cell.imageView.image = comics[indexPath.item].cover
|
withReuseIdentifier: "ComicImageCell", for: indexPath)
|
||||||
return cell
|
as! ComicImageCell
|
||||||
|
cell.imageView.image = comics[indexPath.item].cover
|
||||||
|
return cell
|
||||||
|
} else if collectionView == scrollingCollectionView {
|
||||||
|
let cell =
|
||||||
|
collectionView.dequeueReusableCell(
|
||||||
|
withReuseIdentifier: "ScrollingImageCell", for: indexPath)
|
||||||
|
as! ScrollingImageCell
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
return cell
|
||||||
|
} else {
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Xcode profiling sucks:
|
||||||
|
return
|
||||||
|
collectionView.dequeueReusableCell(
|
||||||
|
withReuseIdentifier: "ScrollingImageCell", for: indexPath)
|
||||||
|
as! ScrollingImageCell
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(
|
func collectionView(
|
||||||
_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
|
_ collectionView: UICollectionView,
|
||||||
|
layout collectionViewLayout: UICollectionViewLayout,
|
||||||
sizeForItemAt indexPath: IndexPath
|
sizeForItemAt indexPath: IndexPath
|
||||||
) -> CGSize {
|
) -> CGSize {
|
||||||
let spacing: CGFloat = 8
|
if collectionView == comicCollectionView {
|
||||||
let itemsPerRow: CGFloat = 3
|
let spacing: CGFloat = 8
|
||||||
let totalSpacing = (itemsPerRow - 1) * spacing
|
let itemsPerRow: CGFloat = 3
|
||||||
let width = (collectionView.bounds.width - totalSpacing) / itemsPerRow
|
let totalSpacing = (itemsPerRow - 1) * spacing
|
||||||
return CGSize(width: width, height: width)
|
let width = (collectionView.bounds.width - totalSpacing) / itemsPerRow
|
||||||
|
return CGSize(width: width, height: width)
|
||||||
|
} else if collectionView == scrollingCollectionView {
|
||||||
|
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 let imagePath = getImagePath(
|
||||||
|
chapter: chapter,
|
||||||
|
volume: Int(metadata.chapters[chapter - 1].volume),
|
||||||
|
page: page, path: currentPath),
|
||||||
|
let imageSource = CGImageSourceCreateWithURL(imagePath as CFURL, nil),
|
||||||
|
let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil)
|
||||||
|
as? [CFString: Any],
|
||||||
|
let height = imageProperties[kCGImagePropertyPixelHeight] as? CGFloat
|
||||||
|
{
|
||||||
|
return CGSize(width: readerView.bounds.width, height: height)
|
||||||
|
}
|
||||||
|
return CGSize(width: readerView.bounds.width, height: readerView.bounds.height)
|
||||||
|
} else {
|
||||||
|
assert(false)
|
||||||
|
}
|
||||||
|
// Xcode profiling sucks:
|
||||||
|
return CGSize()
|
||||||
}
|
}
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
|
||||||
let selectedComic = comics[indexPath.item]
|
|
||||||
|
|
||||||
readComic(name: selectedComic.metadata.title)
|
func collectionView(
|
||||||
|
_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath
|
||||||
|
) {
|
||||||
|
if collectionView == comicCollectionView {
|
||||||
|
let selectedComic = comics[indexPath.item]
|
||||||
|
|
||||||
|
readComic(name: selectedComic.metadata.title)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImageCell: UICollectionViewCell {
|
class ScrollingImageCell: UICollectionViewCell {
|
||||||
|
let imageView = UIImageView()
|
||||||
|
|
||||||
|
override init(frame: CGRect) {
|
||||||
|
super.init(frame: frame)
|
||||||
|
imageView.contentMode = .scaleAspectFill
|
||||||
|
imageView.clipsToBounds = true
|
||||||
|
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
contentView.addSubview(imageView)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
imageView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
||||||
|
imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
||||||
|
imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
||||||
|
imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ComicImageCell: UICollectionViewCell {
|
||||||
let imageView = UIImageView()
|
let imageView = UIImageView()
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
|
2
run.sh
2
run.sh
@@ -18,7 +18,7 @@ xcodebuild -scheme "$SCHEME" -configuration "$BUILD_TYPE" -derivedDataPath "$DER
|
|||||||
|
|
||||||
APP_PATH="$DERIVED_DATA/Build/Products/Debug-iphonesimulator/$APP_NAME.app"
|
APP_PATH="$DERIVED_DATA/Build/Products/Debug-iphonesimulator/$APP_NAME.app"
|
||||||
xcrun simctl install booted "$APP_PATH"
|
xcrun simctl install booted "$APP_PATH"
|
||||||
xcrun simctl spawn booted log stream --predicate 'process == "ImageViewer"' --style syslog &
|
# xcrun simctl spawn booted log stream --predicate 'process == "ImageViewer"' --style syslog &
|
||||||
xcrun simctl launch booted "$COMPANY.$APP_NAME"
|
xcrun simctl launch booted "$COMPANY.$APP_NAME"
|
||||||
|
|
||||||
sudo sed -i '' '/developerservices2\.apple\.com/d' /etc/hosts
|
sudo sed -i '' '/developerservices2\.apple\.com/d' /etc/hosts
|
||||||
|
Reference in New Issue
Block a user