ios電子書翻頁(yè)效果代碼詳解
近實(shí)現(xiàn)了一個(gè)完整的電子書閱讀器,支持txt和epub格式的電子書閱讀,其中epub支持圖文混排的方式展示。本文主要談?wù)勂渲袃煞N翻頁(yè)效果的實(shí)現(xiàn),分別為仿真翻頁(yè)和水平滑動(dòng)翻頁(yè)。
仿真翻頁(yè)
最合適的方案就是使用系統(tǒng)提供的UIPageviewcontroller了,不過(guò)默認(rèn)的UIpageviewcontroller翻頁(yè)時(shí)背面是白色的,而閱讀器通常都會(huì)有背景色或背景圖片,翻頁(yè)時(shí)用戶體驗(yàn)就很糟糕,比如就像下面這樣

所以接下來(lái)主要說(shuō)說(shuō)如何修改背面顏色以達(dá)到美觀的翻頁(yè)效果。
UIpageviewcontroller有一個(gè)屬性叫做isDoubleSided,默認(rèn)為yes,也就是內(nèi)容只會(huì)在單面(正面)顯示,設(shè)置為no后,內(nèi)容便可以正面和背面雙面顯示,這時(shí)每翻一頁(yè),pageview的下面兩個(gè)回調(diào)會(huì)調(diào)用兩次
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController?
{
// 第一次回調(diào)索取背面的controller
// 第二次回調(diào)索取正面的controller
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController?
{
// 第一次回調(diào)索取背面的controller
// 第二次回調(diào)索取正面的controller
}
所以我們可以對(duì)正面的controller進(jìn)行反向截圖,并將其放在背面的controller上顯示,這樣整體翻頁(yè)效果就會(huì)很美觀了。
代碼示例
// 對(duì)輸入的controller進(jìn)行反向截圖
func grabViewController(viewController: DUAPageViewController) -> Void {
self.index = viewController.index
self.chapterBelong = viewController.chapterBelong
let rect = viewController.view.bounds
UIGraphicsBeginImageContextWithOptions(rect.size, true, 0.0)
let context = UIGraphicsGetCurrentContext()
let transform = CGAffineTransform(a: -1.0, b: 0.0, c: 0.0, d: 1.0, tx: rect.size.width, ty: 0.0)
context?.concatenate(transform)
viewController.view.layer.render(in: context!)
self.backImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
}
效果像下面這樣

左右平滑翻頁(yè)
最初采用的是UIpageviewcontroller的另一種翻頁(yè)模式——滾動(dòng)模式。該模式蘋果在底層是用scrollView實(shí)現(xiàn)的。但這種模式在頁(yè)面切換時(shí)存在一些問(wèn)題,由于蘋果會(huì)對(duì)相鄰的controller進(jìn)行緩存,當(dāng)調(diào)用open func setViewControllers(_ viewControllers: [UIViewController]?, direction: UIPageViewControllerNavigationDirection, animated: Bool, completion: ((Bool) -> Swift.Void)? = nil)方法并且動(dòng)畫為true時(shí),有時(shí)蘋果會(huì)錯(cuò)誤的認(rèn)為它已經(jīng)有了頁(yè)面的緩存而不再執(zhí)行數(shù)據(jù)源方法,從而引發(fā)一些問(wèn)題,更詳細(xì)的說(shuō)明可以看這篇文章,因此決定自己寫一個(gè)翻頁(yè)控件
DUATranslationController
DUATranslationController并沒(méi)有采用Scrollview的方式實(shí)現(xiàn),而是基于controller容器,通過(guò)替換child controller來(lái)實(shí)現(xiàn),具體來(lái)說(shuō)就是當(dāng)用戶點(diǎn)擊或者滑動(dòng)時(shí),判斷需要展示上一個(gè)頁(yè)面還是下一個(gè)頁(yè)面,然后模仿UIpageviewcontroller通過(guò)回調(diào)的方式索取controller,加入到controller容器中,并通過(guò)動(dòng)畫的方式將新的controller平滑移動(dòng)進(jìn)入屏幕,舊的controller同時(shí)移出,如下是單擊手勢(shì)代碼示例(滑動(dòng)手勢(shì)涉及和用戶交互,邏輯更復(fù)雜些,但基本思路是一致的)
@objc func handleTapGes(gesture: UITapGestureRecognizer) -> Void {
let hitPoint = gesture.location(in: gesture.view)
let curController = self.childViewControllers.first!
if hitPoint.x < gesture.view!.frame.size.width/3 {
// 滑向上一個(gè)controller
let lastController = self.delegate?.translationController(translationController: self, controllerBefore: curController)
if lastController != nil {
self.delegate?.translationController(translationController: self, willTransitionTo: lastController!)
self.setViewController(viewController: lastController!, direction: .right, animated: allowAnimating, completionHandler: {(complete) in
self.delegate?.translationController(translationController: self, didFinishAnimating: complete, previousController: curController, transitionCompleted: complete)
})
}
}
if hitPoint.x > gesture.view!.frame.size.width*2/3 {
// 滑向下一個(gè)controller
let nextController: UIViewController? = self.delegate?.translationController(translationController: self, controllerAfter: self.childViewControllers.first!)
if nextController != nil {
self.delegate?.translationController(translationController: self, willTransitionTo: nextController!)
self.setViewController(viewController: nextController!, direction: .left, animated: allowAnimating, completionHandler: {(complete) in
self.delegate?.translationController(translationController: self, didFinishAnimating: complete, previousController: curController, transitionCompleted: complete)
})
}
}
}
// 該方法模仿UIpageviewcontroller,切換到某一個(gè)controller
func setViewController(viewController: UIViewController, direction: translationControllerNavigationDirection, animated: Bool, completionHandler: ((Bool) -> Void)?) -> Void {
if animated == false {
// 直接添加child controller ,略
}else {
let oldController = self.childViewControllers.first
self.addController(controller: viewController)
var newVCEndTransform: CGAffineTransform
var oldVCEndTransform: CGAffineTransform
viewController.view.transform = .identity
if direction == .left {
viewController.view.transform = CGAffineTransform(translationX: screenWidth, y: 0)
newVCEndTransform = .identity
oldController?.view.transform = .identity
oldVCEndTransform = CGAffineTransform(translationX: -screenWidth, y: 0)
}else {
viewController.view.transform = CGAffineTransform(translationX: -screenWidth, y: 0)
newVCEndTransform = .identity
oldController?.view.transform = .identity
oldVCEndTransform = CGAffineTransform(translationX: screenWidth, y: 0)
}
UIView.animate(withDuration: animationDuration, animations: {
oldController?.view.transform = oldVCEndTransform
viewController.view.transform = newVCEndTransform
}, completion: { (complete) in
if complete {
self.removeController(controller: oldController!)
}
if completionHandler != nil {
completionHandler!(complete)
}
})
}
}
最終效果像這樣:

- iOS實(shí)現(xiàn)翻頁(yè)效果動(dòng)畫實(shí)例代碼
- iOS圖片界面翻頁(yè)切換效果
- iOS實(shí)現(xiàn)日歷翻頁(yè)動(dòng)畫
- 實(shí)例講解iOS中的UIPageViewController翻頁(yè)視圖控制器
- iOS開(kāi)發(fā)中使用屏幕旋轉(zhuǎn)功能的相關(guān)方法
- iOS開(kāi)發(fā)中控制屏幕旋轉(zhuǎn)的編寫方法小結(jié)
- iOS拍照后圖片自動(dòng)旋轉(zhuǎn)90度的完美解決方法
- IOS手勢(shì)操作(拖動(dòng)、捏合、旋轉(zhuǎn)、點(diǎn)按、長(zhǎng)按、輕掃、自定義)
- iOS漸變圓環(huán)旋轉(zhuǎn)動(dòng)畫CAShapeLayer CAGradientLayer
- iOS基于CATransition實(shí)現(xiàn)翻頁(yè)、旋轉(zhuǎn)等動(dòng)畫效果
相關(guān)文章
淺談Xcode 開(kāi)發(fā)工具 XCActionBar
本文主要給大家簡(jiǎn)單講解了Xcode的開(kāi)發(fā)工具 XCActionBar的介紹與使用方法,非常的全面實(shí)用,有需要的小伙伴可以參考下。2015-11-11
iOS開(kāi)發(fā)中使用UIWebView 屏蔽 alert警告框
這篇文章主要介紹了iOS開(kāi)發(fā)中使用UIWebView 屏蔽 alert警告框的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
iOS端React Native差異化增量更新的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于iOS端React Native差異化增量更新的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06
IOS開(kāi)發(fā)Objective-C?Runtime使用示例詳解
這篇文章主要為大家介紹了IOS開(kāi)發(fā)Objective-C?Runtime使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
簡(jiǎn)單談?wù)刢/c++中#import、#include和@class的區(qū)別
對(duì)于#import,我想做過(guò)iOS開(kāi)發(fā)的人應(yīng)該都不陌生。在開(kāi)發(fā)過(guò)程中,當(dāng)我們需要聲明某一個(gè)類時(shí),都需要去引用。而#imclude的話,在我們學(xué)習(xí)C時(shí)就已經(jīng)知道了,他的作用也是引用聲明的意思。在表面上他們的作用似乎都是一樣的。但是在具體功能實(shí)現(xiàn)方式上,還是有著很大的區(qū)別。2018-01-01
ios實(shí)現(xiàn)簡(jiǎn)易隊(duì)列
這篇文章主要為大家詳細(xì)介紹了ios實(shí)現(xiàn)簡(jiǎn)易隊(duì)列,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02

