// // FolioReaderCenter.swift // FolioReaderKit // // Created by Heberti Almeida on 08/04/15. // Copyright (c) 2015 Folio Reader. All rights reserved. // import UIKit 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: /// - page: The `FolioReaderPage`. /// - htmlContent: The current HTML content as `String`. /// - Returns: The adjusted HTML content as `String`. This is the content which will be loaded into the given `FolioReaderPage`. @objc optional func htmlContentForPage(_ page: FolioReaderPage, htmlContent: String) -> String /// Notifies that a page changed. This is triggered when collection view cell is changed. /// /// - 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! let collectionViewLayout = UICollectionViewFlowLayout() var loadingView: UIActivityIndicatorView! var pages: [String]! var totalPages: Int = 0 var tempFragment: String? var animator: ZFModalTransitionAnimator! var pageIndicatorView: FolioReaderPageIndicator? var pageIndicatorHeight: CGFloat = 20 var recentlyScrolled = false var recentlyScrolledDelay = 2.0 // 2 second delay until we clear recentlyScrolled var recentlyScrolledTimer: Timer! var scrollScrubber: ScrollScrubber? var activityIndicator = UIActivityIndicatorView() var isScrolling = false var pageScrollDirection = ScrollDirection() var nextPageNumber: Int = 0 var previousPageNumber: Int = 0 var currentPageNumber: Int = 0 var pageWidth: CGFloat = 0.0 var pageHeight: CGFloat = 0.0 var anchor: String! var lastRow: Int? fileprivate var screenBounds: CGRect! fileprivate var pointNow = CGPoint.zero fileprivate var pageOffsetRate: CGFloat = 0 fileprivate var tempReference: FRTocReference? fileprivate var isFirstLoad = true fileprivate var currentWebViewScrollPositions = [Int: CGPoint]() fileprivate var currentOrientation: UIInterfaceOrientation? fileprivate var changingChapter = false 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) loadingView.hidesWhenStopped = true 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 collectionViewLayout.minimumInteritemSpacing = 0 collectionViewLayout.scrollDirection = .direction(withConfiguration: self.readerConfig) let background = folioReader.isNight(self.readerConfig.nightModeBackground, UIColor.white) view.backgroundColor = background // CollectionView collectionView = UICollectionView(frame: screenBounds, collectionViewLayout: collectionViewLayout) collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight] collectionView.delegate = self collectionView.dataSource = self collectionView.isPagingEnabled = true collectionView.showsVerticalScrollIndicator = false collectionView.showsHorizontalScrollIndicator = false collectionView.backgroundColor = background collectionView.decelerationRate = UIScrollViewDecelerationRateFast enableScrollBetweenChapters(scrollEnabled: true) view.addSubview(collectionView) if #available(iOS 11.0, *) { collectionView.contentInsetAdjustmentBehavior = .never } // Activity Indicator self.activityIndicator.activityIndicatorViewStyle = .gray self.activityIndicator.hidesWhenStopped = true self.activityIndicator = UIActivityIndicatorView(frame: CGRect(x: screenBounds.size.width/2, y: screenBounds.size.height/2, width: 30, height: 30)) self.activityIndicator.backgroundColor = UIColor.gray self.view.addSubview(self.activityIndicator) self.view.bringSubview(toFront: self.activityIndicator) 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() pageIndicatorView = FolioReaderPageIndicator(frame: frame, readerConfig: readerConfig, folioReader: folioReader) if let pageIndicatorView = pageIndicatorView { view.addSubview(pageIndicatorView) } } guard let readerContainer = readerContainer else { return } self.scrollScrubber = ScrollScrubber(frame: frameForScrollScrubber(), withReaderContainer: readerContainer) self.scrollScrubber?.delegate = self if let scrollScrubber = scrollScrubber { 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) if #available(iOS 11.0, *) { bounds.size.height = bounds.size.height + view.safeAreaInsets.bottom } 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 greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00) let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor) let tintColor = folioReader.isNight(greenColor, UIColor.white)// readerConfig.tintColor let navText = tintColor// folioReader.isNight(UIColor.white, UIColor.black) 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 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 if(self.readerConfig.displayTitle){ 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) } else { 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 self.readerConfig.scrollDirection = direction self.collectionViewLayout.scrollDirection = .direction(withConfiguration: self.readerConfig) 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 } }) 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() } var isNextChapter = true if let lastRow = lastRow { isNextChapter = lastRow < indexPath.row } if changingChapter { isNextChapter = true } lastRow = indexPath.row cell.setup(withReaderContainer: readerContainer) cell.pageNumber = indexPath.row+1 cell.webView?.scrollView.delegate = self if #available(iOS 11.0, *) { cell.webView?.scrollView.contentInsetAdjustmentBehavior = .never } cell.webView?.setupScrollDirection() 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(sliderType: .font))" classes += " \(folioReader.currentMarginSize.cssIdentifier(sliderType: .margin))" classes += " \(folioReader.currentInterlineSize.cssIdentifier(sliderType: .interline))" html = html.replacingOccurrences(of: " CGSize { var size = CGSize(width: collectionView.frame.width, height: collectionView.frame.height) if #available(iOS 11.0, *) { let orientation = UIDevice.current.orientation if orientation == .portrait || orientation == .portraitUpsideDown { if readerConfig.scrollDirection == .horizontal { size.height = size.height - view.safeAreaInsets.bottom } } } return size } // 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)), CGSize(width: self.pageWidth * CGFloat(self.totalPages), height: self.pageHeight), CGSize(width: self.pageWidth * CGFloat(self.totalPages), height: self.pageHeight) ) 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: { self.collectionView.setContentOffset(self.frameForPage(self.currentPageNumber).origin, animated: false) }) } } // MARK: - Page func setPageSize(_ orientation: UIInterfaceOrientation) { guard orientation.isPortrait else { if screenBounds.size.width > screenBounds.size.height { self.pageWidth = screenBounds.size.width self.pageHeight = screenBounds.size.height } else { self.pageWidth = screenBounds.size.height self.pageHeight = screenBounds.size.width } return } if screenBounds.size.width < screenBounds.size.height { self.pageWidth = screenBounds.size.width self.pageHeight = screenBounds.size.height } else { self.pageWidth = screenBounds.size.height self.pageHeight = screenBounds.size.width } } func updateCurrentPage(_ page: FolioReaderPage? = nil, completion: (() -> Void)? = nil) { if let page = page { currentPage = page self.previousPageNumber = page.pageNumber-1 self.currentPageNumber = page.pageNumber } 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 { indexPath = last } else { indexPath = first } default: if first.compare(last) == .orderedAscending { indexPath = first } else { indexPath = last } } } 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), CGRect(x: self.pageWidth * CGFloat(page-1), y: 0, width: self.pageWidth, height: self.pageHeight), 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 != "" { currentPage.handleAnchor(fragment, avoidBeginningAnchors: true, animated: animated) } completion?() } else { tempFragment = fragment changePageWith(page: page, animated: animated, completion: { () -> Void in self.updateCurrentPage { completion?() } }) } } open func changePageWith(href: String, animated: Bool = false, completion: (() -> Void)? = nil) { let item = findPageByHref(href) let indexPath = IndexPath(row: item, section: 0) changePageWith(indexPath: indexPath, animated: animated, completion: { () -> Void in self.updateCurrentPage { completion?() } }) } 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) changePageWith(indexPath: indexPath, animated: true) { () -> Void in if pageUpdateNeeded { self.updateCurrentPage { currentPage.audioMarkID(markID) } } else { currentPage.audioMarkID(markID) } } } 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 completion?() } } open func changePageWith(href: String, pageItem: Int, animated: Bool = false, completion: (() -> Void)? = nil) { changePageWith(href: href, animated: animated) { 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?() } } public func changePageItemToNext(_ completion: (() -> Void)? = nil) { // TODO: It was implemented for horizontal orientation. // Need check page orientation (v/h) and make correct calc for vertical guard let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage, let contentOffset = cell.webView?.scrollView.contentOffset, let contentOffsetXLimit = cell.webView?.scrollView.contentSize.width else { completion?() return } let cellSize = cell.frame.size let contentOffsetX = contentOffset.x + cellSize.width if contentOffsetX >= contentOffsetXLimit { changePageToNext(completion) } else { cell.scrollPageToOffset(contentOffsetX, animated: true) } completion?() } public func getCurrentPageItemNumber() -> Int { guard let page = currentPage, let webView = page.webView else { return 0 } let pageSize = readerConfig.isDirection(pageHeight, pageWidth, pageHeight) let pageOffSet = readerConfig.isDirection(webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.x, webView.scrollView.contentOffset.y) let webViewPage = pageForOffset(pageOffSet, pageHeight: pageSize) 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 guard let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage, let contentOffset = cell.webView?.scrollView.contentOffset else { completion?() return } let cellSize = cell.frame.size let contentOffsetX = contentOffset.x - cellSize.width if contentOffsetX < 0 { changePageToPrevious(completion) } else { cell.scrollPageToOffset(contentOffsetX, animated: true) } 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 guard let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage, let contentSize = cell.webView?.scrollView.contentSize else { completion?() return } let cellSize = cell.frame.size var contentOffsetX: CGFloat = 0.0 if contentSize.width > 0 && cellSize.width > 0 { contentOffsetX = (cellSize.width * (contentSize.width / cellSize.width)) - cellSize.width } if contentOffsetX < 0 { contentOffsetX = 0 } cell.scrollPageToOffset(contentOffsetX, animated: animated) 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 guard let cell = collectionView.cellForItem(at: getCurrentIndexPath()) as? FolioReaderPage, let contentSize = cell.webView?.scrollView.contentSize else { delegate?.pageItemChanged?(getCurrentPageItemNumber()) completion?() return } let cellSize = cell.frame.size var contentOffsetX: CGFloat = 0.0 if contentSize.width > 0 && cellSize.width > 0 { contentOffsetX = (cellSize.width * CGFloat(to)) - cellSize.width } if contentOffsetX > contentSize.width { contentOffsetX = contentSize.width - cellSize.width } if contentOffsetX < 0 { contentOffsetX = 0 } UIView.animate(withDuration: animated ? 0.3 : 0, delay: 0, options: UIViewAnimationOptions(), animations: { () -> Void in cell.scrollPageToOffset(contentOffsetX, animated: animated) }) { (finished: Bool) -> Void in self.updateCurrentPage { completion?() } } } /** Find a page by FRTocReference. */ public func findPageByResource(_ reference: FRTocReference) -> Int { var count = 0 for item in self.book.spine.spineReferences { if let resource = reference.resource, item.resource == resource { return count } count += 1 } return count } /** Find a page by href. */ public func findPageByHref(_ href: String) -> Int { var count = 0 for item in self.book.spine.spineReferences { if item.resource.href == href { return count } count += 1 } return count } /** Find and return the current chapter resource. */ public func getCurrentChapter() -> FRResource? { for item in self.book.flatTableOfContents { if let reference = self.book.spine.spineReferences[safe: (self.currentPageNumber - 1)], let resource = item.resource, (resource == reference.resource) { return item.resource } } return nil } /** Return the current chapter progress based on current chapter and total of chapters. */ public func getCurrentChapterProgress() -> CGFloat { let total = totalPages let current = currentPageNumber if total == 0 { return 0 } return CGFloat((100 * current) / total) } /** Find and return the current chapter name. */ public func getCurrentChapterName() -> String? { for item in self.book.flatTableOfContents { guard let reference = self.book.spine.spineReferences[safe: (self.currentPageNumber - 1)], let resource = item.resource, (resource == reference.resource), 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. */ public func changePageWith(page: Int, animated: Bool = false, completion: (() -> Void)? = nil) { if page > 0 && page-1 < totalPages { let indexPath = IndexPath(row: page-1, section: 0) changePageWith(indexPath: indexPath, animated: animated, completion: { () -> Void in self.updateCurrentPage { completion?() } }) } } // 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 var html = "" var text = "" var bookTitle = "" 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) } } /** Sharing highlight method. */ func shareHighlight(_ string: String, rect: CGRect) { var subject = readerConfig.localizedShareHighlightSubject var html = "" var text = "" var bookTitle = "" 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)

