diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 076595264..f393605c0 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -376,7 +376,7 @@ TargetAttributes = { 1A42C2881C0E3882000F2137 = { CreatedOnToolsVersion = 7.1.1; - DevelopmentTeam = 32F2T8EJ6G; + DevelopmentTeam = C3VKVFB3SA; LastSwiftMigration = 0800; ProvisioningStyle = Automatic; SystemCapabilities = { @@ -944,7 +944,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = 32F2T8EJ6G; + DEVELOPMENT_TEAM = C3VKVFB3SA; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.folioreader.Example; @@ -961,7 +961,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = 32F2T8EJ6G; + DEVELOPMENT_TEAM = C3VKVFB3SA; INFOPLIST_FILE = Example/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.folioreader.Example; diff --git a/Example/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Example/Example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Source/FolioReaderBookmarkList.swift b/Source/FolioReaderBookmarkList.swift new file mode 100644 index 000000000..fff4da3bf --- /dev/null +++ b/Source/FolioReaderBookmarkList.swift @@ -0,0 +1,92 @@ +// +// FolioReaderBookmarkList.swift +// FolioReaderKit +// +// Created by Omar Albeik on 26.03.2018. +// Copyright (c) 2015 Folio Reader. All rights reserved. +// + +import UIKit + +class FolioReaderBookmarkList: UITableViewController { + + fileprivate var bookmarks = [Bookmark]() + fileprivate var readerConfig: FolioReaderConfig + fileprivate var folioReader: FolioReader + + init(folioReader: FolioReader, readerConfig: FolioReaderConfig) { + self.readerConfig = readerConfig + self.folioReader = folioReader + + super.init(style: UITableViewStyle.plain) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init with coder not supported") + } + + override func viewDidLoad() { + super.viewDidLoad() + + tableView.register(UITableViewCell.self, forCellReuseIdentifier: kReuseCellIdentifier) + tableView.separatorInset = UIEdgeInsets.zero + tableView.backgroundColor = folioReader.isNight(readerConfig.nightModeMenuBackground, readerConfig.menuBackgroundColor) + tableView.separatorColor = folioReader.isNight(readerConfig.nightModeSeparatorColor, readerConfig.menuSeparatorColor) + + guard let bookId = (folioReader.readerContainer?.book.name as NSString?)?.deletingPathExtension else { + bookmarks = [] + return + } + + bookmarks = Bookmark.allByBookId(withConfiguration: readerConfig, bookId: bookId) + } + + // MARK: - Table view data source + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return bookmarks.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: kReuseCellIdentifier, for: indexPath) + cell.backgroundColor = .clear + + let bookmark = bookmarks[indexPath.row] + + // Format date + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = readerConfig.localizedHighlightsDateFormat + let dateString = dateFormatter.string(from: bookmark.date) + + cell.textLabel?.text = "Page \(bookmark.page)" + cell.detailTextLabel?.text = dateString + + return cell + } + + // MARK: - Table view delegate + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + guard let bookmark = bookmarks[safe: indexPath.row] else { + return + } + + folioReader.readerCenter?.changePageWith(page: bookmark.page, andFragment: bookmark.bookmarkId) + dismiss() + } + + override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { + if editingStyle == .delete { + let bookmark = bookmarks[indexPath.row] + bookmark.remove(withConfiguration: readerConfig) // Remove from Database + bookmarks.remove(at: indexPath.row) + tableView.deleteRows(at: [indexPath], with: .fade) + } + } + + // MARK: - Handle rotation transition + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + tableView.reloadData() + } +} diff --git a/Source/FolioReaderCenter.swift b/Source/FolioReaderCenter.swift index be862143a..fa8670e84 100755 --- a/Source/FolioReaderCenter.swift +++ b/Source/FolioReaderCenter.swift @@ -11,12 +11,12 @@ import ZFDragableModalTransition /// Protocol which is used from `FolioReaderCenter`s. @objc public protocol FolioReaderCenterDelegate: class { - + /// Notifies that a page appeared. This is triggered when a page is chosen and displayed. /// /// - Parameter page: The appeared page @objc optional func pageDidAppear(_ page: FolioReaderPage) - + /// Passes and returns the HTML content as `String`. Implement this method if you want to modify the HTML content of a `FolioReaderPage`. /// /// - Parameters: @@ -29,24 +29,24 @@ import ZFDragableModalTransition /// /// - Parameter pageNumber: The appeared page item @objc optional func pageItemChanged(_ pageNumber: Int) - + } /// The base reader class open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { - + /// This delegate receives the events from the current `FolioReaderPage`s delegate. open weak var delegate: FolioReaderCenterDelegate? - + /// This delegate receives the events from current page open weak var pageDelegate: FolioReaderPageDelegate? - + /// The base reader container open weak var readerContainer: FolioReaderContainer? - + /// The current visible page on reader open fileprivate(set) var currentPage: FolioReaderPage? - + /// The collection view with pages open var collectionView: UICollectionView! @@ -70,7 +70,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl var currentPageNumber: Int = 0 var pageWidth: CGFloat = 0.0 var pageHeight: CGFloat = 0.0 - + fileprivate var screenBounds: CGRect! fileprivate var pointNow = CGPoint.zero fileprivate var pageOffsetRate: CGFloat = 0 @@ -78,46 +78,46 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl fileprivate var isFirstLoad = true fileprivate var currentWebViewScrollPositions = [Int: CGPoint]() fileprivate var currentOrientation: UIInterfaceOrientation? - + fileprivate var readerConfig: FolioReaderConfig { guard let readerContainer = readerContainer else { return FolioReaderConfig() } return readerContainer.readerConfig } - + fileprivate var book: FRBook { guard let readerContainer = readerContainer else { return FRBook() } return readerContainer.book } - + fileprivate var folioReader: FolioReader { guard let readerContainer = readerContainer else { return FolioReader() } return readerContainer.folioReader } - + // MARK: - Init - + init(withContainer readerContainer: FolioReaderContainer) { self.readerContainer = readerContainer super.init(nibName: nil, bundle: Bundle.frameworkBundle()) - + self.initialization() } - + required public init?(coder aDecoder: NSCoder) { fatalError("This class doesn't support NSCoding.") } - + /** Common Initialization */ fileprivate func initialization() { - + if (self.readerConfig.hideBars == true) { self.pageIndicatorHeight = 0 } self.totalPages = book.spine.spineReferences.count - + // Loading indicator let style: UIActivityIndicatorViewStyle = folioReader.isNight(.white, .gray) loadingView = UIActivityIndicatorView(activityIndicatorStyle: style) @@ -125,16 +125,16 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl loadingView.startAnimating() self.view.addSubview(loadingView) } - + // MARK: - View life cicle - + override open func viewDidLoad() { super.viewDidLoad() - + screenBounds = self.getScreenBounds() setPageSize(UIApplication.shared.statusBarOrientation) - + // Layout collectionViewLayout.sectionInset = UIEdgeInsets.zero collectionViewLayout.minimumLineSpacing = 0 @@ -143,7 +143,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl let background = folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white) view.backgroundColor = background - + // CollectionView collectionView = UICollectionView(frame: screenBounds, collectionViewLayout: collectionViewLayout) collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] @@ -157,10 +157,6 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl enableScrollBetweenChapters(scrollEnabled: true) view.addSubview(collectionView) - if #available(iOS 11.0, *) { - collectionView.contentInsetAdjustmentBehavior = .never - } - // Activity Indicator self.activityIndicator.activityIndicatorViewStyle = .gray self.activityIndicator.hidesWhenStopped = true @@ -168,19 +164,23 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl self.activityIndicator.backgroundColor = UIColor.gray self.view.addSubview(self.activityIndicator) self.view.bringSubview(toFront: self.activityIndicator) - + + if #available(iOS 11.0, *) { + collectionView.contentInsetAdjustmentBehavior = .never + } + if #available(iOS 10.0, *) { collectionView.isPrefetchingEnabled = false } - + // Register cell classes collectionView?.register(FolioReaderPage.self, forCellWithReuseIdentifier: kReuseCellIdentifier) - + // Configure navigation bar and layout automaticallyAdjustsScrollViewInsets = false extendedLayoutIncludesOpaqueBars = true configureNavBar() - + // Page indicator view if (self.readerConfig.hidePageIndicator == false) { let frame = self.frameForPageIndicatorView() @@ -189,7 +189,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl view.addSubview(pageIndicatorView) } } - + guard let readerContainer = readerContainer else { return } self.scrollScrubber = ScrollScrubber(frame: frameForScrollScrubber(), withReaderContainer: readerContainer) self.scrollScrubber?.delegate = self @@ -197,43 +197,43 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl view.addSubview(scrollScrubber.slider) } } - + override open func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + configureNavBar() - + // Update pages pagesForCurrentPage(currentPage) pageIndicatorView?.reloadView(updateShadow: true) } - + override open func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - + screenBounds = self.getScreenBounds() loadingView.center = view.center - + setPageSize(UIApplication.shared.statusBarOrientation) updateSubviewFrames() } - + // MARK: Layout - + /** Enable or disable the scrolling between chapters (`FolioReaderPage`s). If this is enabled it's only possible to read the current chapter. If another chapter should be displayed is has to be triggered programmatically with `changePageWith`. - + - parameter scrollEnabled: `Bool` which enables or disables the scrolling between `FolioReaderPage`s. */ open func enableScrollBetweenChapters(scrollEnabled: Bool) { self.collectionView.isScrollEnabled = scrollEnabled } - + fileprivate func updateSubviewFrames() { self.pageIndicatorView?.frame = self.frameForPageIndicatorView() self.scrollScrubber?.frame = self.frameForScrollScrubber() } - + fileprivate func frameForPageIndicatorView() -> CGRect { var bounds = CGRect(x: 0, y: screenBounds.size.height-pageIndicatorHeight, width: screenBounds.size.width, height: pageIndicatorHeight) @@ -243,12 +243,12 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl return bounds } - + fileprivate func frameForScrollScrubber() -> CGRect { let scrubberY: CGFloat = ((self.readerConfig.shouldHideNavigationOnTap == true || self.readerConfig.hideBars == true) ? 50 : 74) return CGRect(x: self.pageWidth + 10, y: scrubberY, width: 40, height: (self.pageHeight - 100)) } - + func configureNavBar() { let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, UIColor.white) let tintColor = readerConfig.tintColor @@ -256,35 +256,41 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl let font = UIFont(name: "Avenir-Light", size: 17)! setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font) } - + func configureNavBarButtons() { - + // Navbar buttons let shareIcon = UIImage(readerImageNamed: "icon-navbar-share")?.ignoreSystemTint(withConfiguration: self.readerConfig) let audioIcon = UIImage(readerImageNamed: "icon-navbar-tts")?.ignoreSystemTint(withConfiguration: self.readerConfig) //man-speech-icon let closeIcon = UIImage(readerImageNamed: "icon-navbar-close")?.ignoreSystemTint(withConfiguration: self.readerConfig) let tocIcon = UIImage(readerImageNamed: "icon-navbar-toc")?.ignoreSystemTint(withConfiguration: self.readerConfig) let fontIcon = UIImage(readerImageNamed: "icon-navbar-font")?.ignoreSystemTint(withConfiguration: self.readerConfig) + let space = 70 as CGFloat - + let menu = UIBarButtonItem(image: closeIcon, style: .plain, target: self, action:#selector(closeReader(_:))) let toc = UIBarButtonItem(image: tocIcon, style: .plain, target: self, action:#selector(presentChapterList(_:))) - + navigationItem.leftBarButtonItems = [menu, toc] - + var rightBarIcons = [UIBarButtonItem]() - + if (self.readerConfig.allowSharing == true) { rightBarIcons.append(UIBarButtonItem(image: shareIcon, style: .plain, target: self, action:#selector(shareChapter(_:)))) } - + + if readerConfig.allowBookmarking { + let bookmarkButton = UIBookmarkBarButtonItem(target: self, action: #selector(didTapBookmarkIcon(_:)), readerConfig: readerConfig) + rightBarIcons.append(bookmarkButton) + } + if self.book.hasAudio || self.readerConfig.enableTTS { rightBarIcons.append(UIBarButtonItem(image: audioIcon, style: .plain, target: self, action:#selector(presentPlayerMenu(_:)))) } - + let font = UIBarButtonItem(image: fontIcon, style: .plain, target: self, action: #selector(presentFontsMenu)) font.width = space - + rightBarIcons.append(contentsOf: [font]) navigationItem.rightBarButtonItems = rightBarIcons @@ -292,28 +298,28 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl navigationItem.title = book.title } } - + func reloadData() { self.loadingView.stopAnimating() self.totalPages = book.spine.spineReferences.count - + self.collectionView.reloadData() self.configureNavBarButtons() self.setCollectionViewProgressiveDirection() - + if self.readerConfig.loadSavedPositionForCurrentBook { guard let position = folioReader.savedPositionForCurrentBook, let pageNumber = position["pageNumber"] as? Int, pageNumber > 0 else { self.currentPageNumber = 1 return } - + self.changePageWith(page: pageNumber) self.currentPageNumber = pageNumber } } - + // MARK: Change page progressive direction - + private func transformViewForRTL(_ view: UIView?) { if folioReader.needsRTLChange { view?.transform = CGAffineTransform(scaleX: -1, y: 1) @@ -321,36 +327,36 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl view?.transform = CGAffineTransform.identity } } - + func setCollectionViewProgressiveDirection() { self.transformViewForRTL(self.collectionView) } - + func setPageProgressiveDirection(_ page: FolioReaderPage) { self.transformViewForRTL(page) } - + // MARK: Change layout orientation - + /// Get internal page offset before layout change private func updatePageOffsetRate() { guard let currentPage = self.currentPage, let webView = currentPage.webView else { return } - + let pageScrollView = webView.scrollView let contentSize = pageScrollView.contentSize.forDirection(withConfiguration: self.readerConfig) let contentOffset = pageScrollView.contentOffset.forDirection(withConfiguration: self.readerConfig) self.pageOffsetRate = (contentSize != 0 ? (contentOffset / contentSize) : 0) } - + func setScrollDirection(_ direction: FolioReaderScrollDirection) { guard let currentPage = self.currentPage, let webView = currentPage.webView else { return } - + let pageScrollView = webView.scrollView - + // Get internal page offset before layout change self.updatePageOffsetRate() // Change layout @@ -359,65 +365,65 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl self.currentPage?.setNeedsLayout() self.collectionView.collectionViewLayout.invalidateLayout() self.collectionView.setContentOffset(frameForPage(self.currentPageNumber).origin, animated: false) - + // Page progressive direction self.setCollectionViewProgressiveDirection() delay(0.2) { self.setPageProgressiveDirection(currentPage) } - - + + /** * This delay is needed because the page will not be ready yet * so the delay wait until layout finished the changes. */ delay(0.1) { var pageOffset = (pageScrollView.contentSize.forDirection(withConfiguration: self.readerConfig) * self.pageOffsetRate) - + // Fix the offset for paged scroll if (self.readerConfig.scrollDirection == .horizontal && self.pageWidth != 0) { let page = round(pageOffset / self.pageWidth) pageOffset = (page * self.pageWidth) } - + let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset)) pageScrollView.setContentOffset(pageOffsetPoint, animated: true) } } - + // MARK: Status bar and Navigation bar - + func hideBars() { guard self.readerConfig.shouldHideNavigationOnTap == true else { return } - + self.updateBarsStatus(true) } - + func showBars() { self.configureNavBar() self.updateBarsStatus(false) } - + func toggleBars() { guard self.readerConfig.shouldHideNavigationOnTap == true else { return } - + let shouldHide = !self.navigationController!.isNavigationBarHidden if shouldHide == false { self.configureNavBar() } - + self.updateBarsStatus(shouldHide) } - + private func updateBarsStatus(_ shouldHide: Bool, shouldShowIndicator: Bool = false) { guard let readerContainer = readerContainer else { return } readerContainer.shouldHideStatusBar = shouldHide - + UIView.animate(withDuration: 0.25, animations: { readerContainer.setNeedsStatusBarAppearanceUpdate() - + // Show minutes indicator if (shouldShowIndicator == true) { self.pageIndicatorView?.minutesLabel.alpha = shouldHide ? 0 : 1 @@ -425,27 +431,27 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl }) self.navigationController?.setNavigationBarHidden(shouldHide, animated: true) } - + // MARK: UICollectionViewDataSource - + open func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } - + open func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return totalPages } - + open func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let reuseableCell = collectionView.dequeueReusableCell(withReuseIdentifier: kReuseCellIdentifier, for: indexPath) as? FolioReaderPage return self.configure(readerPageCell: reuseableCell, atIndexPath: indexPath) } - + private func configure(readerPageCell cell: FolioReaderPage?, atIndexPath indexPath: IndexPath) -> UICollectionViewCell { guard let cell = cell, let readerContainer = readerContainer else { return UICollectionViewCell() } - + cell.setup(withReaderContainer: readerContainer) cell.pageNumber = indexPath.row+1 cell.webView?.scrollView.delegate = self @@ -456,50 +462,50 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl cell.webView?.frame = cell.webViewFrame() cell.delegate = self cell.backgroundColor = .clear - + setPageProgressiveDirection(cell) - + // Configure the cell let resource = self.book.spine.spineReferences[indexPath.row].resource guard var html = try? String(contentsOfFile: resource.fullHref, encoding: String.Encoding.utf8) else { return cell } - + let mediaOverlayStyleColors = "\"\(self.readerConfig.mediaOverlayColor.hexString(false))\", \"\(self.readerConfig.mediaOverlayColor.highlightColor().hexString(false))\"" - + // Inject CSS let jsFilePath = Bundle.frameworkBundle().path(forResource: "Bridge", ofType: "js") let cssFilePath = Bundle.frameworkBundle().path(forResource: "Style", ofType: "css") let cssTag = "" let jsTag = "" + "" - + let toInject = "\n\(cssTag)\n\(jsTag)\n" html = html.replacingOccurrences(of: "", with: toInject) - + // Font class name var classes = folioReader.currentFont.cssIdentifier classes += " " + folioReader.currentMediaOverlayStyle.className() - + // Night mode if folioReader.nightMode { classes += " nightMode" } - + // Font Size classes += " \(folioReader.currentFontSize.cssIdentifier)" - + html = html.replacingOccurrences(of: " CGSize { var size = CGSize(width: collectionView.frame.width, height: collectionView.frame.height) @@ -517,38 +523,38 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } // MARK: - Device rotation - + override open func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) { guard folioReader.isReaderReady else { return } - + setPageSize(toInterfaceOrientation) updateCurrentPage() - + if self.currentOrientation == nil || (self.currentOrientation?.isPortrait != toInterfaceOrientation.isPortrait) { var pageIndicatorFrame = pageIndicatorView?.frame pageIndicatorFrame?.origin.y = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.height - pageIndicatorHeight) : (self.collectionView.frame.width - pageIndicatorHeight)) pageIndicatorFrame?.origin.x = 0 pageIndicatorFrame?.size.width = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.width) : (self.collectionView.frame.height)) pageIndicatorFrame?.size.height = pageIndicatorHeight - + var scrollScrubberFrame = scrollScrubber?.slider.frame; scrollScrubberFrame?.origin.x = ((screenBounds.size.height < screenBounds.size.width) ? (screenBounds.size.width - 100) : (screenBounds.size.height + 10)) scrollScrubberFrame?.size.height = ((screenBounds.size.height < screenBounds.size.width) ? (self.collectionView.frame.height - 100) : (self.collectionView.frame.width - 100)) - + self.collectionView.collectionViewLayout.invalidateLayout() - + UIView.animate(withDuration: duration, animations: { // Adjust page indicator view if let pageIndicatorFrame = pageIndicatorFrame { self.pageIndicatorView?.frame = pageIndicatorFrame self.pageIndicatorView?.reloadView(updateShadow: true) } - + // Adjust scroll scrubber slider if let scrollScrubberFrame = scrollScrubberFrame { self.scrollScrubber?.slider.frame = scrollScrubberFrame } - + // Adjust collectionView self.collectionView.contentSize = self.readerConfig.isDirection( CGSize(width: self.pageWidth, height: self.pageHeight * CGFloat(self.totalPages)), @@ -557,44 +563,44 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl ) self.collectionView.setContentOffset(self.frameForPage(self.currentPageNumber).origin, animated: false) self.collectionView.collectionViewLayout.invalidateLayout() - + // Adjust internal page offset self.updatePageOffsetRate() }) } - + self.currentOrientation = toInterfaceOrientation } - + override open func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) { guard folioReader.isReaderReady == true, let currentPage = currentPage else { return } - + // Update pages pagesForCurrentPage(currentPage) currentPage.refreshPageMode() - + scrollScrubber?.setSliderVal() - + // After rotation fix internal page offset var pageOffset = (currentPage.webView?.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) ?? 0) * pageOffsetRate - + // Fix the offset for paged scroll if (self.readerConfig.scrollDirection == .horizontal && self.pageWidth != 0) { let page = round(pageOffset / self.pageWidth) pageOffset = page * self.pageWidth } - + let pageOffsetPoint = self.readerConfig.isDirection(CGPoint(x: 0, y: pageOffset), CGPoint(x: pageOffset, y: 0), CGPoint(x: 0, y: pageOffset)) currentPage.webView?.scrollView.setContentOffset(pageOffsetPoint, animated: true) } - + override open func willAnimateRotation(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) { guard folioReader.isReaderReady else { return } - + self.collectionView.scrollToItem(at: IndexPath(row: self.currentPageNumber - 1, section: 0), at: UICollectionViewScrollPosition(), animated: false) if (self.currentPageNumber + 1) >= totalPages { UIView.animate(withDuration: duration, animations: { @@ -602,9 +608,9 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl }) } } - + // MARK: - Page - + func setPageSize(_ orientation: UIInterfaceOrientation) { guard orientation.isPortrait else { if screenBounds.size.width > screenBounds.size.height { @@ -616,7 +622,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } return } - + if screenBounds.size.width < screenBounds.size.height { self.pageWidth = screenBounds.size.width self.pageHeight = screenBounds.size.height @@ -625,7 +631,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl self.pageHeight = screenBounds.size.width } } - + func updateCurrentPage(_ page: FolioReaderPage? = nil, completion: (() -> Void)? = nil) { if let page = page { currentPage = page @@ -634,64 +640,64 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } else { let currentIndexPath = getCurrentIndexPath() currentPage = collectionView.cellForItem(at: currentIndexPath) as? FolioReaderPage - + self.previousPageNumber = currentIndexPath.row self.currentPageNumber = currentIndexPath.row+1 } - + self.nextPageNumber = (((self.currentPageNumber + 1) <= totalPages) ? (self.currentPageNumber + 1) : self.currentPageNumber) - + // Set pages guard let currentPage = currentPage else { completion?() return } - + scrollScrubber?.setSliderVal() - + if let readingTime = currentPage.webView?.js("getReadingTime()") { pageIndicatorView?.totalMinutes = Int(readingTime)! } else { pageIndicatorView?.totalMinutes = 0 } pagesForCurrentPage(currentPage) - + delegate?.pageDidAppear?(currentPage) delegate?.pageItemChanged?(self.getCurrentPageItemNumber()) - + completion?() } - + func pagesForCurrentPage(_ page: FolioReaderPage?) { guard let page = page, let webView = page.webView else { return } - + let pageSize = self.readerConfig.isDirection(pageHeight, self.pageWidth, pageHeight) let contentSize = page.webView?.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) ?? 0 self.pageIndicatorView?.totalPages = ((pageSize != 0) ? Int(ceil(contentSize / pageSize)) : 0) - + let pageOffSet = self.readerConfig.isDirection(webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.y) let webViewPage = pageForOffset(pageOffSet, pageHeight: pageSize) - + self.pageIndicatorView?.currentPage = webViewPage } - + func pageForOffset(_ offset: CGFloat, pageHeight height: CGFloat) -> Int { guard (height != 0) else { return 0 } - + let page = Int(ceil(offset / height))+1 return page } - + func getCurrentIndexPath() -> IndexPath { let indexPaths = collectionView.indexPathsForVisibleItems var indexPath = IndexPath() - + if indexPaths.count > 1 { let first = indexPaths.first! let last = indexPaths.last! - + switch self.pageScrollDirection { case .up, .left: if first.compare(last) == .orderedAscending { @@ -709,10 +715,10 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } else { indexPath = indexPaths.first ?? IndexPath(row: 0, section: 0) } - + return indexPath } - + func frameForPage(_ page: Int) -> CGRect { return self.readerConfig.isDirection( CGRect(x: 0, y: self.pageHeight * CGFloat(page-1), width: self.pageWidth, height: self.pageHeight), @@ -720,7 +726,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl CGRect(x: 0, y: self.pageHeight * CGFloat(page-1), width: self.pageWidth, height: self.pageHeight) ) } - + open func changePageWith(page: Int, andFragment fragment: String, animated: Bool = false, completion: (() -> Void)? = nil) { if (self.currentPageNumber == page) { if let currentPage = currentPage , fragment != "" { @@ -736,7 +742,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl }) } } - + open func changePageWith(href: String, animated: Bool = false, completion: (() -> Void)? = nil) { let item = findPageByHref(href) let indexPath = IndexPath(row: item, section: 0) @@ -746,11 +752,11 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } }) } - + open func changePageWith(href: String, andAudioMarkID markID: String) { if recentlyScrolled { return } // if user recently scrolled, do not change pages or scroll the webview guard let currentPage = currentPage else { return } - + let item = findPageByHref(href) let pageUpdateNeeded = item+1 != currentPage.pageNumber let indexPath = IndexPath(row: item, section: 0) @@ -764,14 +770,14 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } } } - + open func changePageWith(indexPath: IndexPath, animated: Bool = false, completion: (() -> Void)? = nil) { guard indexPathIsValid(indexPath) else { print("ERROR: Attempt to scroll to invalid index path") completion?() return } - + UIView.animate(withDuration: animated ? 0.3 : 0, delay: 0, options: UIViewAnimationOptions(), animations: { () -> Void in self.collectionView.scrollToItem(at: indexPath, at: .direction(withConfiguration: self.readerConfig), animated: false) }) { (finished: Bool) -> Void in @@ -784,31 +790,31 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl self.changePageItem(to: pageItem) } } - + func indexPathIsValid(_ indexPath: IndexPath) -> Bool { let section = indexPath.section let row = indexPath.row let lastSectionIndex = numberOfSections(in: collectionView) - 1 - + //Make sure the specified section exists if section > lastSectionIndex { return false } - + let rowCount = self.collectionView(collectionView, numberOfItemsInSection: indexPath.section) - 1 return row <= rowCount } - + open func isLastPage() -> Bool{ return (currentPageNumber == self.nextPageNumber) } - + public func changePageToNext(_ completion: (() -> Void)? = nil) { changePageWith(page: self.nextPageNumber, animated: true) { () -> Void in completion?() } } - + public func changePageToPrevious(_ completion: (() -> Void)? = nil) { changePageWith(page: self.previousPageNumber, animated: true) { () -> Void in completion?() @@ -837,7 +843,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl completion?() } - + public func getCurrentPageItemNumber() -> Int { guard let page = currentPage, let webView = page.webView else { return 0 } @@ -847,7 +853,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl return webViewPage } - + public func changePageItemToPrevious(_ completion: (() -> Void)? = nil) { // TODO: It was implemented for horizontal orientation. // Need check page orientation (v/h) and make correct calc for vertical @@ -869,7 +875,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl completion?() } - + public func changePageItemToLast(animated: Bool = true, _ completion: (() -> Void)? = nil) { // TODO: It was implemented for horizontal orientation. // Need check page orientation (v/h) and make correct calc for vertical @@ -895,7 +901,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl completion?() } - + public func changePageItem(to: Int, animated: Bool = true, completion: (() -> Void)? = nil) { // TODO: It was implemented for horizontal orientation. // Need check page orientation (v/h) and make correct calc for vertical @@ -930,7 +936,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } } } - + /** Find a page by FRTocReference. */ @@ -944,7 +950,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } return count } - + /** Find a page by href. */ @@ -958,17 +964,17 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } return count } - + /** Find and return the current chapter resource. */ public func getCurrentChapter() -> FRResource? { var foundResource: FRResource? - + func search(_ items: [FRTocReference]) { for item in items { guard foundResource == nil else { break } - + if let reference = book.spine.spineReferences[safe: (currentPageNumber - 1)], let resource = item.resource, resource == reference.resource { foundResource = resource break @@ -978,10 +984,10 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } } search(book.flatTableOfContents) - + return foundResource } - + /** Return the current chapter progress based on current chapter and total of chapters. */ @@ -995,7 +1001,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl return CGFloat((100 * current) / total) } - + /** Find and return the current chapter name. */ @@ -1008,18 +1014,18 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl let title = item.title else { continue } - + return title } - + return nil } - + // MARK: Public page methods - + /** Changes the current page of the reader. - + - parameter page: The target page index. Note: The page index starts at 1 (and not 0). - parameter animated: En-/Disables the animation of the page change. - parameter completion: A Closure which is called if the page change is completed. @@ -1034,21 +1040,21 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl }) } } - + // MARK: - Audio Playing - + func audioMark(href: String, fragmentID: String) { changePageWith(href: href, andAudioMarkID: fragmentID) } - + // MARK: - Sharing - + /** Sharing chapter method. */ @objc func shareChapter(_ sender: UIBarButtonItem) { guard let currentPage = currentPage else { return } - + if let chapterText = currentPage.webView?.js("getBodyText()") { let htmlText = chapterText.replacingOccurrences(of: "[\\n\\r]+", with: "
", options: .regularExpression) var subject = readerConfig.localizedShareChapterSubject @@ -1058,53 +1064,68 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl var chapterName = "" var authorName = "" var shareItems = [AnyObject]() - + // Get book title if let title = self.book.title { bookTitle = title subject += " “\(title)”" } - + // Get chapter name if let chapter = getCurrentChapterName() { chapterName = chapter } - + // Get author name if let author = self.book.metadata.creators.first { authorName = author.name } - + // Sharing html and text html = "" html += "

\(htmlText)



" html += "

"+readerConfig.localizedShareAllExcerptsFrom+"

" html += "\(bookTitle)
" html += readerConfig.localizedShareBy+" \(authorName)
" - + if let bookShareLink = readerConfig.localizedShareWebLink { html += "\(bookShareLink.absoluteString)" shareItems.append(bookShareLink as AnyObject) } - + html += "
" text = "\(chapterName)\n\n“\(chapterText)” \n\n\(bookTitle) \n\(readerConfig.localizedShareBy) \(authorName)" - + let act = FolioReaderSharingProvider(subject: subject, text: text, html: html) shareItems.insert(contentsOf: [act, "" as AnyObject], at: 0) - + let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil) activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo] - + // Pop style on iPad if let actv = activityViewController.popoverPresentationController { actv.barButtonItem = sender } - + present(activityViewController, animated: true, completion: nil) } } + + @objc func didTapBookmarkIcon(_ sender: UIBookmarkBarButtonItem) { +// guard let bookId = (self.book.name as NSString?)?.deletingPathExtension else { return } +// let pageNumber = folioReader.readerCenter?.currentPageNumber ?? 0 +// +// let match = Bookmark.MatchingBookmark(id: "", bookId: bookId, currentPage: pageNumber) +// let bookmark = Bookmark.matchBookmark(match, withConfiguration: readerConfig) + if sender.isHighlighed { + sender.isHighlighed = false + + } else { + sender.isHighlighed = true + } + } + /** Sharing highlight method. */ @@ -1116,23 +1137,23 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl var chapterName = "" var authorName = "" var shareItems = [AnyObject]() - + // Get book title if let title = self.book.title { bookTitle = title subject += " “\(title)”" } - + // Get chapter name if let chapter = getCurrentChapterName() { chapterName = chapter } - + // Get author name if let author = self.book.metadata.creators.first { authorName = author.name } - + // Sharing html and text html = "" html += "

\(chapterName)

" @@ -1140,70 +1161,70 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl html += "

"+readerConfig.localizedShareAllExcerptsFrom+"

" html += "\(bookTitle)
" html += readerConfig.localizedShareBy+" \(authorName)
" - + if let bookShareLink = readerConfig.localizedShareWebLink { html += "\(bookShareLink.absoluteString)" shareItems.append(bookShareLink as AnyObject) } - + html += "
" text = "\(chapterName)\n\n“\(string)” \n\n\(bookTitle) \n\(readerConfig.localizedShareBy) \(authorName)" - + let act = FolioReaderSharingProvider(subject: subject, text: text, html: html) shareItems.insert(contentsOf: [act, "" as AnyObject], at: 0) - + let activityViewController = UIActivityViewController(activityItems: shareItems, applicationActivities: nil) activityViewController.excludedActivityTypes = [UIActivityType.print, UIActivityType.postToVimeo] - + // Pop style on iPad if let actv = activityViewController.popoverPresentationController { actv.sourceView = currentPage actv.sourceRect = rect } - + present(activityViewController, animated: true, completion: nil) } - + // MARK: - ScrollView Delegate - + open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { self.isScrolling = true clearRecentlyScrolled() recentlyScrolled = true pointNow = scrollView.contentOffset - + if let currentPage = currentPage { currentPage.webView?.createMenu(options: true) currentPage.webView?.setMenuVisible(false) } - + scrollScrubber?.scrollViewWillBeginDragging(scrollView) } - + open func scrollViewDidScroll(_ scrollView: UIScrollView) { - + if (navigationController?.isNavigationBarHidden == false) { self.toggleBars() } - + scrollScrubber?.scrollViewDidScroll(scrollView) - + let isCollectionScrollView = (scrollView is UICollectionView) let scrollType: ScrollType = ((isCollectionScrollView == true) ? .chapter : .page) - + // Update current reading page if (isCollectionScrollView == false), let page = currentPage, let webView = page.webView { - + let pageSize = self.readerConfig.isDirection(self.pageHeight, self.pageWidth, self.pageHeight) let contentOffset = webView.scrollView.contentOffset.forDirection(withConfiguration: self.readerConfig) let contentSize = webView.scrollView.contentSize.forDirection(withConfiguration: self.readerConfig) if (contentOffset + pageSize <= contentSize) { - + let webViewPage = pageForOffset(contentOffset, pageHeight: pageSize) - + if (readerConfig.scrollDirection == .horizontalWithVerticalContent) { let currentIndexPathRow = (page.pageNumber - 1) - + // if the cell reload doesn't save the top position offset if let oldOffSet = self.currentWebViewScrollPositions[currentIndexPathRow], (abs(oldOffSet.y - scrollView.contentOffset.y) > 100) { // Do nothing @@ -1211,7 +1232,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl self.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset } } - + if (pageIndicatorView?.currentPage != webViewPage) { pageIndicatorView?.currentPage = webViewPage } @@ -1219,17 +1240,17 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl self.delegate?.pageItemChanged?(webViewPage) } } - + self.updatePageScrollDirection(inScrollView: scrollView, forScrollType: scrollType) } - + private func updatePageScrollDirection(inScrollView scrollView: UIScrollView, forScrollType scrollType: ScrollType) { - + let scrollViewContentOffsetForDirection = scrollView.contentOffset.forDirection(withConfiguration: self.readerConfig, scrollType: scrollType) let pointNowForDirection = pointNow.forDirection(withConfiguration: self.readerConfig, scrollType: scrollType) // The movement is either positive or negative. This happens if the page change isn't completed. Toggle to the other scroll direction then. let isCurrentlyPositive = (self.pageScrollDirection == .left || self.pageScrollDirection == .up) - + if (scrollViewContentOffsetForDirection < pointNowForDirection) { self.pageScrollDirection = .negative(withConfiguration: self.readerConfig, scrollType: scrollType) } else if (scrollViewContentOffsetForDirection > pointNowForDirection) { @@ -1240,10 +1261,10 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl self.pageScrollDirection = .positive(withConfiguration: self.readerConfig, scrollType: scrollType) } } - + open func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { self.isScrolling = false - + // Perform the page after a short delay as the collection view hasn't completed it's transition if this method is called (the index paths aren't right during fast scrolls). delay(0.2, closure: { [weak self] in if (self?.readerConfig.scrollDirection == .horizontalWithVerticalContent), @@ -1251,7 +1272,7 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl let currentIndexPathRow = cell.pageNumber - 1 self?.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset } - + if (scrollView is UICollectionView) { guard let instance = self else { return @@ -1266,12 +1287,12 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } }) } - + open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { recentlyScrolledTimer = Timer(timeInterval:recentlyScrolledDelay, target: self, selector: #selector(FolioReaderCenter.clearRecentlyScrolled), userInfo: nil, repeats: false) RunLoop.current.add(recentlyScrolledTimer, forMode: RunLoopMode.commonModes) } - + @objc func clearRecentlyScrolled() { if(recentlyScrolledTimer != nil) { recentlyScrolledTimer.invalidate() @@ -1279,46 +1300,52 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl } recentlyScrolled = false } - + open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { scrollScrubber?.scrollViewDidEndScrollingAnimation(scrollView) } - + // MARK: NavigationBar Actions - + @objc func closeReader(_ sender: UIBarButtonItem) { dismiss() folioReader.close() } - + /** Present chapter list */ @objc func presentChapterList(_ sender: UIBarButtonItem) { folioReader.saveReaderState() - + let chapter = FolioReaderChapterList(folioReader: folioReader, readerConfig: readerConfig, book: book, delegate: self) let highlight = FolioReaderHighlightList(folioReader: folioReader, readerConfig: readerConfig) + let bookmark = FolioReaderBookmarkList(folioReader: folioReader, readerConfig: readerConfig) let pageController = PageViewController(folioReader: folioReader, readerConfig: readerConfig) - + pageController.viewControllerOne = chapter pageController.viewControllerTwo = highlight + pageController.viewControllerThree = bookmark pageController.segmentedControlItems = [readerConfig.localizedContentsTitle, readerConfig.localizedHighlightsTitle] - + + if readerConfig.allowBookmarking { + pageController.segmentedControlItems.append(readerConfig.localizedBookmarksTitle) + } + let nav = UINavigationController(rootViewController: pageController) present(nav, animated: true, completion: nil) } - + /** Present fonts and settings menu */ @objc func presentFontsMenu() { folioReader.saveReaderState() hideBars() - + let menu = FolioReaderFontsMenu(folioReader: folioReader, readerConfig: readerConfig) menu.modalPresentationStyle = .custom - + animator = ZFModalTransitionAnimator(modalViewController: menu) animator.isDragable = false animator.bounces = false @@ -1326,21 +1353,21 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl animator.behindViewScale = 1 animator.transitionDuration = 0.6 animator.direction = ZFModalTransitonDirection.bottom - + menu.transitioningDelegate = animator self.present(menu, animated: true, completion: nil) } - + /** Present audio player menu */ @objc func presentPlayerMenu(_ sender: UIBarButtonItem) { folioReader.saveReaderState() hideBars() - + let menu = FolioReaderPlayerMenu(folioReader: folioReader, readerConfig: readerConfig) menu.modalPresentationStyle = .custom - + animator = ZFModalTransitionAnimator(modalViewController: menu) animator.isDragable = true animator.bounces = false @@ -1348,18 +1375,18 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl animator.behindViewScale = 1 animator.transitionDuration = 0.6 animator.direction = ZFModalTransitonDirection.bottom - + menu.transitioningDelegate = animator present(menu, animated: true, completion: nil) } - + /** Present Quote Share */ func presentQuoteShare(_ string: String) { let quoteShare = FolioReaderQuoteShare(initWithText: string, readerConfig: readerConfig, folioReader: folioReader, book: book) let nav = UINavigationController(rootViewController: quoteShare) - + if UIDevice.current.userInterfaceIdiom == .pad { nav.modalPresentationStyle = .formSheet } @@ -1382,17 +1409,17 @@ open class FolioReaderCenter: UIViewController, UICollectionViewDelegate, UIColl // MARK: FolioPageDelegate extension FolioReaderCenter: FolioReaderPageDelegate { - + public func pageDidLoad(_ page: FolioReaderPage) { if self.readerConfig.loadSavedPositionForCurrentBook, let position = folioReader.savedPositionForCurrentBook { let pageNumber = position["pageNumber"] as? Int let offset = self.readerConfig.isDirection(position["pageOffsetY"], position["pageOffsetX"], position["pageOffsetY"]) as? CGFloat let pageOffset = offset - + if isFirstLoad { updateCurrentPage(page) isFirstLoad = false - + if (self.currentPageNumber == pageNumber && pageOffset > 0) { page.scrollPageToOffset(pageOffset!, animated: false) } @@ -1403,7 +1430,7 @@ extension FolioReaderCenter: FolioReaderPageDelegate { updateCurrentPage(page) isFirstLoad = false } - + // Go to fragment if needed if let fragmentID = tempFragment, let currentPage = currentPage , fragmentID != "" { currentPage.handleAnchor(fragmentID, avoidBeginningAnchors: true, animated: true) diff --git a/Source/FolioReaderConfig.swift b/Source/FolioReaderConfig.swift index 033772f3e..6552a0331 100755 --- a/Source/FolioReaderConfig.swift +++ b/Source/FolioReaderConfig.swift @@ -138,6 +138,9 @@ open class FolioReaderConfig: NSObject { /// Allow sharing option, if `false` will hide all sharing icons and options open var allowSharing = true + /// Allow bookmarking option, if `false` will hide all bookmarking icons and options + open var allowBookmarking = true + /// Enable TTS (Text To Speech) open var enableTTS = true @@ -168,6 +171,9 @@ open class FolioReaderConfig: NSObject { // MARK: Localized strings + /// Localizes Bookmarks title + open var localizedBookmarksTitle = NSLocalizedString("Bookmarks", comment: "") + /// Localizes Highlight title open var localizedHighlightsTitle = NSLocalizedString("Highlights", comment: "") diff --git a/Source/Models/Bookmark+Helper.swift b/Source/Models/Bookmark+Helper.swift new file mode 100644 index 000000000..935936735 --- /dev/null +++ b/Source/Models/Bookmark+Helper.swift @@ -0,0 +1,131 @@ +// +// Bookmark+Helper.swift +// FolioReaderKit +// +// Created by Omar Albeik on 26.03.2018. +// Copyright (c) 2015 Folio Reader. All rights reserved. +// + +import Foundation +import RealmSwift + +extension Bookmark { + + public struct MatchingBookmark { + var id: String + var bookId: String + var currentPage: Int + } + + /** + Match a bookmark on string. + */ + public static func matchBookmark(_ matchingBookmark: MatchingBookmark, withConfiguration readerConfig: FolioReaderConfig) -> Bookmark? { + + guard let bookmark = Bookmark.allByBookId(withConfiguration: readerConfig, bookId: matchingBookmark.bookId, andPage: matchingBookmark.currentPage as NSNumber).filter({ $0.bookmarkId == matchingBookmark.id }).first else { + + let bookmark = Bookmark() + bookmark.bookmarkId = matchingBookmark.id + bookmark.page = matchingBookmark.currentPage + bookmark.bookId = matchingBookmark.bookId + bookmark.date = Date() + + return bookmark + } + + return bookmark + } + + /// Save a Bookmark with completion block + /// + /// - Parameters: + /// - readerConfig: Current folio reader configuration. + /// - completion: Completion block. + public func persist(withConfiguration readerConfig: FolioReaderConfig, completion: Completion? = nil) { + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + realm.beginWrite() + realm.add(self, update: true) + try realm.commitWrite() + completion?(nil) + } catch let error as NSError { + print("Error on persist bookmark: \(error)") + completion?(error) + } + } + + /// Remove a Bookmark + /// + /// - Parameter readerConfig: Current folio reader configuration. + public func remove(withConfiguration readerConfig: FolioReaderConfig) { + do { + guard let realm = try? Realm(configuration: readerConfig.realmConfiguration) else { + return + } + try realm.write { + realm.delete(self) + try realm.commitWrite() + } + } catch let error as NSError { + print("Error on remove bookmark: \(error)") + } + } + + /// Remove a Bookmark by ID + /// + /// - Parameters: + /// - readerConfig: Current folio reader configuration. + /// - bookmarkId: The ID to be removed + public static func removeById(withConfiguration readerConfig: FolioReaderConfig, bookmarkId: String) { + var bookmark: Bookmark? + let predicate = NSPredicate(format:"bookmarkId = %@", bookmarkId) + + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + bookmark = realm.objects(Bookmark.self).filter(predicate).toArray(Bookmark.self).first + bookmark?.remove(withConfiguration: readerConfig) + } catch let error as NSError { + print("Error on remove bookmark by id: \(error)") + } + } + + /// Return a list of Bookmarks with a given ID + /// + /// - Parameters: + /// - readerConfig: Current folio reader configuration. + /// - bookId: Book ID + /// - page: Page number + /// - Returns: Return a list of Bookmarks + public static func allByBookId(withConfiguration readerConfig: FolioReaderConfig, bookId: String, andPage page: NSNumber? = nil) -> [Bookmark] { + var bookmarks: [Bookmark]? + var predicate = NSPredicate(format: "bookId = %@", bookId) + if let page = page { + predicate = NSPredicate(format: "bookId = %@ && page = %@", bookId, page) + } + + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + bookmarks = realm.objects(Bookmark.self).filter(predicate).toArray(Bookmark.self) + return (bookmarks ?? []) + } catch let error as NSError { + print("Error on fetch all by book Id: \(error)") + return [] + } + } + + /// Return all Bookmarks + /// + /// - Parameter readerConfig: - readerConfig: Current folio reader configuration. + /// - Returns: Return all Bookmarks + public static func all(withConfiguration readerConfig: FolioReaderConfig) -> [Bookmark] { + var bookmarks: [Bookmark]? + do { + let realm = try Realm(configuration: readerConfig.realmConfiguration) + bookmarks = realm.objects(Bookmark.self).toArray(Bookmark.self) + return bookmarks ?? [] + } catch let error { + print("Error on fetch all: \(error)") + return [] + } + } +} diff --git a/Source/Models/Bookmark.swift b/Source/Models/Bookmark.swift new file mode 100644 index 000000000..55e00cc7d --- /dev/null +++ b/Source/Models/Bookmark.swift @@ -0,0 +1,22 @@ +// +// Bookmark.swift +// FolioReaderKit +// +// Created by Omar Albeik on 26.03.2018. +// Copyright (c) 2015 Folio Reader. All rights reserved. +// + +import Foundation +import RealmSwift + +/// A Bookmark object +open class Bookmark: Object { + @objc open dynamic var bookId: String! + @objc open dynamic var date: Date! + @objc open dynamic var bookmarkId: String! + @objc open dynamic var page: Int = 0 + + override open class func primaryKey()-> String { + return "bookmarkId" + } +} diff --git a/Source/PageViewController.swift b/Source/PageViewController.swift index fcfaf6383..46fa5f29f 100644 --- a/Source/PageViewController.swift +++ b/Source/PageViewController.swift @@ -15,6 +15,7 @@ class PageViewController: UIPageViewController { var segmentedControlItems = [String]() var viewControllerOne: UIViewController! var viewControllerTwo: UIViewController! + var viewControllerThree: UIViewController! var index: Int fileprivate var readerConfig: FolioReaderConfig fileprivate var folioReader: FolioReader @@ -43,13 +44,22 @@ class PageViewController: UIPageViewController { segmentedControl.selectedSegmentIndex = index segmentedControl.setWidth(100, forSegmentAt: 0) segmentedControl.setWidth(100, forSegmentAt: 1) + segmentedControl.setWidth(100, forSegmentAt: 2) self.navigationItem.titleView = segmentedControl viewList = [viewControllerOne, viewControllerTwo] + if readerConfig.allowBookmarking { + viewList.append(viewControllerThree) + } + viewControllerOne.didMove(toParentViewController: self) viewControllerTwo.didMove(toParentViewController: self) + if readerConfig.allowBookmarking { + viewControllerThree.didMove(toParentViewController: self) + } + self.delegate = self self.dataSource = self diff --git a/Source/Resources/Images.xcassets/icon-navbar-bookmark-selected.imageset/Contents.json b/Source/Resources/Images.xcassets/icon-navbar-bookmark-selected.imageset/Contents.json new file mode 100644 index 000000000..54824e564 --- /dev/null +++ b/Source/Resources/Images.xcassets/icon-navbar-bookmark-selected.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon-navbar-bookmark-selected.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Source/Resources/Images.xcassets/icon-navbar-bookmark-selected.imageset/icon-navbar-bookmark-selected.pdf b/Source/Resources/Images.xcassets/icon-navbar-bookmark-selected.imageset/icon-navbar-bookmark-selected.pdf new file mode 100644 index 000000000..c6a1faec9 Binary files /dev/null and b/Source/Resources/Images.xcassets/icon-navbar-bookmark-selected.imageset/icon-navbar-bookmark-selected.pdf differ diff --git a/Source/Resources/Images.xcassets/icon-navbar-bookmark.imageset/Contents.json b/Source/Resources/Images.xcassets/icon-navbar-bookmark.imageset/Contents.json new file mode 100644 index 000000000..62366f8cf --- /dev/null +++ b/Source/Resources/Images.xcassets/icon-navbar-bookmark.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon-navbar-bookmark.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Source/Resources/Images.xcassets/icon-navbar-bookmark.imageset/icon-navbar-bookmark.pdf b/Source/Resources/Images.xcassets/icon-navbar-bookmark.imageset/icon-navbar-bookmark.pdf new file mode 100644 index 000000000..503de88af Binary files /dev/null and b/Source/Resources/Images.xcassets/icon-navbar-bookmark.imageset/icon-navbar-bookmark.pdf differ diff --git a/Source/UIBookmarkBarButtonItem.swift b/Source/UIBookmarkBarButtonItem.swift new file mode 100644 index 000000000..6fa8acb42 --- /dev/null +++ b/Source/UIBookmarkBarButtonItem.swift @@ -0,0 +1,34 @@ +// +// UIBookmarkBarButtonItem.swift +// AEXML +// +// Created by Omar Albeik on 26.03.2018. +// + +import UIKit + +class UIBookmarkBarButtonItem: UIBarButtonItem { + + var isHighlighed = false { + didSet { + image = UIImage(readerImageNamed: isHighlighed ? "icon-navbar-bookmark-selected" : "icon-navbar-bookmark") + } + } + + var readerConfig: FolioReaderConfig? { + didSet { + print("readerConfig") + guard let config = readerConfig else { return } + image = image?.ignoreSystemTint(withConfiguration: config) + } + } + + convenience init(target: Any?, action: Selector?, isHighlighed: Bool = false, readerConfig: FolioReaderConfig) { + + self.init(image: UIImage(readerImageNamed: "icon-navbar-bookmark"), style: .plain, target: target, action: action) + + self.isHighlighed = isHighlighed + self.readerConfig = readerConfig + } + +}