" html += "

\(string)



" 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 } else { self.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset } } if (pageIndicatorView?.currentPage != webViewPage) { pageIndicatorView?.currentPage = webViewPage } 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) { self.pageScrollDirection = .positive(withConfiguration: self.readerConfig, scrollType: scrollType) } else if (isCurrentlyPositive == true) { self.pageScrollDirection = .negative(withConfiguration: self.readerConfig, scrollType: scrollType) } else { 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), let cell = ((scrollView.superview as? UIWebView)?.delegate as? FolioReaderPage) { let currentIndexPathRow = cell.pageNumber - 1 self?.currentWebViewScrollPositions[currentIndexPathRow] = scrollView.contentOffset } if (scrollView is UICollectionView) { guard let instance = self else { return } if instance.totalPages > 0 { instance.updateCurrentPage() instance.delegate?.pageItemChanged?(instance.getCurrentPageItemNumber()) } } else { self?.scrollScrubber?.scrollViewDidEndDecelerating(scrollView) } }) } open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { if scrollView == collectionView{ scrollView.isUserInteractionEnabled = false delay(0.5, closure: { [weak self] in scrollView.isUserInteractionEnabled = true }) } 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() recentlyScrolledTimer = nil } recentlyScrolled = false } open func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { scrollScrubber?.scrollViewDidEndScrollingAnimation(scrollView) } // MARK: NavigationBar Actions @objc func closeReader(_ sender: UIBarButtonItem) { dismiss() folioReader.close() } //PD: changed /** Present chapter list */ @objc func presentChapterList(_ sender: UIBarButtonItem) { folioReader.saveReaderState() let chapter = FolioReaderChapterList(folioReader: folioReader, readerConfig: readerConfig, book: book, delegate: self) chapter.title = "Spis treści" // let highlight = FolioReaderHighlightList(folioReader: folioReader, readerConfig: readerConfig) // let pageController = PageViewController(folioReader: folioReader, readerConfig: readerConfig) // pageController.viewControllerOne = chapter // pageController.viewControllerTwo = highlight // pageController.segmentedControlItems = [readerConfig.localizedContentsTitle, readerConfig.localizedHighlightsTitle] let vc = FolioReaderModalViewController(folioReader: folioReader, readerConfig: readerConfig) vc.viewControllerOne = chapter let nav = UINavigationController(rootViewController:vc /*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 animator.behindViewAlpha = 0.4 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 animator.behindViewAlpha = 0.4 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 } present(nav, animated: true, completion: nil) } } // 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) } } else if (self.isScrolling == false && folioReader.needsRTLChange == true) { page.scrollPageToBottom() } } else if isFirstLoad { updateCurrentPage(page) isFirstLoad = false } // Go to fragment if needed if let fragmentID = tempFragment, let currentPage = currentPage , fragmentID != "" { currentPage.handleAnchor(fragmentID, avoidBeginningAnchors: true, animated: true) tempFragment = nil } if (readerConfig.scrollDirection == .horizontalWithVerticalContent), let offsetPoint = self.currentWebViewScrollPositions[page.pageNumber - 1] { page.webView?.scrollView.setContentOffset(offsetPoint, animated: false) } // Pass the event to the centers `pageDelegate` pageDelegate?.pageDidLoad?(page) } public func pageWillLoad(_ page: FolioReaderPage) { // Pass the event to the centers `pageDelegate` pageDelegate?.pageWillLoad?(page) } public func pageTap(_ recognizer: UITapGestureRecognizer) { // Pass the event to the centers `pageDelegate` pageDelegate?.pageTap?(recognizer) } } // MARK: FolioReaderChapterListDelegate extension FolioReaderCenter: FolioReaderChapterListDelegate { func chapterList(_ chapterList: FolioReaderChapterList, didSelectRowAtIndexPath indexPath: IndexPath, withTocReference reference: FRTocReference) { let item = findPageByResource(reference) if item < totalPages { let indexPath = IndexPath(row: item, section: 0) changingChapter = true changePageWith(indexPath: indexPath, animated: false, completion: { () -> Void in self.updateCurrentPage() self.changingChapter = false }) tempReference = reference } else { print("Failed to load book because the requested resource is missing.") } } func chapterList(didDismissedChapterList chapterList: FolioReaderChapterList) { updateCurrentPage() // Move to #fragment if let reference = tempReference { if let fragmentID = reference.fragmentID, let currentPage = currentPage , fragmentID != "" { currentPage.handleAnchor(reference.fragmentID!, avoidBeginningAnchors: true, animated: true) } tempReference = nil } } func getScreenBounds() -> CGRect { var bounds = view.frame if #available(iOS 11.0, *) { bounds.size.height = bounds.size.height - view.safeAreaInsets.bottom } return bounds } } class FolioReaderModalViewController: UIViewController{ var viewControllerOne: UIViewController! fileprivate var readerConfig: FolioReaderConfig fileprivate var folioReader: FolioReader // MARK: Init init(folioReader: FolioReader, readerConfig: FolioReaderConfig) { self.folioReader = folioReader self.readerConfig = readerConfig super.init(nibName: nil, bundle: nil) self.edgesForExtendedLayout = UIRectEdge() self.extendedLayoutIncludesOpaqueBars = true } required init?(coder: NSCoder) { fatalError("storyboards are incompatible with truth and beauty") } override func viewDidLoad() { super.viewDidLoad() self.navigationItem.title = viewControllerOne.title addChildViewController(viewControllerOne) viewControllerOne.view.frame = view.bounds view.addSubview(viewControllerOne.view) viewControllerOne.view.translatesAutoresizingMaskIntoConstraints = false if #available(iOS 9.0, *) { viewControllerOne.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true viewControllerOne.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true viewControllerOne.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true viewControllerOne.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true } else { // Fallback on earlier versions } viewControllerOne.didMove(toParentViewController: self) // self.view.backgroundColor = UIColor.white // self.setViewControllers([viewList[index]], direction: .forward, animated: false, completion: nil) //PD: changed let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00) let tintColor = folioReader.isNight(greenColor, UIColor.white) let closeImage = UIImage(readerImageNamed: "icon-navbar-close")?.imageTintColor(tintColor)?.withRenderingMode(.alwaysOriginal)// ignoreSystemTint(withConfiguration: readerConfig) self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: closeImage, style: .plain, target: self, action: #selector(dismiss as () -> Void)) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) configureNavBar() } func configureNavBar() { let greenColor = UIColor(red:0.00, green:0.51, blue:0.53, alpha:1.00) let navBackground = folioReader.isNight(self.readerConfig.nightModeMenuBackground, greenColor) let tintColor = folioReader.isNight(greenColor, UIColor.white) let navText = tintColor let font = UIFont(name: "Avenir-Light", size: 17)! setTranslucentNavigation(color: navBackground, tintColor: tintColor, titleColor: navText, andFont: font) } //PD: changed override var preferredStatusBarStyle : UIStatusBarStyle { return .lightContent //self.folioReader.isNight(.lightContent, .default) } }