├── .DS_Store ├── .gitignore ├── DZMeBookRead.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── DZMeBookRead ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── DZMeBookRead │ │ ├── Contents.json │ │ ├── back.imageset │ │ ├── Contents.json │ │ ├── back@2x.png │ │ └── back@3x.png │ │ ├── bar_0.imageset │ │ ├── Contents.json │ │ ├── bar_0@2x.png │ │ └── bar_0@3x.png │ │ ├── bar_1.imageset │ │ ├── Contents.json │ │ ├── bar_1@2x.png │ │ └── bar_1@3x.png │ │ ├── bar_2.imageset │ │ ├── Contents.json │ │ ├── bar_2@2x.png │ │ └── bar_2@3x.png │ │ ├── battery_black.imageset │ │ ├── Contents.json │ │ ├── battery_black@2x.png │ │ └── battery_black@3x.png │ │ ├── light_0.imageset │ │ ├── Contents.json │ │ ├── light_0@2x.png │ │ └── light_0@3x.png │ │ ├── light_1.imageset │ │ ├── Contents.json │ │ ├── light_1@2x.png │ │ └── light_1@3x.png │ │ ├── mark.imageset │ │ ├── Contents.json │ │ ├── mark@2x.png │ │ └── mark@3x.png │ │ ├── page.imageset │ │ ├── Contents.json │ │ └── page.png │ │ ├── read_bg_0.imageset │ │ ├── Contents.json │ │ ├── read_bg_0@2x.png │ │ └── read_bg_0@3x.png │ │ ├── read_bg_0_icon.imageset │ │ ├── Contents.json │ │ └── read_bg_0_icon@2x.png │ │ ├── slider.imageset │ │ ├── Contents.json │ │ ├── slider@2x.png │ │ └── slider@3x.png │ │ ├── spacing_0.imageset │ │ ├── Contents.json │ │ ├── spacing_0@2x.png │ │ └── spacing_0@3x.png │ │ ├── spacing_1.imageset │ │ ├── Contents.json │ │ ├── spacing_1@2x.png │ │ └── spacing_1@3x.png │ │ └── spacing_2.imageset │ │ ├── Contents.json │ │ ├── spacing_2@2x.png │ │ └── spacing_2@3x.png ├── Base.lproj │ └── LaunchScreen.storyboard ├── DZMMainController.swift ├── DZMeBookRead-Bridging-Header.h ├── DZMeBookRead │ ├── ReadParser │ │ ├── DZMReadParser.swift │ │ ├── DZMReadTextFastParser.swift │ │ └── DZMReadTextParser.swift │ ├── ReadView │ │ ├── controller │ │ │ ├── DZMReadLongPressViewController.swift │ │ │ ├── DZMReadViewBGController.swift │ │ │ ├── DZMReadViewController.swift │ │ │ └── DZMReadViewScrollController.swift │ │ └── view │ │ │ ├── DZMReadHomeView.swift │ │ │ ├── DZMReadHomeViewCell.swift │ │ │ ├── DZMReadLongPressCursorView.swift │ │ │ ├── DZMReadLongPressView.swift │ │ │ ├── DZMReadView.swift │ │ │ ├── DZMReadViewCell.swift │ │ │ ├── DZMReadViewStatusBottomView.swift │ │ │ └── DZMReadViewStatusTopView.swift │ ├── controller │ │ ├── DZMReadConfigure.swift │ │ ├── DZMReadController+EffectType.swift │ │ ├── DZMReadController+Operation.swift │ │ └── DZMReadController.swift │ ├── menuUI │ │ ├── DZMRMBaseView.swift │ │ ├── DZMRMBottomView │ │ │ ├── DZMRMBottomView.swift │ │ │ ├── DZMRMFuncView.swift │ │ │ └── DZMRMProgressView.swift │ │ ├── DZMRMSettingView │ │ │ ├── DZMRMBGColorView.swift │ │ │ ├── DZMRMEffectTypeView.swift │ │ │ ├── DZMRMFontSizeView.swift │ │ │ ├── DZMRMFontTypeView.swift │ │ │ ├── DZMRMLightView.swift │ │ │ ├── DZMRMSettingView.swift │ │ │ └── DZMRMSpacingView.swift │ │ ├── DZMRMTopView.swift │ │ └── DZMReadMenu.swift │ ├── model │ │ ├── DZMReadChapterListModel.swift │ │ ├── DZMReadChapterModel.swift │ │ ├── DZMReadMarkModel.swift │ │ ├── DZMReadModel+Mark.swift │ │ ├── DZMReadModel.swift │ │ ├── DZMReadPageModel.swift │ │ └── DZMReadRecordModel.swift │ ├── other │ │ ├── DZMeBookRead-Bridging-Pch.h │ │ ├── base │ │ │ ├── DZMPageViewController.swift │ │ │ ├── DZMTableView.swift │ │ │ └── DZMViewController.swift │ │ ├── custom │ │ │ ├── DZMCoreText.swift │ │ │ ├── DZMKeyedArchiver.swift │ │ │ └── DZMUserDefaults.swift │ │ ├── extension │ │ │ ├── String+Extension.swift │ │ │ └── UIPageViewController+Extension.swift │ │ ├── public │ │ │ ├── DZMColorList.swift │ │ │ ├── DZMEnumList.swift │ │ │ ├── DZMFontList.swift │ │ │ ├── DZMPublic.swift │ │ │ └── DZMSpaceList.swift │ │ └── thirdParty │ │ │ ├── ASValueTrackingSlider │ │ │ ├── ASValuePopUpView.h │ │ │ ├── ASValuePopUpView.m │ │ │ ├── ASValueTrackingSlider.h │ │ │ └── ASValueTrackingSlider.m │ │ │ ├── DZMBatteryView.swift │ │ │ ├── DZMCoverController │ │ │ ├── DZMCoverController.h │ │ │ └── DZMCoverController.m │ │ │ ├── DZMMagnifierView │ │ │ ├── DZMMagnifierView.h │ │ │ ├── DZMMagnifierView.m │ │ │ ├── magnifier_0.png │ │ │ └── magnifier_1.png │ │ │ ├── DZMSegmentedControl │ │ │ ├── DZMSegmentedControl.h │ │ │ └── DZMSegmentedControl.m │ │ │ └── FDFullscreenPopGesture │ │ │ ├── UINavigationController+FDFullscreenPopGesture.h │ │ │ └── UINavigationController+FDFullscreenPopGesture.m │ └── view │ │ ├── DZMReadContentView.swift │ │ └── DZMReadLeftView │ │ ├── DZMReadCatalogCell.swift │ │ ├── DZMReadCatalogView.swift │ │ ├── DZMReadLeftView.swift │ │ ├── DZMReadMarkCell.swift │ │ └── DZMReadMarkView.swift ├── Info.plist ├── MBProgressHUD+DZM │ ├── MBProgressHUD+DZM.h │ ├── MBProgressHUD+DZM.m │ ├── MBProgressHUD.bundle │ │ ├── error.png │ │ ├── error@2x.png │ │ ├── error@3x.png │ │ ├── right.png │ │ ├── right@2x.png │ │ └── right@3x.png │ ├── MBProgressHUD.h │ └── MBProgressHUD.m ├── en.txt └── 求魔.txt ├── DZMeBookReadTests ├── DZMeBookReadTests.swift └── Info.plist ├── DZMeBookReadUITests ├── DZMeBookReadUITests.swift └── Info.plist ├── LICENSE ├── README.md ├── gif_0.gif ├── gif_1.gif └── icon_0.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | .DS_Store 6 | .DS_Store? 7 | */.DS_Store 8 | 9 | ## Build generated 10 | build/ 11 | DerivedData 12 | 13 | ## Various settings 14 | *.pbxuser 15 | !default.pbxuser 16 | *.mode1v3 17 | !default.mode1v3 18 | *.mode2v3 19 | !default.mode2v3 20 | *.perspectivev3 21 | !default.perspectivev3 22 | xcuserdata 23 | 24 | ## Other 25 | *.xccheckout 26 | *.moved-aside 27 | *.xcuserstate 28 | *.xcscmblueprint 29 | *.xcscheme 30 | 31 | ## Obj-C/Swift specific 32 | *.hmap 33 | *.ipa 34 | 35 | # CocoaPods 36 | # 37 | # We recommend against adding the Pods directory to your .gitignore. However 38 | # you should judge for yourself, the pros and cons are mentioned at: 39 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 40 | # 41 | # Pods/ 42 | 43 | # Carthage 44 | # 45 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 46 | # Carthage/Checkouts 47 | 48 | Carthage/Build 49 | 50 | # fastlane 51 | # 52 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 53 | # screenshots whenever they are needed. 54 | # For more information about the recommended setup visit: 55 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 56 | 57 | fastlane/report.xml 58 | fastlane/screenshots -------------------------------------------------------------------------------- /DZMeBookRead.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DZMeBookRead.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DZMeBookRead/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | 18 | application.setStatusBarHidden(false, with: UIStatusBarAnimation.fade) 19 | 20 | window = UIWindow(frame:UIScreen.main.bounds) 21 | 22 | window!.makeKeyAndVisible() 23 | 24 | let vc = DZMMainController() 25 | 26 | let navVC = UINavigationController(rootViewController:vc) 27 | 28 | window!.rootViewController = navVC 29 | 30 | return true 31 | } 32 | 33 | func applicationWillResignActive(_ application: UIApplication) { 34 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 35 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 36 | } 37 | 38 | func applicationDidEnterBackground(_ application: UIApplication) { 39 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 41 | } 42 | 43 | func applicationWillEnterForeground(_ application: UIApplication) { 44 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 45 | } 46 | 47 | func applicationDidBecomeActive(_ application: UIApplication) { 48 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 49 | } 50 | 51 | func applicationWillTerminate(_ application: UIApplication) { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "back@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/back.imageset/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/back.imageset/back@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/back.imageset/back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/back.imageset/back@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "bar_0@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "bar_0@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_0.imageset/bar_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_0.imageset/bar_0@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_0.imageset/bar_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_0.imageset/bar_0@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "bar_1@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "bar_1@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_1.imageset/bar_1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_1.imageset/bar_1@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_1.imageset/bar_1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_1.imageset/bar_1@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "bar_2@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "bar_2@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_2.imageset/bar_2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_2.imageset/bar_2@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_2.imageset/bar_2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/bar_2.imageset/bar_2@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/battery_black.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "battery_black@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "battery_black@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/battery_black.imageset/battery_black@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/battery_black.imageset/battery_black@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/battery_black.imageset/battery_black@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/battery_black.imageset/battery_black@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/light_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "light_0@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "light_0@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/light_0.imageset/light_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/light_0.imageset/light_0@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/light_0.imageset/light_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/light_0.imageset/light_0@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/light_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "light_1@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "light_1@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/light_1.imageset/light_1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/light_1.imageset/light_1@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/light_1.imageset/light_1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/light_1.imageset/light_1@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/mark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "mark@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "mark@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/mark.imageset/mark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/mark.imageset/mark@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/mark.imageset/mark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/mark.imageset/mark@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/page.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "page.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/page.imageset/page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/page.imageset/page.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/read_bg_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "read_bg_0@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "read_bg_0@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/read_bg_0.imageset/read_bg_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/read_bg_0.imageset/read_bg_0@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/read_bg_0.imageset/read_bg_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/read_bg_0.imageset/read_bg_0@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/read_bg_0_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "read_bg_0_icon@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/read_bg_0_icon.imageset/read_bg_0_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/read_bg_0_icon.imageset/read_bg_0_icon@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/slider.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "slider@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "slider@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/slider.imageset/slider@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/slider.imageset/slider@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/slider.imageset/slider@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/slider.imageset/slider@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_0.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "spacing_0@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "spacing_0@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_0.imageset/spacing_0@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_0.imageset/spacing_0@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_0.imageset/spacing_0@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_0.imageset/spacing_0@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "spacing_1@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "spacing_1@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_1.imageset/spacing_1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_1.imageset/spacing_1@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_1.imageset/spacing_1@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_1.imageset/spacing_1@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "spacing_2@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "spacing_2@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_2.imageset/spacing_2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_2.imageset/spacing_2@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_2.imageset/spacing_2@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/Assets.xcassets/DZMeBookRead/spacing_2.imageset/spacing_2@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | /// 桥接头文件 6 | #import "DZMeBookRead-Bridging-Pch.h" 7 | 8 | /// 零时使用用于阻挡的 跟项目无关 9 | #import "MBProgressHUD+DZM.h" 10 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadParser/DZMReadParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadParser.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// 解析完成 12 | typealias DZMParserCompletion = (_ readModel:DZMReadModel?) ->Void 13 | 14 | class DZMReadParser: NSObject { 15 | 16 | // MARK: -- 内容分页 17 | 18 | /// 内容分页 19 | /// 20 | /// - Parameters: 21 | /// - attrString: 内容 22 | /// - rect: 显示范围 23 | /// - isFirstChapter: 是否为本文章第一个展示章节,如果是则加入书籍首页。(小技巧:如果不需要书籍首页,可不用传,默认就是不带书籍首页) 24 | /// - Returns: 内容分页列表 25 | @objc class func pageing(attrString:NSAttributedString, rect:CGRect, isFirstChapter:Bool = false) ->[DZMReadPageModel] { 26 | 27 | var pageModels:[DZMReadPageModel] = [] 28 | 29 | if isFirstChapter { // 第一页为书籍页面 30 | 31 | let pageModel = DZMReadPageModel() 32 | 33 | pageModel.range = NSMakeRange(DZM_READ_BOOK_HOME_PAGE, 1) 34 | 35 | pageModel.contentSize = DZM_READ_VIEW_RECT.size 36 | 37 | pageModels.append(pageModel) 38 | } 39 | 40 | let ranges = DZMCoreText.GetPageingRanges(attrString: attrString, rect: rect) 41 | 42 | if !ranges.isEmpty { 43 | 44 | let count = ranges.count 45 | 46 | for i in 0..String { 102 | 103 | // 替换单换行 104 | var content = content.replacingOccurrences(of: "\r", with: "") 105 | 106 | // 替换换行 以及 多个换行 为 换行加空格 107 | content = content.replacingCharacters("\\s*\\n+\\s*", "\n" + DZM_READ_PH_SPACE) 108 | 109 | // 返回 110 | return content 111 | } 112 | 113 | 114 | // MARK: -- 解码URL 115 | 116 | /// 解码URL 117 | /// 118 | /// - Parameter url: 文件路径 119 | /// - Returns: 内容 120 | @objc class func encode(url:URL) ->String { 121 | 122 | var content = "" 123 | 124 | if url.absoluteString.isEmpty { return content } 125 | 126 | // utf8 127 | content = encode(url: url, encoding: String.Encoding.utf8.rawValue) 128 | 129 | // 进制编码 130 | if content.isEmpty { content = encode(url: url, encoding: 0x80000632) } 131 | 132 | if content.isEmpty { content = encode(url: url, encoding: 0x80000631) } 133 | 134 | if content.isEmpty { content = "" } 135 | 136 | return content 137 | } 138 | 139 | /// 解析URL 140 | /// 141 | /// - Parameters: 142 | /// - url: 文件路径 143 | /// - encoding: 进制编码 144 | /// - Returns: 内容 145 | @objc class func encode(url:URL, encoding:UInt) ->String { 146 | 147 | do{ 148 | return try NSString(contentsOf: url, encoding: encoding) as String 149 | 150 | }catch{} 151 | 152 | return "" 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/controller/DZMReadLongPressViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadLongPressViewController.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/30. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadLongPressViewController: DZMReadViewController { 12 | 13 | /// 阅读视图 14 | private var readView:DZMReadLongPressView! 15 | 16 | // 初始化阅读视图 17 | override func initReadView() { 18 | 19 | // 是否为书籍首页 20 | if recordModel.pageModel.isHomePage { 21 | 22 | super.initReadView() 23 | 24 | }else{ 25 | 26 | // 阅读视图范围 27 | let rect = DZM_READ_VIEW_RECT! 28 | 29 | // 长按功能需要内容高度防止拖拽超出界限 30 | let pageModel = recordModel.pageModel! 31 | 32 | // 阅读视图 33 | readView = DZMReadLongPressView() 34 | readView.pageModel = pageModel 35 | view.addSubview(readView) 36 | readView.frame = CGRect(x: rect.minX, y: rect.minY, width: rect.width, height: pageModel.contentSize.height) 37 | } 38 | } 39 | 40 | // MARK: 页面触摸拖拽处理 41 | 42 | /// 触摸开始 43 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 44 | 45 | drag(touches: touches, status: .begin) 46 | } 47 | 48 | /// 触摸移动 49 | override func touchesMoved(_ touches: Set, with event: UIEvent?) { 50 | 51 | drag(touches: touches, status: .changed) 52 | } 53 | 54 | /// 触摸结束 55 | override func touchesEnded(_ touches: Set, with event: UIEvent?) { 56 | 57 | drag(touches: touches, status: .end) 58 | } 59 | 60 | /// 触摸取消 61 | override func touchesCancelled(_ touches: Set, with event: UIEvent?) { 62 | 63 | drag(touches: touches, status: .end) 64 | } 65 | 66 | /// 解析触摸事件 67 | private func drag(touches: Set, status: DZMPanGesStatus) { 68 | 69 | // 是否为书籍首页 70 | if recordModel.pageModel.isHomePage { return } 71 | 72 | if readView?.isOpenDrag ?? false { 73 | 74 | let windowPoint = ((touches as NSSet).anyObject() as? UITouch)?.location(in: view) 75 | 76 | if windowPoint != nil { 77 | 78 | let point = view.convert(windowPoint!, to: readView) 79 | 80 | readView?.drag(status: status, point: point, windowPoint: windowPoint!) 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/controller/DZMReadViewBGController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadViewBGController.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/24. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadViewBGController: DZMViewController { 12 | 13 | /// 当前页阅读记录 14 | var recordModel:DZMReadRecordModel! 15 | 16 | /// 目标视图(无值则跟阅读背景颜色保持一致) 17 | var targetView:UIView! 18 | 19 | /// imageView 20 | private var imageView:UIImageView! 21 | 22 | override func viewDidLoad() { 23 | 24 | super.viewDidLoad() 25 | 26 | // imageView 27 | imageView = UIImageView() 28 | imageView.backgroundColor = DZMReadConfigure.shared().bgColor 29 | view.addSubview(imageView) 30 | imageView.frame = view.bounds 31 | 32 | // 显示背面 33 | // funcOne() 34 | funcTwo() 35 | 36 | // 清空视图 37 | targetView = nil 38 | } 39 | 40 | // MARK: 方式一 41 | 42 | /// 方式一 43 | private func funcOne() { 44 | 45 | // 展示图片 46 | if targetView != nil { 47 | 48 | imageView.layer.transform = CATransform3DMakeRotation(CGFloat.pi, 0, 1, 0) 49 | 50 | imageView.image = ScreenCapture(targetView) 51 | } 52 | } 53 | 54 | // MARK: 方式二 55 | 56 | /// 方式二 57 | private func funcTwo() { 58 | 59 | // 展示图片 60 | if targetView != nil { 61 | 62 | let rect = targetView.frame 63 | 64 | UIGraphicsBeginImageContextWithOptions(rect.size, true, 0.0) 65 | 66 | let context = UIGraphicsGetCurrentContext() 67 | 68 | let transform = CGAffineTransform(a: -1.0, b: 0.0, c: 0.0, d: 1.0, tx: rect.size.width, ty: 0.0) 69 | 70 | context?.concatenate(transform) 71 | 72 | targetView.layer.render(in: context!) 73 | 74 | imageView.image = UIGraphicsGetImageFromCurrentImageContext() 75 | 76 | UIGraphicsEndImageContext() 77 | } 78 | } 79 | 80 | override func didReceiveMemoryWarning() { 81 | 82 | super.didReceiveMemoryWarning() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/controller/DZMReadViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadViewController.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadViewController: DZMViewController { 12 | 13 | /// 当前页阅读记录对象 14 | var recordModel:DZMReadRecordModel! 15 | 16 | /// 阅读对象(用于显示书名以及书籍首页显示书籍信息) 17 | weak var readModel:DZMReadModel! 18 | 19 | /// 顶部状态栏 20 | var topView:DZMReadViewStatusTopView! 21 | 22 | /// 底部状态栏 23 | var bottomView:DZMReadViewStatusBottomView! 24 | 25 | /// 阅读视图 26 | private var readView:DZMReadView! 27 | 28 | /// 书籍首页视图 29 | private var homeView:DZMReadHomeView! 30 | 31 | override func viewDidLoad() { 32 | 33 | super.viewDidLoad() 34 | 35 | // 设置阅读背景 36 | view.backgroundColor = DZMReadConfigure.shared().bgColor 37 | 38 | // 刷新阅读进度 39 | reloadProgress() 40 | } 41 | 42 | override func addSubviews() { 43 | 44 | super.addSubviews() 45 | 46 | // 阅读使用范围 47 | let readRect = DZM_READ_RECT! 48 | 49 | // 顶部状态栏 50 | topView = DZMReadViewStatusTopView() 51 | topView.bookName.text = readModel.bookName 52 | topView.chapterName.text = recordModel.chapterModel.name 53 | view.addSubview(topView) 54 | topView.frame = CGRect(x: readRect.minX, y: readRect.minY, width: readRect.width, height: DZM_READ_STATUS_TOP_VIEW_HEIGHT) 55 | 56 | // 底部状态栏 57 | bottomView = DZMReadViewStatusBottomView() 58 | view.addSubview(bottomView) 59 | bottomView.frame = CGRect(x: readRect.minX, y: readRect.maxY - DZM_READ_STATUS_BOTTOM_VIEW_HEIGHT, width: readRect.width, height: DZM_READ_STATUS_BOTTOM_VIEW_HEIGHT) 60 | 61 | // 阅读视图 62 | initReadView() 63 | } 64 | 65 | /// 初始化阅读视图 66 | func initReadView() { 67 | 68 | // 是否为书籍首页 69 | if recordModel.pageModel.isHomePage { 70 | 71 | topView.isHidden = true 72 | bottomView.isHidden = true 73 | 74 | homeView = DZMReadHomeView() 75 | homeView.readModel = readModel 76 | view.addSubview(homeView) 77 | homeView.frame = DZM_READ_VIEW_RECT 78 | 79 | }else{ 80 | 81 | readView = DZMReadView() 82 | readView.content = recordModel.contentAttributedString 83 | view.addSubview(readView) 84 | readView.frame = DZM_READ_VIEW_RECT 85 | } 86 | } 87 | 88 | /// 刷新阅读进度显示 89 | private func reloadProgress() { 90 | 91 | if DZMReadConfigure.shared().progressType == .total { // 总进度 92 | 93 | // 当前阅读进度 94 | let progress:Float = DZM_READ_TOTAL_PROGRESS(readModel: readModel, recordModel: recordModel) 95 | 96 | // 显示进度 97 | bottomView.progress.text = DZM_READ_TOTAL_PROGRESS_STRING(progress: progress) 98 | 99 | 100 | }else{ // 分页进度 101 | 102 | // 显示进度 103 | bottomView.progress.text = "\(recordModel.page.intValue + 1)/\(recordModel.chapterModel!.pageCount.intValue)" 104 | } 105 | } 106 | 107 | deinit { 108 | 109 | bottomView?.removeTimer() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/view/DZMReadHomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadHomeView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/5/7. 6 | // Copyright © 2019 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadHomeView: UIView { 12 | 13 | /// 书籍名称 14 | private var name:UILabel! 15 | 16 | /// 当前阅读模型 17 | var readModel:DZMReadModel! { 18 | 19 | didSet{ 20 | 21 | name.text = readModel.bookName 22 | } 23 | } 24 | 25 | override init(frame: CGRect) { 26 | 27 | super.init(frame: frame) 28 | 29 | addSubviews() 30 | } 31 | 32 | private func addSubviews() { 33 | 34 | // 书籍名称 35 | name = UILabel() 36 | name.textAlignment = .center 37 | name.font = DZM_FONT_BOLD_SA(50) 38 | name.textColor = DZMReadConfigure.shared().textColor 39 | addSubview(name) 40 | } 41 | 42 | override func layoutSubviews() { 43 | 44 | super.layoutSubviews() 45 | 46 | name.frame = bounds 47 | } 48 | 49 | required init?(coder aDecoder: NSCoder) { 50 | 51 | fatalError("init(coder:) has not been implemented") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/view/DZMReadHomeViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadHomeViewCell.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/5/7. 6 | // Copyright © 2019 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadHomeViewCell: UITableViewCell { 12 | 13 | /// 书籍首页视图 14 | private(set) var homeView:DZMReadHomeView! 15 | 16 | class func cell(_ tableView:UITableView) ->DZMReadHomeViewCell { 17 | 18 | var cell = tableView.dequeueReusableCell(withIdentifier: "DZMReadHomeViewCell") 19 | 20 | if cell == nil { 21 | 22 | cell = DZMReadHomeViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "DZMReadHomeViewCell") 23 | } 24 | 25 | return cell as! DZMReadHomeViewCell 26 | } 27 | 28 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 29 | 30 | super.init(style: style, reuseIdentifier: reuseIdentifier) 31 | 32 | selectionStyle = .none 33 | 34 | backgroundColor = UIColor.clear 35 | 36 | addSubviews() 37 | } 38 | 39 | private func addSubviews() { 40 | 41 | // 书籍首页 42 | homeView = DZMReadHomeView() 43 | contentView.addSubview(homeView) 44 | } 45 | 46 | override func layoutSubviews() { 47 | 48 | super.layoutSubviews() 49 | 50 | homeView.frame = bounds 51 | } 52 | 53 | required init?(coder aDecoder: NSCoder) { 54 | 55 | fatalError("init(coder:) has not been implemented") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/view/DZMReadLongPressCursorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadLongPressCursorView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/30. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadLongPressCursorView: UIView { 12 | 13 | /// 光标圆圈显示位置: true -> 圆圈在上面 , false -> 圆圈在下面 14 | var isTorB:Bool = true { 15 | 16 | didSet{ setNeedsDisplay() } 17 | } 18 | 19 | /// 光标颜色 20 | var color:UIColor = DZM_READ_COLOR_MAIN { 21 | 22 | didSet{ setNeedsDisplay() } 23 | } 24 | 25 | override init(frame: CGRect) { 26 | 27 | super.init(frame: frame) 28 | 29 | backgroundColor = UIColor.clear 30 | } 31 | 32 | override func draw(_ rect: CGRect) { 33 | 34 | let ctx = UIGraphicsGetCurrentContext() 35 | 36 | color.set() 37 | 38 | let rectW:CGFloat = bounds.width / 2 39 | 40 | ctx?.addRect(CGRect(x: (bounds.width - rectW) / 2, y: (isTorB ? 1 : 0), width: rectW, height: bounds.height - 1)) 41 | 42 | ctx?.fillPath() 43 | 44 | if isTorB { 45 | 46 | ctx?.addEllipse(in: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.width)) 47 | 48 | }else{ 49 | 50 | ctx?.addEllipse(in: CGRect(x: 0, y: bounds.height - bounds.width, width: bounds.width, height: bounds.width)) 51 | } 52 | 53 | color.set() 54 | 55 | 56 | ctx?.fillPath() 57 | } 58 | 59 | required init?(coder aDecoder: NSCoder) { 60 | 61 | fatalError("init(coder:) has not been implemented") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/view/DZMReadView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadView: UIView { 12 | 13 | /// 当前页模型(使用contentSize绘制) 14 | var pageModel:DZMReadPageModel! { 15 | 16 | didSet{ 17 | 18 | frameRef = DZMCoreText.GetFrameRef(attrString: pageModel.showContent, rect: CGRect(origin: CGPoint.zero, size: pageModel.contentSize)) 19 | } 20 | } 21 | 22 | /// 当前页内容(使用固定范围绘制) 23 | var content:NSAttributedString! { 24 | 25 | didSet{ 26 | 27 | frameRef = DZMCoreText.GetFrameRef(attrString: content, rect: CGRect(origin: CGPoint.zero, size: DZM_READ_VIEW_RECT.size)) 28 | } 29 | } 30 | 31 | /// CTFrame 32 | var frameRef:CTFrame? { 33 | 34 | didSet{ 35 | 36 | if frameRef != nil { setNeedsDisplay() } 37 | } 38 | } 39 | 40 | override init(frame: CGRect) { 41 | 42 | super.init(frame: frame) 43 | 44 | // 正常使用 45 | backgroundColor = UIColor.clear 46 | 47 | // 可以修改为随机颜色便于调试范围 48 | // backgroundColor = DZM_COLOR_ARC 49 | } 50 | 51 | /// 绘制 52 | override func draw(_ rect: CGRect) { 53 | 54 | if (frameRef == nil) {return} 55 | 56 | let ctx = UIGraphicsGetCurrentContext() 57 | 58 | ctx?.textMatrix = CGAffineTransform.identity 59 | 60 | ctx?.translateBy(x: 0, y: bounds.size.height); 61 | 62 | ctx?.scaleBy(x: 1.0, y: -1.0); 63 | 64 | CTFrameDraw(frameRef!, ctx!); 65 | } 66 | 67 | required init?(coder aDecoder: NSCoder) { 68 | 69 | fatalError("init(coder:) has not been implemented") 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/view/DZMReadViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadViewCell.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadViewCell: UITableViewCell { 12 | 13 | /// 阅读视图 14 | private var readView:DZMReadView! 15 | 16 | var pageModel:DZMReadPageModel! { 17 | 18 | didSet{ 19 | 20 | readView.pageModel = pageModel 21 | 22 | setNeedsLayout() 23 | } 24 | } 25 | 26 | class func cell(_ tableView:UITableView) ->DZMReadViewCell { 27 | 28 | var cell = tableView.dequeueReusableCell(withIdentifier: "DZMReadViewCell") 29 | 30 | if cell == nil { 31 | 32 | cell = DZMReadViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "DZMReadViewCell") 33 | } 34 | 35 | return cell as! DZMReadViewCell 36 | } 37 | 38 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 39 | 40 | super.init(style: style, reuseIdentifier: reuseIdentifier) 41 | 42 | selectionStyle = .none 43 | 44 | backgroundColor = UIColor.clear 45 | 46 | addSubviews() 47 | } 48 | 49 | private func addSubviews() { 50 | 51 | // 阅读视图 52 | readView = DZMReadView() 53 | contentView.addSubview(readView) 54 | } 55 | 56 | override func layoutSubviews() { 57 | 58 | super.layoutSubviews() 59 | 60 | // 分页顶部高度 61 | let y = pageModel?.headTypeHeight ?? DZM_SPACE_MIN_HEIGHT 62 | 63 | // 内容高度 64 | let h = pageModel?.contentSize.height ?? DZM_SPACE_MIN_HEIGHT 65 | 66 | readView.frame = CGRect(x: 0, y: y, width: DZM_READ_VIEW_RECT.width, height: h) 67 | } 68 | 69 | required init?(coder aDecoder: NSCoder) { 70 | 71 | fatalError("init(coder:) has not been implemented") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/view/DZMReadViewStatusBottomView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadViewStatusBottomView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// bottomView 高度 12 | let DZM_READ_STATUS_BOTTOM_VIEW_HEIGHT:CGFloat = DZM_SPACE_SA_30 13 | 14 | class DZMReadViewStatusBottomView: UIView { 15 | 16 | /// 进度 17 | private(set) var progress:UILabel! 18 | 19 | /// 电池 20 | private var batteryView:DZMBatteryView! 21 | 22 | /// 时间 23 | private var timeLabel:UILabel! 24 | 25 | /// 计时器 26 | private var timer:Timer? 27 | 28 | override init(frame: CGRect) { 29 | 30 | super.init(frame: frame) 31 | 32 | addSubviews() 33 | } 34 | 35 | private func addSubviews() { 36 | 37 | // 电池 38 | batteryView = DZMBatteryView() 39 | batteryView.tintColor = DZMReadConfigure.shared().statusTextColor 40 | addSubview(batteryView) 41 | 42 | // 时间 43 | timeLabel = UILabel() 44 | timeLabel.textAlignment = .center 45 | timeLabel.font = DZM_FONT_SA_10 46 | timeLabel.textColor = DZMReadConfigure.shared().statusTextColor 47 | addSubview(timeLabel) 48 | 49 | // 进度 50 | progress = UILabel() 51 | progress.font = DZM_FONT_SA_10 52 | progress.textColor = DZMReadConfigure.shared().statusTextColor 53 | addSubview(progress) 54 | 55 | // 初始化调用 56 | didChangeTime() 57 | 58 | // 添加定时器 59 | addTimer() 60 | } 61 | 62 | override func layoutSubviews() { 63 | 64 | super.layoutSubviews() 65 | 66 | let w = frame.size.width 67 | let h = frame.size.height 68 | 69 | // 电池 70 | batteryView.frame.origin = CGPoint(x: w - DZMBatterySize.width, y: (h - DZMBatterySize.height) / 2) 71 | 72 | // 时间 73 | timeLabel.frame = CGRect(x: batteryView.frame.minX - DZM_SPACE_SA_50, y: 0, width: DZM_SPACE_SA_50, height: h) 74 | 75 | // 进度 76 | progress.frame = CGRect(x: 0, y: 0, width: DZM_SPACE_SA_50, height: h) 77 | } 78 | 79 | // MARK: -- 时间相关 80 | 81 | /// 添加定时器 82 | func addTimer() { 83 | 84 | if timer == nil { 85 | 86 | timer = Timer.scheduledTimer(timeInterval: 15, target: self, selector: #selector(didChangeTime), userInfo: nil, repeats: true) 87 | 88 | RunLoop.current.add(timer!, forMode: .common) 89 | } 90 | } 91 | 92 | /// 删除定时器 93 | func removeTimer() { 94 | 95 | if timer != nil { 96 | 97 | timer!.invalidate() 98 | 99 | timer = nil 100 | } 101 | } 102 | 103 | /// 时间变化 104 | @objc func didChangeTime() { 105 | 106 | timeLabel.text = TimerString("HH:mm") 107 | 108 | batteryView.batteryLevel = UIDevice.current.batteryLevel 109 | } 110 | 111 | /// 销毁 112 | deinit { 113 | 114 | removeTimer() 115 | } 116 | 117 | required init?(coder aDecoder: NSCoder) { 118 | 119 | fatalError("init(coder:) has not been implemented") 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/ReadView/view/DZMReadViewStatusTopView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadViewStatusTopView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// topView 高度 12 | let DZM_READ_STATUS_TOP_VIEW_HEIGHT:CGFloat = DZM_SPACE_SA_40 13 | 14 | class DZMReadViewStatusTopView: UIView { 15 | 16 | /// 书名 17 | private(set) var bookName:UILabel! 18 | 19 | /// 章节名 20 | private(set) var chapterName:UILabel! 21 | 22 | override init(frame: CGRect) { 23 | 24 | super.init(frame: frame) 25 | 26 | addSubviews() 27 | } 28 | 29 | private func addSubviews() { 30 | 31 | // 书名 32 | bookName = UILabel() 33 | bookName.font = DZM_FONT_SA_10 34 | bookName.textColor = DZMReadConfigure.shared().statusTextColor 35 | bookName.textAlignment = .left 36 | addSubview(bookName) 37 | 38 | // 章节名 39 | chapterName = UILabel() 40 | chapterName.font = DZM_FONT_SA_10 41 | chapterName.textColor = DZMReadConfigure.shared().statusTextColor 42 | chapterName.textAlignment = .right 43 | addSubview(chapterName) 44 | } 45 | 46 | override func layoutSubviews() { 47 | 48 | super.layoutSubviews() 49 | 50 | let w = frame.size.width 51 | let h = frame.size.height 52 | let labelW = (w - DZM_SPACE_SA_15) / 2 53 | 54 | // 书名 55 | bookName.frame = CGRect(x: 0, y: 0, width: labelW, height: h) 56 | 57 | // 章节名 58 | chapterName.frame = CGRect(x: w - labelW, y: 0, width: labelW, height: h) 59 | } 60 | 61 | required init?(coder aDecoder: NSCoder) { 62 | 63 | fatalError("init(coder:) has not been implemented") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/menuUI/DZMRMBaseView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMRMBaseView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/18. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMRMBaseView: UIView { 12 | 13 | /// 菜单对象 14 | weak var readMenu:DZMReadMenu! 15 | 16 | /// 系统初始化 17 | override init(frame: CGRect) { super.init(frame: frame) } 18 | 19 | /// 初始化 20 | convenience init(readMenu:DZMReadMenu!) { 21 | 22 | self.init(frame: CGRect.zero) 23 | 24 | self.readMenu = readMenu 25 | 26 | addSubviews() 27 | } 28 | 29 | func addSubviews() { 30 | 31 | backgroundColor = DZM_READ_COLOR_MENU_BG_COLOR 32 | } 33 | 34 | required init?(coder aDecoder: NSCoder) { 35 | fatalError("init(coder:) has not been implemented") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/menuUI/DZMRMBottomView/DZMRMBottomView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMRMBottomView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/18. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// funcView 高度 12 | let DZM_READ_MENU_FUNC_VIEW_HEIGHT:CGFloat = DZM_SPACE_SA_55 13 | 14 | /// progressView 高度 15 | let DZM_READ_MENU_PROGRESS_VIEW_HEIGHT:CGFloat = DZM_SPACE_SA_55 16 | 17 | /// bottomView 高度 (TabBarHeight就包含了funcView高度, 所以只需要在上面在加progressView高度就好了) 18 | let DZM_READ_MENU_BOTTOM_VIEW_HEIGHT:CGFloat = TabBarHeight + DZM_READ_MENU_PROGRESS_VIEW_HEIGHT 19 | 20 | class DZMRMBottomView: DZMRMBaseView { 21 | 22 | /// 进度 23 | private(set) var progressView:DZMRMProgressView! 24 | 25 | /// 功能 26 | private var funcView:DZMRMFuncView! 27 | 28 | override init(frame: CGRect) { super.init(frame: frame) } 29 | 30 | override func addSubviews() { 31 | 32 | super.addSubviews() 33 | 34 | progressView = DZMRMProgressView(readMenu: readMenu) 35 | addSubview(progressView) 36 | 37 | funcView = DZMRMFuncView(readMenu: readMenu) 38 | addSubview(funcView) 39 | } 40 | 41 | override func layoutSubviews() { 42 | 43 | super.layoutSubviews() 44 | 45 | progressView.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: DZM_READ_MENU_PROGRESS_VIEW_HEIGHT) 46 | 47 | funcView.frame = CGRect(x: 0, y: progressView.frame.maxY, width: frame.size.width, height: DZM_READ_MENU_FUNC_VIEW_HEIGHT) 48 | } 49 | 50 | required init?(coder aDecoder: NSCoder) { 51 | 52 | fatalError("init(coder:) has not been implemented") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/menuUI/DZMRMBottomView/DZMRMFuncView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMRMFuncView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/19. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMRMFuncView: DZMRMBaseView { 12 | 13 | /// 目录 14 | private var catalogue:UIButton! 15 | 16 | /// 设置 17 | private var setting:UIButton! 18 | 19 | /// 日夜间切换 (Day and Night) 20 | private var dn:UIButton! 21 | 22 | override init(frame: CGRect) { super.init(frame: frame) } 23 | 24 | override func addSubviews() { 25 | 26 | super.addSubviews() 27 | 28 | backgroundColor = UIColor.clear 29 | 30 | // 目录 31 | catalogue = UIButton(type:.custom) 32 | catalogue.setImage(UIImage(named:"bar_0")?.withRenderingMode(.alwaysTemplate), for: .normal) 33 | catalogue.addTarget(self, action: #selector(clickCatalogue), for: .touchUpInside) 34 | catalogue.tintColor = DZM_READ_COLOR_MENU_COLOR 35 | addSubview(catalogue) 36 | 37 | // 日夜间 38 | dn = UIButton(type:.custom) 39 | dn.setImage(UIImage(named:"bar_2")!.withRenderingMode(.alwaysTemplate), for: .normal) 40 | dn.addTarget(self, action: #selector(clickDN(_:)), for: .touchUpInside) 41 | dn.tintColor = DZM_READ_COLOR_MENU_COLOR 42 | dn.isSelected = DZMUserDefaults.bool(DZM_READ_KEY_MODE_DAY_NIGHT) 43 | addSubview(dn) 44 | updateDNButton() 45 | 46 | // 设置 47 | setting = UIButton(type: .custom) 48 | setting.setImage(UIImage(named:"bar_1")!.withRenderingMode(.alwaysTemplate), for: .normal) 49 | setting.addTarget(self, action: #selector(clickSetting), for: .touchUpInside) 50 | setting.tintColor = DZM_READ_COLOR_MENU_COLOR 51 | addSubview(setting) 52 | } 53 | 54 | /// 点击目录 55 | @objc private func clickCatalogue() { 56 | 57 | readMenu?.delegate?.readMenuClickCatalogue?(readMenu: readMenu) 58 | } 59 | 60 | /// 点击日夜间 61 | @objc private func clickDN(_ button:UIButton) { 62 | 63 | button.isSelected = !button.isSelected 64 | 65 | // 切换日夜间 66 | readMenu.cover.alpha = CGFloat(NSNumber(value: button.isSelected).floatValue) 67 | 68 | // 刷新显示 69 | updateDNButton() 70 | 71 | // 记录日夜间状态 72 | DZMUserDefaults.setBool(button.isSelected, DZM_READ_KEY_MODE_DAY_NIGHT) 73 | 74 | readMenu?.delegate?.readMenuClickDayAndNight?(readMenu: readMenu) 75 | } 76 | 77 | /// 点击设置 78 | @objc private func clickSetting() { 79 | 80 | readMenu.showTopView(isShow: false) 81 | 82 | readMenu.showBottomView(isShow: false) 83 | 84 | readMenu.showSettingView(isShow: true) 85 | } 86 | 87 | /// 刷新日夜间按钮显示状态 88 | func updateDNButton() { 89 | 90 | if dn.isSelected { dn.tintColor = DZM_READ_COLOR_MAIN 91 | 92 | }else{ dn.tintColor = DZM_READ_COLOR_MENU_COLOR } 93 | } 94 | 95 | override func layoutSubviews() { 96 | 97 | super.layoutSubviews() 98 | 99 | let wh = frame.size.height 100 | 101 | catalogue.frame = CGRect(x: 0, y: DZM_SPACE_SA_3, width: wh, height: wh) 102 | 103 | dn.frame = CGRect(x: (frame.size.width - wh)/2, y: DZM_SPACE_SA_3, width: wh, height: wh) 104 | 105 | setting.frame = CGRect(x: frame.size.width - wh, y: DZM_SPACE_SA_3, width: wh, height: wh) 106 | } 107 | 108 | required init?(coder aDecoder: NSCoder) { 109 | 110 | fatalError("init(coder:) has not been implemented") 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/menuUI/DZMRMBottomView/DZMRMProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMRMProgressView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/19. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMRMProgressView: DZMRMBaseView,ASValueTrackingSliderDelegate,ASValueTrackingSliderDataSource { 12 | 13 | /// 上一章 14 | private var previousChapter:UIButton! 15 | 16 | /// 进度 17 | private var slider:ASValueTrackingSlider! 18 | 19 | /// 下一章 20 | private var nextChapter:UIButton! 21 | 22 | override init(frame: CGRect) { super.init(frame: frame) } 23 | 24 | override func addSubviews() { 25 | 26 | super.addSubviews() 27 | 28 | backgroundColor = UIColor.clear 29 | 30 | // 上一章 31 | previousChapter = UIButton(type:.custom) 32 | previousChapter.titleLabel?.font = DZM_FONT_SA_14 33 | previousChapter.setTitle("上一章", for: .normal) 34 | previousChapter.setTitleColor(DZM_READ_COLOR_MENU_COLOR, for: .normal) 35 | previousChapter.addTarget(self, action: #selector(clickPreviousChapter), for: .touchUpInside) 36 | addSubview(previousChapter) 37 | 38 | // 下一章 39 | nextChapter = UIButton(type:.custom) 40 | nextChapter.titleLabel?.font = DZM_FONT_SA_14 41 | nextChapter.setTitle("下一章", for: .normal) 42 | nextChapter.setTitleColor(DZM_READ_COLOR_MENU_COLOR, for: .normal) 43 | nextChapter.addTarget(self, action: #selector(clickNextChapter), for: .touchUpInside) 44 | addSubview(nextChapter) 45 | 46 | // 进度条 47 | slider = ASValueTrackingSlider() 48 | slider.delegate = self 49 | slider.dataSource = self 50 | slider.setThumbImage(UIImage(named:"slider")!.withRenderingMode(.alwaysTemplate), for: .normal) 51 | // 设置显示进度保留几位小数 (由于重写了 dataSource 则不用不到该属性了) 52 | // slider.setMaxFractionDigitsDisplayed(0) 53 | // 设置气泡背景颜色 54 | slider.popUpViewColor = DZM_READ_COLOR_MAIN 55 | // 设置气泡字体颜色 56 | slider.textColor = DZM_READ_COLOR_MENU_COLOR 57 | // 设置气泡字体以及字体大小 58 | slider.font = UIFont(name: "Futura-CondensedExtraBold", size: 22) 59 | // 设置气泡箭头高度 60 | slider.popUpViewArrowLength = DZM_SPACE_SA_5 61 | // 设置当前进度颜色 62 | slider.minimumTrackTintColor = DZM_READ_COLOR_MAIN 63 | // 设置总进度颜色 64 | slider.maximumTrackTintColor = DZM_READ_COLOR_MENU_COLOR 65 | // 设置当前拖拽圆圈颜色 66 | slider.tintColor = DZM_READ_COLOR_MENU_COLOR 67 | addSubview(slider) 68 | reloadProgress() 69 | } 70 | 71 | /// 刷新阅读进度显示 72 | func reloadProgress() { 73 | 74 | // 有阅读数据 75 | let readModel = readMenu.vc.readModel 76 | 77 | // 有阅读记录以及章节数据 78 | if readModel != nil && (readModel?.recordModel?.chapterModel != nil) { 79 | 80 | if DZMReadConfigure.shared().progressType == .total { // 总进度 81 | 82 | slider.minimumValue = 0 83 | slider.maximumValue = 1 84 | slider.value = DZM_READ_TOTAL_PROGRESS(readModel: readModel, recordModel: readModel?.recordModel) 85 | 86 | }else{ // 分页进度 87 | 88 | slider.minimumValue = 1 89 | slider.maximumValue = readModel!.recordModel.chapterModel.pageCount.floatValue 90 | slider.value = readModel!.recordModel.page.floatValue + 1 91 | } 92 | 93 | }else{ // 没有则清空 94 | 95 | slider.minimumValue = 0 96 | slider.maximumValue = 0 97 | slider.value = 0 98 | } 99 | } 100 | 101 | /// 上一章 102 | @objc func clickPreviousChapter() { 103 | 104 | readMenu?.delegate?.readMenuClickPreviousChapter?(readMenu: readMenu) 105 | } 106 | 107 | /// 下一章 108 | @objc func clickNextChapter() { 109 | 110 | readMenu?.delegate?.readMenuClickNextChapter?(readMenu: readMenu) 111 | } 112 | 113 | // MARK: ASValueTrackingSliderDataSource 114 | 115 | func slider(_ slider: ASValueTrackingSlider!, stringForValue value: Float) -> String! { 116 | 117 | if DZMReadConfigure.shared().progressType == .total { // 总进度 118 | 119 | // 如果有需求可显示章节名 120 | return DZM_READ_TOTAL_PROGRESS_STRING(progress: value) 121 | 122 | }else{ // 分页进度 123 | 124 | return "\(NSInteger(value))" 125 | } 126 | } 127 | 128 | // MARK: -- ASValueTrackingSliderDelegate 129 | 130 | /// 进度显示将要显示 131 | func sliderWillDisplayPopUpView(_ slider: ASValueTrackingSlider!) { } 132 | 133 | /// 进度显示将要隐藏 134 | func sliderWillHidePopUpView(_ slider: ASValueTrackingSlider!) { 135 | 136 | if DZMReadConfigure.shared().progressType == .total { // 总进度 137 | 138 | // 有阅读数据 139 | let readModel = readMenu.vc.readModel 140 | 141 | // 有阅读记录以及章节数据 142 | if readModel != nil && (readModel?.recordModel?.chapterModel != nil) { 143 | 144 | // 总章节个数 145 | let count = (readModel!.chapterListModels.count - 1) 146 | 147 | // 获得当前进度的章节索引 148 | let index = NSInteger(Float(count) * slider.value) 149 | 150 | // 获得章节列表模型 151 | let chapterListModel = readModel!.chapterListModels[index] 152 | 153 | // 页码 154 | let toPage = (index == count) ? DZM_READ_LAST_PAGE : 0 155 | 156 | // 传递 157 | readMenu?.delegate?.readMenuDraggingProgress?(readMenu: readMenu, toChapterID: chapterListModel.id, toPage: toPage) 158 | } 159 | 160 | }else{ // 分页进度 161 | 162 | readMenu?.delegate?.readMenuDraggingProgress?(readMenu: readMenu, toPage: NSInteger(slider.value - 1)) 163 | } 164 | } 165 | 166 | override func layoutSubviews() { 167 | 168 | super.layoutSubviews() 169 | 170 | let w = frame.size.width 171 | let h = frame.size.height 172 | let buttonW = DZM_SPACE_SA_55 173 | 174 | // 上一章 175 | previousChapter.frame = CGRect(x: DZM_SPACE_SA_5, y: 0, width: buttonW, height: h) 176 | 177 | // 下一章 178 | nextChapter.frame = CGRect(x: w - buttonW - DZM_SPACE_SA_5, y: 0, width: buttonW, height: h) 179 | 180 | // 进度条 181 | let sliderX = previousChapter.frame.maxX + DZM_SPACE_SA_10 182 | let sliderW = w - 2 * sliderX 183 | slider.frame = CGRect(x: sliderX, y: 0, width: sliderW, height: h) 184 | } 185 | 186 | required init?(coder aDecoder: NSCoder) { 187 | 188 | fatalError("init(coder:) has not been implemented") 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/menuUI/DZMRMSettingView/DZMRMBGColorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMRMBGColorView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/18. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMRMBGColorView: DZMRMBaseView { 12 | 13 | private var items:[UIButton] = [] 14 | 15 | private var selectItem:UIButton! 16 | 17 | override init(frame: CGRect) { super.init(frame: frame) } 18 | 19 | override func addSubviews() { 20 | 21 | super.addSubviews() 22 | 23 | backgroundColor = UIColor.clear 24 | 25 | let count = DZM_READ_BG_COLORS.count 26 | 27 | for i in 0.. DZM_READ_FONT_SIZE_MAX) { 108 | 109 | fontSize.text = "\(size.stringValue)" 110 | 111 | DZMReadConfigure.shared().fontSize = size 112 | 113 | DZMReadConfigure.shared().save() 114 | 115 | readMenu?.delegate?.readMenuClickFontSize?(readMenu: readMenu) 116 | } 117 | } 118 | 119 | override func layoutSubviews() { 120 | 121 | super.layoutSubviews() 122 | 123 | let w = frame.size.width 124 | let h = frame.size.height 125 | 126 | let itemW = DZM_SPACE_SA_100 127 | let itemH = DZM_SPACE_SA_30 128 | let itemY = (h - itemH) / 2 129 | let displayProgressWH = DZM_SPACE_SA_45 130 | 131 | leftButton.frame = CGRect(x: 0, y: itemY, width: itemW, height: itemH) 132 | 133 | rightButton.frame = CGRect(x: w - itemW - displayProgressWH - DZM_SPACE_SA_25, y: itemY, width: itemW, height: itemH) 134 | 135 | fontSize.frame = CGRect(x: leftButton.frame.maxX, y: itemY, width: rightButton.frame.minX - leftButton.frame.maxX, height: itemH) 136 | 137 | displayProgress.frame = CGRect(x: w - displayProgressWH - DZM_SPACE_SA_2, y: (h - displayProgressWH) / 2, width: displayProgressWH, height: displayProgressWH) 138 | } 139 | 140 | required init?(coder aDecoder: NSCoder) { 141 | 142 | fatalError("init(coder:) has not been implemented") 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/menuUI/DZMRMSettingView/DZMRMFontTypeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMRMFontTypeView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/18. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMRMFontTypeView: DZMRMBaseView { 12 | 13 | private var fontNames:[String] = ["系统","黑体","楷体","宋体"] 14 | 15 | private var items:[UIButton] = [] 16 | 17 | private var selectItem:UIButton! 18 | 19 | override init(frame: CGRect) { super.init(frame: frame) } 20 | 21 | override func addSubviews() { 22 | 23 | super.addSubviews() 24 | 25 | backgroundColor = UIColor.clear 26 | 27 | let count = fontNames.count 28 | 29 | for i in 0.. CGRect { 104 | 105 | return CGRect(x: (contentRect.width - DZM_SPACE_SA_25) / 2, y: (contentRect.height - DZM_SPACE_SA_20) / 2, width: DZM_SPACE_SA_25, height: DZM_SPACE_SA_20) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/menuUI/DZMRMTopView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMRMTopView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/18. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// topView 高度 12 | let DZM_READ_MENU_TOP_VIEW_HEIGHT:CGFloat = NavgationBarHeight 13 | 14 | class DZMRMTopView: DZMRMBaseView { 15 | 16 | /// 返回 17 | private var back:UIButton! 18 | 19 | /// 书签 20 | private var mark:UIButton! 21 | 22 | override init(frame: CGRect) { super.init(frame: frame) } 23 | 24 | override func addSubviews() { 25 | 26 | super.addSubviews() 27 | 28 | // 返回 29 | back = UIButton(type:.custom) 30 | back.setImage(UIImage(named:"back")!.withRenderingMode(.alwaysTemplate), for: .normal) 31 | back.addTarget(self, action: #selector(clickBack), for: .touchUpInside) 32 | back.tintColor = DZM_READ_COLOR_MENU_COLOR 33 | addSubview(back) 34 | 35 | // 书签 36 | mark = UIButton(type:.custom) 37 | mark.contentMode = .center 38 | mark.setImage(UIImage(named:"mark")!.withRenderingMode(.alwaysTemplate), for: .normal) 39 | mark.addTarget(self, action: #selector(clickMark(_:)), for: .touchUpInside) 40 | mark.tintColor = DZM_READ_COLOR_MENU_COLOR 41 | addSubview(mark) 42 | updateMarkButton() 43 | } 44 | 45 | /// 点击返回 46 | @objc private func clickBack() { 47 | 48 | readMenu?.delegate?.readMenuClickBack?(readMenu: readMenu) 49 | } 50 | 51 | /// 点击书签 52 | @objc private func clickMark(_ button:UIButton) { 53 | 54 | readMenu?.delegate?.readMenuClickMark?(readMenu: readMenu, topView: self, markButton: button) 55 | } 56 | 57 | /// 检查是否存在书签 58 | func checkForMark() { 59 | 60 | mark.isSelected = (readMenu.vc.readModel.isExistMark() != nil) 61 | 62 | updateMarkButton() 63 | } 64 | 65 | /// 刷新书签按钮显示状态 66 | func updateMarkButton() { 67 | 68 | if mark.isSelected { mark.tintColor = DZM_READ_COLOR_MAIN 69 | 70 | }else{ mark.tintColor = DZM_READ_COLOR_MENU_COLOR } 71 | } 72 | 73 | override func layoutSubviews() { 74 | 75 | super.layoutSubviews() 76 | 77 | let y = StatusBarHeight 78 | 79 | let wh = NavgationBarHeight - y 80 | 81 | back.frame = CGRect(x: 0, y: y, width: wh, height: wh) 82 | 83 | mark.frame = CGRect(x: frame.size.width - wh, y: y, width: wh, height: wh) 84 | } 85 | 86 | required init?(coder aDecoder: NSCoder) { 87 | 88 | fatalError("init(coder:) has not been implemented") 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/model/DZMReadChapterListModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadChapterListModel.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadChapterListModel: NSObject,NSCoding { 12 | 13 | /// 章节ID 14 | @objc var id:NSNumber! 15 | 16 | /// 小说ID 17 | var bookID:String! 18 | 19 | /// 章节名称 20 | var name:String! 21 | 22 | // MARK: -- NSCoding 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | 26 | super.init() 27 | 28 | bookID = aDecoder.decodeObject(forKey: "bookID") as? String 29 | 30 | id = aDecoder.decodeObject(forKey: "id") as? NSNumber 31 | 32 | name = aDecoder.decodeObject(forKey: "name") as? String 33 | } 34 | 35 | func encode(with aCoder: NSCoder) { 36 | 37 | aCoder.encode(bookID, forKey: "bookID") 38 | 39 | aCoder.encode(id, forKey: "id") 40 | 41 | aCoder.encode(name, forKey: "name") 42 | } 43 | 44 | init(_ dict:Any? = nil) { 45 | 46 | super.init() 47 | 48 | if dict != nil { setValuesForKeys(dict as! [String : Any]) } 49 | } 50 | 51 | override func setValue(_ value: Any?, forUndefinedKey key: String) { } 52 | } 53 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/model/DZMReadMarkModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadMarkModel.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadMarkModel: NSObject,NSCoding { 12 | 13 | /// 小说ID 14 | var bookID:String! 15 | 16 | /// 章节ID 17 | var chapterID:NSNumber! 18 | 19 | /// 章节名称 20 | var name:String! 21 | 22 | /// 内容 23 | var content:String! 24 | 25 | /// 时间戳 26 | var time:NSNumber! = NSNumber(value: 0) 27 | 28 | /// 位置 29 | var location:NSNumber! = NSNumber(value: 0) 30 | 31 | // MARK: -- 构造 32 | 33 | override init() { 34 | 35 | super.init() 36 | } 37 | 38 | required init?(coder aDecoder: NSCoder) { 39 | 40 | super.init() 41 | 42 | bookID = aDecoder.decodeObject(forKey: "bookID") as? String 43 | 44 | chapterID = aDecoder.decodeObject(forKey: "chapterID") as? NSNumber 45 | 46 | name = aDecoder.decodeObject(forKey: "name") as? String 47 | 48 | content = aDecoder.decodeObject(forKey: "content") as? String 49 | 50 | time = aDecoder.decodeObject(forKey: "time") as? NSNumber 51 | 52 | location = aDecoder.decodeObject(forKey: "location") as? NSNumber 53 | } 54 | 55 | func encode(with aCoder: NSCoder) { 56 | 57 | aCoder.encode(bookID, forKey: "bookID") 58 | 59 | aCoder.encode(chapterID, forKey: "chapterID") 60 | 61 | aCoder.encode(name, forKey: "name") 62 | 63 | aCoder.encode(content, forKey: "content") 64 | 65 | aCoder.encode(time, forKey: "time") 66 | 67 | aCoder.encode(location, forKey: "location") 68 | } 69 | 70 | init(_ dict:Any? = nil) { 71 | 72 | super.init() 73 | 74 | if dict != nil { setValuesForKeys(dict as! [String : Any]) } 75 | } 76 | 77 | override func setValue(_ value: Any?, forUndefinedKey key: String) { } 78 | } 79 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/model/DZMReadModel+Mark.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadModel+Mark.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/19. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension DZMReadModel { 12 | 13 | /// 添加书签,默认使用当前阅读记录! 14 | func insetMark(recordModel:DZMReadRecordModel? = nil) { 15 | 16 | let recordModel = (recordModel ?? self.recordModel)! 17 | 18 | let markModel = DZMReadMarkModel() 19 | 20 | markModel.bookID = recordModel.bookID 21 | 22 | markModel.chapterID = recordModel.chapterModel.id 23 | 24 | if recordModel.pageModel.isHomePage { 25 | 26 | markModel.name = "(无章节名)" 27 | 28 | markModel.content = bookName 29 | 30 | }else{ 31 | 32 | markModel.name = recordModel.chapterModel.name 33 | 34 | markModel.content = recordModel.contentString.removeSEHeadAndTail.removeEnterAll 35 | } 36 | 37 | markModel.time = NSNumber(value: Timer1970()) 38 | 39 | markModel.location = recordModel.locationFirst 40 | 41 | if markModels.isEmpty { 42 | 43 | markModels.append(markModel) 44 | 45 | }else{ 46 | 47 | markModels.insert(markModel, at: 0) 48 | } 49 | 50 | save() 51 | } 52 | 53 | /// 移除当前书签 54 | func removeMark(index:NSInteger) ->Bool { 55 | 56 | markModels.remove(at: index) 57 | 58 | save() 59 | 60 | return true 61 | } 62 | 63 | /// 移除当前书签 64 | func removeMark(recordModel:DZMReadRecordModel? = nil) ->Bool { 65 | 66 | let recordModel = (recordModel ?? self.recordModel)! 67 | 68 | let markModel = isExistMark(recordModel: recordModel) 69 | 70 | if markModel != nil { 71 | 72 | let index = markModels.index(of: markModel!)! 73 | 74 | return removeMark(index: index) 75 | } 76 | 77 | return false 78 | } 79 | 80 | /// 是否存在书签 81 | func isExistMark(recordModel:DZMReadRecordModel? = nil) ->DZMReadMarkModel? { 82 | 83 | if markModels.isEmpty { return nil } 84 | 85 | let recordModel = (recordModel ?? self.recordModel)! 86 | 87 | let locationFirst = recordModel.locationFirst! 88 | 89 | let locationLast = recordModel.locationLast! 90 | 91 | for markModel in markModels { 92 | 93 | if markModel.chapterID == recordModel.chapterModel.id { 94 | 95 | if (markModel.location.intValue >= locationFirst.intValue) && (markModel.location.intValue < locationLast.intValue) { 96 | 97 | return markModel 98 | } 99 | } 100 | } 101 | 102 | return nil 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/model/DZMReadModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadModel.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadModel: NSObject,NSCoding { 12 | 13 | /// 小说ID 14 | var bookID:String! 15 | 16 | /// 小说名称 17 | var bookName:String! 18 | 19 | /// 小说来源类型 20 | var bookSourceType:DZMBookSourceType! = .network 21 | 22 | /// 当前阅读记录 23 | var recordModel:DZMReadRecordModel! 24 | 25 | /// 书签列表 26 | var markModels:[DZMReadMarkModel]! = [] 27 | 28 | /// 章节列表(如果是网络小说可以不需要放在这里记录,直接在目录视图里面加载接口或者读取本地数据库就好了。) 29 | var chapterListModels:[DZMReadChapterListModel]! = [] 30 | 31 | 32 | // MARK: 快速进入 33 | 34 | /// 本地小说全文 35 | var fullText:String! 36 | 37 | /// 章节内容范围数组 [章节ID:[章节优先级:章节内容Range]] 38 | var ranges:[String:[String:NSRange]]! 39 | 40 | 41 | // MARK: 辅助 42 | 43 | /// 保存 44 | func save() { 45 | 46 | recordModel.save() 47 | 48 | DZMKeyedArchiver.archiver(folderName: bookID, fileName: DZM_READ_KEY_OBJECT, object: self) 49 | } 50 | 51 | /// 是否存在阅读对象 52 | class func isExist(bookID:String!) ->Bool { 53 | 54 | return DZMKeyedArchiver.isExist(folderName: bookID, fileName: DZM_READ_KEY_OBJECT) 55 | } 56 | 57 | 58 | // MARK: 构造 59 | 60 | /// 获取阅读对象,如果则创建对象返回 61 | @objc class func model(bookID:String!) ->DZMReadModel { 62 | 63 | var readModel:DZMReadModel! 64 | 65 | if DZMReadModel.isExist(bookID: bookID) { 66 | 67 | readModel = DZMKeyedArchiver.unarchiver(folderName: bookID, fileName: DZM_READ_KEY_OBJECT) as? DZMReadModel 68 | 69 | }else{ 70 | 71 | readModel = DZMReadModel() 72 | 73 | readModel.bookID = bookID 74 | } 75 | 76 | // 获取阅读记录 77 | readModel.recordModel = DZMReadRecordModel.model(bookID: bookID) 78 | 79 | return readModel 80 | } 81 | 82 | required init?(coder aDecoder: NSCoder) { 83 | 84 | super.init() 85 | 86 | bookID = aDecoder.decodeObject(forKey: "bookID") as? String 87 | 88 | bookName = aDecoder.decodeObject(forKey: "bookName") as? String 89 | 90 | bookSourceType = DZMBookSourceType(rawValue: (aDecoder.decodeObject(forKey: "bookSourceType") as! NSNumber).intValue) 91 | 92 | chapterListModels = aDecoder.decodeObject(forKey: "chapterListModels") as? [DZMReadChapterListModel] 93 | 94 | markModels = aDecoder.decodeObject(forKey: "markModels") as? [DZMReadMarkModel] 95 | 96 | fullText = aDecoder.decodeObject(forKey: "fullText") as? String 97 | 98 | ranges = aDecoder.decodeObject(forKey: "ranges") as? [String:[String:NSRange]] 99 | } 100 | 101 | func encode(with aCoder: NSCoder) { 102 | 103 | aCoder.encode(bookID, forKey: "bookID") 104 | 105 | aCoder.encode(bookName, forKey: "bookName") 106 | 107 | aCoder.encode(NSNumber(value: bookSourceType.rawValue), forKey: "bookSourceType") 108 | 109 | aCoder.encode(chapterListModels, forKey: "chapterListModels") 110 | 111 | aCoder.encode(markModels, forKey: "markModels") 112 | 113 | aCoder.encode(fullText, forKey: "fullText") 114 | 115 | aCoder.encode(ranges, forKey: "ranges") 116 | } 117 | 118 | init(_ dict:Any? = nil) { 119 | 120 | super.init() 121 | 122 | if dict != nil { setValuesForKeys(dict as! [String : Any]) } 123 | } 124 | 125 | override func setValue(_ value: Any?, forUndefinedKey key: String) { } 126 | } 127 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/model/DZMReadPageModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadPageModel.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadPageModel: NSObject,NSCoding { 12 | 13 | // MARK: 常用属性 14 | 15 | /// 当前页内容 16 | var content:NSAttributedString! 17 | 18 | /// 当前页范围 19 | var range:NSRange! 20 | 21 | /// 当前页序号 22 | var page:NSNumber! 23 | 24 | 25 | // MARK: 滚动模式使用 26 | 27 | /// 根据开头类型返回开头高度 (目前主要是滚动模式使用) 28 | var headTypeHeight:CGFloat! = 0 29 | 30 | /// 当前内容Size (目前主要是(滚动模式 || 长按模式)使用) 31 | var contentSize:CGSize! = CGSize.zero 32 | 33 | /// 当前内容头部类型 (目前主要是滚动模式使用) 34 | var headTypeIndex:NSNumber! 35 | 36 | /// 当前内容头部类型 (目前主要是滚动模式使用) 37 | var headType:DZMPageHeadType! { 38 | 39 | set{ headTypeIndex = NSNumber(value: newValue.rawValue) } 40 | 41 | get{ return DZMPageHeadType(rawValue: headTypeIndex.intValue) } 42 | } 43 | 44 | /// 当前内容总高(cell 高度) 45 | var cellHeight:CGFloat! { 46 | 47 | // 内容高度 + 头部高度 48 | return contentSize.height + headTypeHeight 49 | } 50 | 51 | 52 | // MARK: 快捷获取 53 | 54 | /// 书籍首页 55 | var isHomePage:Bool { return range.location == DZM_READ_BOOK_HOME_PAGE } 56 | 57 | /// 获取显示内容(考虑可能会变换字体颜色的情况) 58 | var showContent:NSAttributedString! { 59 | 60 | let textColor = DZMReadConfigure.shared().textColor! 61 | let tempShowContent = NSMutableAttributedString(attributedString: content) 62 | tempShowContent.addAttributes([.foregroundColor : textColor], range: NSMakeRange(0, content.length)) 63 | return tempShowContent 64 | } 65 | 66 | // MARK: -- NSCoding 67 | 68 | required init?(coder aDecoder: NSCoder) { 69 | 70 | super.init() 71 | 72 | content = aDecoder.decodeObject(forKey: "content") as? NSAttributedString 73 | 74 | range = aDecoder.decodeObject(forKey: "range") as? NSRange 75 | 76 | page = aDecoder.decodeObject(forKey: "page") as? NSNumber 77 | 78 | headTypeHeight = aDecoder.decodeObject(forKey: "headTypeHeight") as? CGFloat 79 | 80 | contentSize = aDecoder.decodeObject(forKey: "contentSize") as? CGSize 81 | 82 | headTypeIndex = aDecoder.decodeObject(forKey: "headTypeIndex") as? NSNumber 83 | } 84 | 85 | func encode(with aCoder: NSCoder) { 86 | 87 | aCoder.encode(content, forKey: "content") 88 | 89 | aCoder.encode(range, forKey: "range") 90 | 91 | aCoder.encode(page, forKey: "page") 92 | 93 | aCoder.encode(headTypeHeight, forKey: "headTypeHeight") 94 | 95 | aCoder.encode(contentSize, forKey: "contentSize") 96 | 97 | aCoder.encode(headTypeIndex, forKey: "headTypeIndex") 98 | } 99 | 100 | init(_ dict:Any? = nil) { 101 | 102 | super.init() 103 | 104 | if dict != nil { setValuesForKeys(dict as! [String : Any]) } 105 | } 106 | 107 | override func setValue(_ value: Any?, forUndefinedKey key: String) { } 108 | } 109 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/model/DZMReadRecordModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadRecordModel.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// 记录当前章节阅读到的坐标 12 | var DZM_READ_RECORD_CURRENT_CHAPTER_LOCATION:NSNumber! 13 | 14 | class DZMReadRecordModel: NSObject,NSCoding { 15 | 16 | /// 小说ID 17 | var bookID:String! 18 | 19 | /// 当前记录的阅读章节 20 | var chapterModel:DZMReadChapterModel! 21 | 22 | /// 阅读到的页码(上传阅读记录到服务器时传当前页面的 location 上去,从服务器拿回来 location 在转成页码。精准回到上次阅读位置) 23 | var page:NSNumber! = NSNumber(value: 0) 24 | 25 | 26 | // MARK: 快捷获取 27 | 28 | /// 当前记录分页模型 29 | var pageModel:DZMReadPageModel! { return chapterModel.pageModels[page.intValue] } 30 | 31 | /// 当前记录起始坐标 32 | var locationFirst:NSNumber! { return chapterModel.locationFirst(page: page.intValue) } 33 | 34 | /// 当前记录末尾坐标 35 | var locationLast:NSNumber! { return chapterModel.locationLast(page: page.intValue) } 36 | 37 | /// 当前记录是否为第一个章节 38 | var isFirstChapter:Bool! { return chapterModel.isFirstChapter } 39 | 40 | /// 当前记录是否为最后一个章节 41 | var isLastChapter:Bool! { return chapterModel.isLastChapter } 42 | 43 | /// 当前记录是否为第一页 44 | var isFirstPage:Bool! { return (page.intValue == 0) } 45 | 46 | /// 当前记录是否为最后一页 47 | var isLastPage:Bool! { return (page.intValue == (chapterModel.pageCount.intValue - 1)) } 48 | 49 | /// 当前记录页码字符串 50 | var contentString:String! { return chapterModel.contentString(page: page.intValue) } 51 | 52 | /// 当前记录页码富文本 53 | var contentAttributedString:NSAttributedString! { return chapterModel.contentAttributedString(page: page.intValue) } 54 | 55 | /// 当前记录切到上一页 56 | func previousPage() { page = NSNumber(value: max(page.intValue - 1, 0)) } 57 | 58 | /// 当前记录切到下一页 59 | func nextPage() { page = NSNumber(value: min(page.intValue + 1, chapterModel.pageCount.intValue - 1)) } 60 | 61 | /// 当前记录切到第一页 62 | func firstPage() { page = NSNumber(value: 0) } 63 | 64 | /// 当前记录切到最后一页 65 | func lastPage() { page = NSNumber(value: chapterModel.pageCount.intValue - 1) } 66 | 67 | 68 | // MARK: 辅助 69 | 70 | /// 修改阅读记录为指定章节位置 71 | func modify(chapterModel:DZMReadChapterModel!, page:NSInteger, isSave:Bool = true) { 72 | 73 | self.chapterModel = chapterModel 74 | 75 | self.page = NSNumber(value: page) 76 | 77 | if isSave { save() } 78 | } 79 | 80 | /// 修改阅读记录为指定章节位置 81 | func modify(chapterID:NSNumber!, location:NSInteger, isSave:Bool = true) { 82 | 83 | if DZMReadChapterModel.isExist(bookID: bookID, chapterID: chapterID) { 84 | 85 | chapterModel = DZMReadChapterModel.model(bookID: bookID, chapterID: chapterID) 86 | 87 | page = chapterModel.page(location: location) 88 | 89 | if isSave { save() } 90 | } 91 | } 92 | 93 | /// 修改阅读记录为指定章节页码 (toPage == DZM_READ_LAST_PAGE 为当前章节最后一页) 94 | func modify(chapterID:NSNumber!, toPage:NSInteger, isSave:Bool = true) { 95 | 96 | if DZMReadChapterModel.isExist(bookID: bookID, chapterID: chapterID) { 97 | 98 | chapterModel = DZMReadChapterModel.model(bookID: bookID, chapterID: chapterID) 99 | 100 | if (toPage == DZM_READ_LAST_PAGE) { lastPage() 101 | 102 | }else{ page = NSNumber(value: toPage) } 103 | 104 | if isSave { save() } 105 | } 106 | } 107 | 108 | /// 更新字体 109 | func updateFont(isSave:Bool = true) { 110 | 111 | if chapterModel != nil { 112 | 113 | chapterModel.updateFont() 114 | 115 | page = chapterModel.page(location: DZM_READ_RECORD_CURRENT_CHAPTER_LOCATION.intValue) 116 | 117 | if isSave { save() } 118 | } 119 | } 120 | 121 | /// 拷贝阅读记录 122 | func copyModel() ->DZMReadRecordModel { 123 | 124 | let recordModel = DZMReadRecordModel() 125 | 126 | recordModel.bookID = bookID 127 | 128 | recordModel.chapterModel = chapterModel 129 | 130 | recordModel.page = page 131 | 132 | return recordModel 133 | } 134 | 135 | /// 保存记录 136 | func save() { 137 | 138 | DZMKeyedArchiver.archiver(folderName: bookID, fileName: DZM_READ_KEY_RECORD, object: self) 139 | } 140 | 141 | /// 是否存在阅读记录 142 | class func isExist(_ bookID:String!) ->Bool { 143 | 144 | return DZMKeyedArchiver.isExist(folderName: bookID, fileName: DZM_READ_KEY_RECORD) 145 | } 146 | 147 | 148 | // MARK: 构造 149 | 150 | /// 获取阅读记录对象,如果则创建对象返回 151 | @objc class func model(bookID:String!) ->DZMReadRecordModel { 152 | 153 | var recordModel:DZMReadRecordModel! 154 | 155 | if DZMReadRecordModel.isExist(bookID) { 156 | 157 | recordModel = DZMKeyedArchiver.unarchiver(folderName: bookID, fileName: DZM_READ_KEY_RECORD) as? DZMReadRecordModel 158 | 159 | recordModel.chapterModel.updateFont() 160 | 161 | }else{ 162 | 163 | recordModel = DZMReadRecordModel() 164 | 165 | recordModel.bookID = bookID 166 | } 167 | 168 | return recordModel 169 | } 170 | 171 | required init?(coder aDecoder: NSCoder) { 172 | 173 | super.init() 174 | 175 | bookID = aDecoder.decodeObject(forKey: "bookID") as? String 176 | 177 | chapterModel = aDecoder.decodeObject(forKey: "chapterModel") as? DZMReadChapterModel 178 | 179 | page = aDecoder.decodeObject(forKey: "page") as? NSNumber 180 | } 181 | 182 | func encode(with aCoder: NSCoder) { 183 | 184 | aCoder.encode(bookID, forKey: "bookID") 185 | 186 | aCoder.encode(chapterModel, forKey: "chapterModel") 187 | 188 | aCoder.encode(page, forKey: "page") 189 | } 190 | 191 | init(_ dict:Any? = nil) { 192 | 193 | super.init() 194 | 195 | if dict != nil { setValuesForKeys(dict as! [String : Any]) } 196 | } 197 | 198 | override func setValue(_ value: Any?, forUndefinedKey key: String) { } 199 | } 200 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/DZMeBookRead-Bridging-Pch.h: -------------------------------------------------------------------------------- 1 | // 2 | // DZMeBookRead-Bridging-Pch.h 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | #ifndef DZMeBookRead_Bridging_Pch_h 10 | #define DZMeBookRead_Bridging_Pch_h 11 | 12 | // MD5 13 | #import 14 | 15 | /// 导航栏扩展 16 | #import "UINavigationController+FDFullscreenPopGesture.h" 17 | 18 | /// 小说阅读效果控制器 (无效果,覆盖) 19 | #import "DZMCoverController.h" 20 | 21 | /// 章节列表顶部 22 | #import "DZMSegmentedControl.h" 23 | 24 | // 放大镜 25 | #import "DZMMagnifierView.h" 26 | 27 | /// 进度条 28 | #import "ASValueTrackingSlider.h" 29 | 30 | #endif /* DZMeBookRead_Bridging_Pch_h */ 31 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/base/DZMPageViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMPageViewController.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2020/3/17. 6 | // Copyright © 2020 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // 左边上一页点击区域 12 | private let LeftWidth:CGFloat = ScreenWidth / 3 13 | 14 | // 右边下一页点击区域 15 | private let RightWidth:CGFloat = ScreenWidth / 3 16 | 17 | @objc protocol DZMPageViewControllerDelegate: NSObjectProtocol { 18 | 19 | /// 获取上一页 20 | @objc optional func pageViewController(_ pageViewController: DZMPageViewController, getViewControllerBefore viewController: UIViewController!) 21 | 22 | /// 获取下一页 23 | @objc optional func pageViewController(_ pageViewController: DZMPageViewController, getViewControllerAfter viewController: UIViewController!) 24 | } 25 | 26 | class DZMPageViewController: UIPageViewController, UIGestureRecognizerDelegate { 27 | 28 | // 自定义tap手势的相关代理 29 | var aDelegate:DZMPageViewControllerDelegate? 30 | 31 | // 自定义Tap手势 32 | private(set) var customTapGestureRecognizer: UITapGestureRecognizer! 33 | 34 | override func viewDidLoad() { 35 | 36 | super.viewDidLoad() 37 | 38 | tapGestureRecognizerEnabled = false 39 | 40 | customTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(touchTap(tap:))) 41 | 42 | customTapGestureRecognizer.delegate = self 43 | 44 | view.addGestureRecognizer(customTapGestureRecognizer) 45 | } 46 | 47 | // tap事件 48 | @objc func touchTap(tap: UIGestureRecognizer) { 49 | 50 | let touchPoint = tap.location(in: view) 51 | 52 | if (touchPoint.x < LeftWidth) { // 左边 53 | 54 | aDelegate?.pageViewController?(self, getViewControllerBefore: viewControllers?.first) 55 | 56 | }else if (touchPoint.x > (ScreenWidth - RightWidth)) { // 右边 57 | 58 | aDelegate?.pageViewController?(self, getViewControllerAfter: viewControllers?.first) 59 | } 60 | } 61 | 62 | // MARK: UIGestureRecognizerDelegate 63 | 64 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 65 | 66 | if (gestureRecognizer.isKind(of: UITapGestureRecognizer.classForCoder()) && gestureRecognizer.isEqual(customTapGestureRecognizer)) { 67 | 68 | let touchPoint = customTapGestureRecognizer.location(in: view) 69 | 70 | if (touchPoint.x > LeftWidth && touchPoint.x < (ScreenWidth - RightWidth)) { 71 | 72 | return true 73 | } 74 | } 75 | 76 | return false 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/base/DZMTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMTableView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMTableView: UITableView { 12 | 13 | override init(frame: CGRect, style: UITableView.Style) { 14 | 15 | super.init(frame: frame, style: style) 16 | 17 | backgroundColor = UIColor.clear 18 | 19 | separatorStyle = .none 20 | 21 | if #available(iOS 11.0, *) { 22 | 23 | contentInsetAdjustmentBehavior = .never 24 | estimatedRowHeight = 0 25 | estimatedSectionFooterHeight = 0 26 | estimatedSectionHeaderHeight = 0 27 | } 28 | } 29 | 30 | required init?(coder aDecoder: NSCoder) { 31 | 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/base/DZMViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMViewController.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | 15 | super.viewDidLoad() 16 | 17 | initialize() 18 | 19 | addSubviews() 20 | 21 | addComplete() 22 | } 23 | 24 | func initialize() { 25 | 26 | view.backgroundColor = UIColor.white 27 | 28 | extendedLayoutIncludesOpaqueBars = true 29 | 30 | if #available(iOS 11.0, *) { } else { 31 | 32 | automaticallyAdjustsScrollViewInsets = false 33 | } 34 | } 35 | 36 | func addSubviews() { } 37 | 38 | func addComplete() { } 39 | } 40 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/custom/DZMKeyedArchiver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMKeyedArchiver.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMKeyedArchiver: NSObject { 12 | 13 | /// 归档文件 14 | class func archiver(folderName:String!, fileName:String!, object:AnyObject!) { 15 | 16 | var path = DZM_READ_DOCUMENT_DIRECTORY_PATH + "/\(DZM_READ_FOLDER_NAME)/\(folderName!)" 17 | 18 | if creat_file(path: path) { // 创建文件夹成功或者文件夹存在 19 | 20 | path += "/\(fileName!)" 21 | 22 | NSKeyedArchiver.archiveRootObject(object, toFile: path) 23 | } 24 | } 25 | 26 | /// 解档文件 27 | class func unarchiver(folderName:String!, fileName:String!) ->AnyObject? { 28 | 29 | let path = DZM_READ_DOCUMENT_DIRECTORY_PATH + "/\(DZM_READ_FOLDER_NAME)/\(folderName!)/\(fileName!)" 30 | 31 | return NSKeyedUnarchiver.unarchiveObject(withFile: path) as AnyObject? 32 | } 33 | 34 | /// 删除归档文件 35 | class func remove(folderName:String!, fileName:String? = nil) ->Bool { 36 | 37 | var path = DZM_READ_DOCUMENT_DIRECTORY_PATH + "/\(DZM_READ_FOLDER_NAME)/\(folderName!)" 38 | 39 | if fileName != nil && !fileName!.isEmpty { path += "/\(fileName!)" } 40 | 41 | do{ 42 | try FileManager.default.removeItem(atPath: path) 43 | 44 | return true 45 | 46 | }catch{ } 47 | 48 | return false 49 | } 50 | 51 | /// 清空归档文件 52 | class func clear() ->Bool { 53 | 54 | let path = DZM_READ_DOCUMENT_DIRECTORY_PATH + "/\(DZM_READ_FOLDER_NAME)" 55 | 56 | do{ 57 | 58 | try FileManager.default.removeItem(atPath: path) 59 | 60 | return true 61 | 62 | }catch{ } 63 | 64 | return false 65 | } 66 | 67 | /// 是否存在归档文件 68 | class func isExist(folderName:String!, fileName:String? = nil) ->Bool { 69 | 70 | var path = DZM_READ_DOCUMENT_DIRECTORY_PATH + "/\(DZM_READ_FOLDER_NAME)/\(folderName!)" 71 | 72 | if fileName != nil && !fileName!.isEmpty { path += "/\(fileName!)" } 73 | 74 | return FileManager.default.fileExists(atPath: path) 75 | } 76 | 77 | /// 创建文件夹,如果存在则不创建 78 | private class func creat_file(path:String) ->Bool { 79 | 80 | let fileManager = FileManager.default 81 | 82 | if fileManager.fileExists(atPath: path) { return true } 83 | 84 | do{ 85 | try fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil) 86 | 87 | return true 88 | 89 | }catch{ } 90 | 91 | return false 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/custom/DZMUserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMUserDefaults.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/18. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMUserDefaults: NSObject { 12 | 13 | // MARK: 删除清空 14 | 15 | /// 清空 16 | class func clear() { 17 | 18 | let defaults:UserDefaults = UserDefaults.standard 19 | let dictionary = defaults.dictionaryRepresentation() 20 | 21 | for key in dictionary.keys { 22 | 23 | defaults.removeObject(forKey: key) 24 | defaults.synchronize() 25 | } 26 | } 27 | 28 | /// 删除 29 | class func remove(_ key:String) { 30 | let defaults:UserDefaults = UserDefaults.standard 31 | defaults.removeObject(forKey: key) 32 | defaults.synchronize() 33 | } 34 | 35 | // MARK: -- 存储 36 | 37 | /// 存储Object 38 | class func setObject(_ value:Any?, _ key:String) { 39 | let defaults:UserDefaults = UserDefaults.standard 40 | defaults.set(value, forKey: key) 41 | defaults.synchronize() 42 | } 43 | 44 | /// 存储String 45 | class func setString(_ value:String?, _ key:String) { 46 | DZMUserDefaults.setObject(value, key) 47 | } 48 | 49 | /// 存储NSInteger 50 | class func setInteger(_ value:NSInteger, _ key:String) { 51 | let defaults:UserDefaults = UserDefaults.standard 52 | defaults.set(value, forKey: key) 53 | defaults.synchronize() 54 | } 55 | 56 | /// 存储Bool 57 | class func setBool(_ value:Bool, _ key:String) { 58 | let defaults:UserDefaults = UserDefaults.standard 59 | defaults.set(value, forKey: key) 60 | defaults.synchronize() 61 | } 62 | 63 | /// 存储Float 64 | class func setFloat(_ value:Float, _ key:String) { 65 | let defaults:UserDefaults = UserDefaults.standard 66 | defaults.set(value, forKey: key) 67 | defaults.synchronize() 68 | } 69 | 70 | /// 存储TimeInterval 71 | class func setTime(_ value:TimeInterval, _ key:String) { 72 | setInteger(NSInteger(value), key) 73 | } 74 | 75 | /// 存储Double 76 | class func setDouble(_ value:Double, _ key:String) { 77 | let defaults:UserDefaults = UserDefaults.standard 78 | defaults.set(value, forKey: key) 79 | defaults.synchronize() 80 | } 81 | 82 | /// 存储URL 83 | class func setURL(_ value:URL?, _ key:String) { 84 | let defaults:UserDefaults = UserDefaults.standard 85 | defaults.set(value, forKey: key) 86 | defaults.synchronize() 87 | } 88 | 89 | // MARK: -- 获取 90 | 91 | /// 获取Object 92 | class func object(_ key:String) -> Any? { 93 | let defaults:UserDefaults = UserDefaults.standard 94 | return defaults.object(forKey: key) 95 | } 96 | 97 | /// 获取String 98 | class func string(_ key:String) -> String { 99 | let defaults:UserDefaults = UserDefaults.standard 100 | let string = defaults.object(forKey: key) as? String 101 | return string ?? "" 102 | } 103 | 104 | /// 获取Bool 105 | class func bool(_ key:String) -> Bool { 106 | let defaults:UserDefaults = UserDefaults.standard 107 | return defaults.bool(forKey: key) 108 | } 109 | 110 | /// 获取NSInteger 111 | class func integer(_ key:String) -> NSInteger { 112 | let defaults:UserDefaults = UserDefaults.standard 113 | return defaults.integer(forKey: key) 114 | } 115 | 116 | /// 获取Float 117 | class func float(_ key:String) -> Float { 118 | let defaults:UserDefaults = UserDefaults.standard 119 | return defaults.float(forKey: key) 120 | } 121 | 122 | /// 获取Double 123 | class func double(_ key:String) -> Double { 124 | let defaults:UserDefaults = UserDefaults.standard 125 | return defaults.double(forKey: key) 126 | } 127 | 128 | /// 获取TimeInterval 129 | class func time(_ key:String) -> TimeInterval { 130 | return TimeInterval(integer(key)) 131 | } 132 | 133 | /// 获取URL 134 | class func url(_ key:String) -> URL? { 135 | let defaults:UserDefaults = UserDefaults.standard 136 | return defaults.url(forKey: key) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/extension/String+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Extension.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension String { 12 | 13 | var length:Int { return (self as NSString).length } 14 | 15 | var bool:Bool { return (self as NSString).boolValue } 16 | 17 | var integer:NSInteger { return (self as NSString).integerValue } 18 | 19 | var float:Float { return (self as NSString).floatValue } 20 | 21 | var cgFloat:CGFloat { return CGFloat(self.float) } 22 | 23 | var double:Double { return (self as NSString).doubleValue } 24 | 25 | /// 文件后缀(不带'.') 26 | var pathExtension:String { return (self as NSString).pathExtension } 27 | 28 | /// 文件名(带后缀) 29 | var lastPathComponent:String { return (self as NSString).lastPathComponent } 30 | 31 | /// 文件名(不带后缀) 32 | var deletingPathExtension:String { return (self as NSString).deletingPathExtension } 33 | 34 | /// 去除首尾空格 35 | var removeSpaceHeadAndTail:String { return trimmingCharacters(in: NSCharacterSet.whitespaces) } 36 | 37 | /// 去除首尾换行 38 | var removeEnterHeadAndTail:String { return trimmingCharacters(in: NSCharacterSet.whitespaces) } 39 | 40 | /// 去除首尾空格和换行 41 | var removeSEHeadAndTail:String { return trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines) } 42 | 43 | /// 去掉所有空格 44 | var removeSapceAll:String { return replacingOccurrences(of: " ", with: "").replacingOccurrences(of: " ", with: "") } 45 | 46 | /// 去除所有换行 47 | var removeEnterAll:String { return replacingOccurrences(of: "\r", with: "").replacingOccurrences(of: "\n", with: "") } 48 | 49 | /// 去除所有空格换行 50 | var removeSapceEnterAll:String { return removeSapceAll.replacingOccurrences(of: "\n", with: "") } 51 | 52 | /// 是否为整数 53 | var isInt:Bool { 54 | 55 | let scan: Scanner = Scanner(string: self) 56 | 57 | var val:Int = 0 58 | 59 | return scan.scanInt(&val) && scan.isAtEnd 60 | } 61 | 62 | /// 是否为数字或Float 63 | var isFloat:Bool { 64 | 65 | let scan: Scanner = Scanner(string: self) 66 | 67 | var val:Float = 0 68 | 69 | return scan.scanFloat(&val) && scan.isAtEnd 70 | } 71 | 72 | /// 是否为空格 73 | var isSpace:Bool { 74 | 75 | if (self == " ") || (self == " ") { return true } 76 | 77 | return false 78 | } 79 | 80 | /// 是否为空格或者回车 81 | var isSpaceOrEnter:Bool { 82 | 83 | if isSpace || (self == "\n") { return true } 84 | 85 | return false 86 | } 87 | 88 | /// MD5加密 89 | var md5:String { 90 | 91 | let string = cString(using: String.Encoding.utf8) 92 | 93 | let stringLength = CUnsignedInt(lengthOfBytes(using: String.Encoding.utf8)) 94 | 95 | let digestLength = Int(CC_MD5_DIGEST_LENGTH) 96 | 97 | let result = UnsafeMutablePointer.allocate(capacity: digestLength) 98 | 99 | CC_MD5(string!, stringLength, result) 100 | 101 | let hash = NSMutableString() 102 | 103 | for i in 0 ..< digestLength { hash.appendFormat("%02x", result[i]) } 104 | 105 | result.deinitialize() 106 | 107 | return String(format: hash as String) 108 | } 109 | 110 | /// 转JSON 111 | var json:Any? { 112 | 113 | let data = self.data(using: String.Encoding.utf8, allowLossyConversion: false) 114 | 115 | let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) 116 | 117 | return json 118 | } 119 | 120 | /// 是否包含指定字符串 121 | func range(_ string: String) ->NSRange { 122 | 123 | return (self as NSString).range(of: string) 124 | } 125 | 126 | /// 截取字符串 127 | func substring(_ range:NSRange) ->String { 128 | 129 | return (self as NSString).substring(with: range) 130 | } 131 | 132 | /// 处理带中文的字符串 133 | func addingPercentEncoding(_ characters: CharacterSet = .urlQueryAllowed) ->String { 134 | 135 | return (self as NSString).addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? "" 136 | } 137 | 138 | /// 正则替换字符 139 | func replacingCharacters(_ pattern:String, _ template:String) ->String { 140 | 141 | do { 142 | let regularExpression = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options.caseInsensitive) 143 | 144 | return regularExpression.stringByReplacingMatches(in: self, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, length), withTemplate: template) 145 | 146 | } catch {return self} 147 | } 148 | 149 | /// 正则搜索相关字符位置 150 | func matches(_ pattern:String) ->[NSTextCheckingResult] { 151 | 152 | if isEmpty {return []} 153 | 154 | do { 155 | let regularExpression = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options.caseInsensitive) 156 | 157 | return regularExpression.matches(in: self, options: NSRegularExpression.MatchingOptions.reportProgress, range: NSMakeRange(0, length)) 158 | 159 | } catch {return []} 160 | } 161 | 162 | /// 计算大小 163 | func size(_ font:UIFont, _ size:CGSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)) ->CGSize { 164 | 165 | let string:NSString = self as NSString 166 | 167 | return string.boundingRect(with: size, options: [.usesLineFragmentOrigin,.usesFontLeading], attributes: [.font:font], context: nil).size 168 | } 169 | } 170 | 171 | extension NSAttributedString { 172 | 173 | /// 计算size 174 | func size(_ size:CGSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)) ->CGSize{ 175 | 176 | return self.boundingRect(with: size, options: [NSStringDrawingOptions.usesLineFragmentOrigin,NSStringDrawingOptions.usesFontLeading], context: nil).size 177 | } 178 | 179 | /// 扩展拼接 180 | func add(_ string:T) ->NSAttributedString { 181 | 182 | let attributedText = NSMutableAttributedString(attributedString: self) 183 | 184 | attributedText.append(string) 185 | 186 | return attributedText 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/extension/UIPageViewController+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIPageViewController+Extension.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/30. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | private var IsGestureRecognizerEnabled = "IsGestureRecognizerEnabled" 10 | private var TapIsGestureRecognizerEnabled = "TapIsGestureRecognizerEnabled" 11 | 12 | import UIKit 13 | 14 | extension UIPageViewController { 15 | 16 | /// 手势启用 17 | var gestureRecognizerEnabled:Bool { 18 | 19 | get{ return (objc_getAssociatedObject(self, &IsGestureRecognizerEnabled) as? Bool) ?? true } 20 | 21 | set{ 22 | 23 | for ges in gestureRecognizers { ges.isEnabled = newValue } 24 | 25 | objc_setAssociatedObject(self, &IsGestureRecognizerEnabled, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_ASSIGN) 26 | } 27 | } 28 | 29 | /// tap手势 30 | var tapGestureRecognizer:UITapGestureRecognizer? { 31 | 32 | for ges in gestureRecognizers { 33 | 34 | if ges.isKind(of: UITapGestureRecognizer.classForCoder()) { 35 | 36 | return ges as? UITapGestureRecognizer 37 | } 38 | } 39 | 40 | return nil 41 | } 42 | 43 | /// tap手势启用 44 | var tapGestureRecognizerEnabled:Bool { 45 | 46 | get{ return (objc_getAssociatedObject(self, &TapIsGestureRecognizerEnabled) as? Bool) ?? true } 47 | 48 | set{ 49 | 50 | tapGestureRecognizer?.isEnabled = newValue 51 | 52 | objc_setAssociatedObject(self, &TapIsGestureRecognizerEnabled, newValue , objc_AssociationPolicy.OBJC_ASSOCIATION_ASSIGN) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/public/DZMColorList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMColorList.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let DZM_COLOR_46_46_46:UIColor = RGB(46, 46, 46) // 菜单背景颜色 12 | let DZM_COLOR_58_52_54:UIColor = RGB(58, 52, 54) // 阅读背景颜色支持 || 灰色 13 | let DZM_COLOR_124_126_128:UIColor = RGB(124, 126, 128) // 阅读上下状态栏字体颜色 14 | let DZM_COLOR_145_145_145:UIColor = RGB(145, 145, 145) // 阅读字体颜色 15 | let DZM_COLOR_205_239_205:UIColor = RGB(205, 239, 205) // 阅读背景颜色支持 16 | let DZM_COLOR_206_233_241:UIColor = RGB(206, 233, 241) // 阅读背景颜色支持 17 | let DZM_COLOR_230_230_230:UIColor = RGB(230, 230, 230) // 分割线 || 菜单默认颜色 18 | let DZM_COLOR_238_224_202:UIColor = RGB(238, 224, 202) // 阅读背景颜色支持 19 | let DZM_COLOR_253_85_103:UIColor = RGB(253, 85, 103) // 粉红色 20 | 21 | /// 阅读背景颜色支持 - 牛皮黄 22 | let DZM_COLOR_BG_0:UIColor = UIColor(patternImage: UIImage(named: "read_bg_0_icon")!) 23 | 24 | /// 随机颜色 25 | var DZM_COLOR_ARC:UIColor { return RGB(CGFloat(arc4random() % 255), CGFloat(arc4random() % 255), CGFloat(arc4random() % 255)) } 26 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/public/DZMEnumList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMEnumList.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/18. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// 书籍来源类型 12 | enum DZMBookSourceType:NSInteger { 13 | /// 网络小说 14 | case network 15 | /// 本地小说 16 | case local 17 | } 18 | 19 | /// 阅读翻页类型 20 | enum DZMEffectType:NSInteger { 21 | /// 仿真 22 | case simulation 23 | /// 覆盖 24 | case cover 25 | /// 平移 26 | case translation 27 | /// 滚动 28 | case scroll 29 | /// 无效果 30 | case no 31 | } 32 | 33 | /// 阅读字体类型 34 | enum DZMFontType:NSInteger { 35 | /// 系统 36 | case system 37 | /// 黑体 38 | case one 39 | /// 楷体 40 | case two 41 | /// 宋体 42 | case three 43 | } 44 | 45 | /// 阅读内容间距类型 46 | enum DZMSpacingType:NSInteger { 47 | /// 大间距 48 | case big 49 | /// 适中间距 50 | case middle 51 | /// 小间距 52 | case small 53 | } 54 | 55 | /// 阅读进度类型 56 | enum DZMProgressType:NSInteger { 57 | /// 总进度 58 | case total 59 | /// 分页进度 60 | case page 61 | } 62 | 63 | /// 分页内容是以什么开头 64 | enum DZMPageHeadType:NSInteger { 65 | /// 章节名 66 | case chapterName 67 | /// 段落 68 | case paragraph 69 | /// 行内容 70 | case line 71 | } 72 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/public/DZMFontList.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMFontList.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Font Size List 12 | let DZM_FONT_SIZE_8:CGFloat = 8 13 | let DZM_FONT_SIZE_9:CGFloat = 9 14 | let DZM_FONT_SIZE_10:CGFloat = 10 15 | let DZM_FONT_SIZE_11:CGFloat = 11 16 | let DZM_FONT_SIZE_12:CGFloat = 12 17 | let DZM_FONT_SIZE_13:CGFloat = 13 18 | let DZM_FONT_SIZE_14:CGFloat = 14 19 | let DZM_FONT_SIZE_15:CGFloat = 15 20 | let DZM_FONT_SIZE_16:CGFloat = 16 21 | let DZM_FONT_SIZE_17:CGFloat = 17 22 | let DZM_FONT_SIZE_18:CGFloat = 18 23 | let DZM_FONT_SIZE_19:CGFloat = 19 24 | let DZM_FONT_SIZE_20:CGFloat = 20 25 | let DZM_FONT_SIZE_21:CGFloat = 21 26 | let DZM_FONT_SIZE_22:CGFloat = 22 27 | let DZM_FONT_SIZE_23:CGFloat = 23 28 | let DZM_FONT_SIZE_24:CGFloat = 24 29 | let DZM_FONT_SIZE_25:CGFloat = 25 30 | let DZM_FONT_SIZE_26:CGFloat = 26 31 | let DZM_FONT_SIZE_29:CGFloat = 29 32 | let DZM_FONT_SIZE_30:CGFloat = 30 33 | 34 | let DZM_FONT_SIZE_SA_8:CGFloat = SA_SIZE(DZM_FONT_SIZE_8) 35 | let DZM_FONT_SIZE_SA_9:CGFloat = SA_SIZE(DZM_FONT_SIZE_9) 36 | let DZM_FONT_SIZE_SA_10:CGFloat = SA_SIZE(DZM_FONT_SIZE_10) 37 | let DZM_FONT_SIZE_SA_11:CGFloat = SA_SIZE(DZM_FONT_SIZE_11) 38 | let DZM_FONT_SIZE_SA_12:CGFloat = SA_SIZE(DZM_FONT_SIZE_12) 39 | let DZM_FONT_SIZE_SA_13:CGFloat = SA_SIZE(DZM_FONT_SIZE_13) 40 | let DZM_FONT_SIZE_SA_14:CGFloat = SA_SIZE(DZM_FONT_SIZE_14) 41 | let DZM_FONT_SIZE_SA_15:CGFloat = SA_SIZE(DZM_FONT_SIZE_15) 42 | let DZM_FONT_SIZE_SA_16:CGFloat = SA_SIZE(DZM_FONT_SIZE_16) 43 | let DZM_FONT_SIZE_SA_17:CGFloat = SA_SIZE(DZM_FONT_SIZE_17) 44 | let DZM_FONT_SIZE_SA_18:CGFloat = SA_SIZE(DZM_FONT_SIZE_18) 45 | let DZM_FONT_SIZE_SA_19:CGFloat = SA_SIZE(DZM_FONT_SIZE_19) 46 | let DZM_FONT_SIZE_SA_20:CGFloat = SA_SIZE(DZM_FONT_SIZE_20) 47 | let DZM_FONT_SIZE_SA_21:CGFloat = SA_SIZE(DZM_FONT_SIZE_21) 48 | let DZM_FONT_SIZE_SA_22:CGFloat = SA_SIZE(DZM_FONT_SIZE_22) 49 | let DZM_FONT_SIZE_SA_23:CGFloat = SA_SIZE(DZM_FONT_SIZE_23) 50 | let DZM_FONT_SIZE_SA_24:CGFloat = SA_SIZE(DZM_FONT_SIZE_24) 51 | let DZM_FONT_SIZE_SA_25:CGFloat = SA_SIZE(DZM_FONT_SIZE_25) 52 | let DZM_FONT_SIZE_SA_26:CGFloat = SA_SIZE(DZM_FONT_SIZE_26) 53 | let DZM_FONT_SIZE_SA_29:CGFloat = SA_SIZE(DZM_FONT_SIZE_29) 54 | let DZM_FONT_SIZE_SA_30:CGFloat = SA_SIZE(DZM_FONT_SIZE_30) 55 | 56 | /// Font List 57 | let DZM_FONT_SA_10 = DZM_FONT(DZM_FONT_SIZE_SA_10) 58 | let DZM_FONT_SA_12 = DZM_FONT(DZM_FONT_SIZE_SA_12) 59 | let DZM_FONT_SA_14 = DZM_FONT(DZM_FONT_SIZE_SA_14) 60 | let DZM_FONT_SA_15 = DZM_FONT(DZM_FONT_SIZE_SA_15) 61 | let DZM_FONT_SA_16 = DZM_FONT(DZM_FONT_SIZE_SA_16) 62 | let DZM_FONT_SA_18 = DZM_FONT(DZM_FONT_SIZE_SA_18) 63 | func DZM_FONT(_ size:CGFloat) ->UIFont { return UIFont.systemFont(ofSize: size) } 64 | func DZM_FONT_SA(_ size:CGFloat) ->UIFont { return UIFont.systemFont(ofSize: SA_SIZE(size)) } 65 | 66 | let DZM_FONT_BOLD_SA_16 = DZM_FONT_BOLD(DZM_FONT_SIZE_SA_16) 67 | func DZM_FONT_BOLD(_ size:CGFloat) ->UIFont { return UIFont.boldSystemFont(ofSize: size) } 68 | func DZM_FONT_BOLD_SA(_ size:CGFloat) ->UIFont { return UIFont.boldSystemFont(ofSize: SA_SIZE(size)) } 69 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/ASValueTrackingSlider/ASValuePopUpView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASValuePopUpView.h 3 | // ValueTrackingSlider 4 | // 5 | // Created by Alan Skipp on 27/03/2014. 6 | // Copyright (c) 2014 Alan Skipp. All rights reserved. 7 | // 8 | 9 | // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 | // This UIView subclass is used internally by ASValueTrackingSlider 11 | // The public API is declared in ASValueTrackingSlider.h 12 | // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 13 | 14 | #import 15 | 16 | @protocol ASValuePopUpViewDelegate 17 | - (CGFloat)currentValueOffset; //expects value in the range 0.0 - 1.0 18 | - (void)colorDidUpdate:(UIColor *)opaqueColor; 19 | @end 20 | 21 | @interface ASValuePopUpView : UIView 22 | 23 | @property (weak, nonatomic) id delegate; 24 | @property (nonatomic) CGFloat cornerRadius; 25 | @property (nonatomic) CGFloat arrowLength; 26 | @property (nonatomic) CGFloat widthPaddingFactor; 27 | @property (nonatomic) CGFloat heightPaddingFactor; 28 | 29 | - (UIColor *)color; 30 | - (void)setColor:(UIColor *)color; 31 | - (UIColor *)opaqueColor; 32 | 33 | - (void)setTextColor:(UIColor *)textColor; 34 | - (void)setFont:(UIFont *)font; 35 | - (void)setText:(NSString *)text; 36 | 37 | - (void)setAnimatedColors:(NSArray *)animatedColors withKeyTimes:(NSArray *)keyTimes; 38 | 39 | - (void)setAnimationOffset:(CGFloat)animOffset returnColor:(void (^)(UIColor *opaqueReturnColor))block; 40 | 41 | - (void)setFrame:(CGRect)frame arrowOffset:(CGFloat)arrowOffset text:(NSString *)text; 42 | 43 | - (void)animateBlock:(void (^)(CFTimeInterval duration))block; 44 | 45 | - (CGSize)popUpSizeForString:(NSString *)string; 46 | 47 | - (void)showAnimated:(BOOL)animated; 48 | - (void)hideAnimated:(BOOL)animated completionBlock:(void (^)())block; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/ASValueTrackingSlider/ASValueTrackingSlider.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASValueTrackingSlider.h 3 | // ValueTrackingSlider 4 | // 5 | // Created by Alan Skipp on 19/10/2013. 6 | // Copyright (c) 2013 Alan Skipp. All rights reserved. 7 | // 8 | 9 | #import 10 | @protocol ASValueTrackingSliderDelegate; 11 | @protocol ASValueTrackingSliderDataSource; 12 | 13 | @interface ASValueTrackingSlider : UISlider 14 | 15 | // present the popUpView manually, without touch event. 16 | - (void)showPopUpViewAnimated:(BOOL)animated; 17 | // the popUpView will not hide again until you call 'hidePopUpViewAnimated:' 18 | - (void)hidePopUpViewAnimated:(BOOL)animated; 19 | 20 | @property (strong, nonatomic) UIColor *textColor; 21 | 22 | // font can not be nil, it must be a valid UIFont 23 | // default is ‘boldSystemFontOfSize:22.0’ 24 | @property (strong, nonatomic) UIFont *font; 25 | 26 | // setting the value of 'popUpViewColor' overrides 'popUpViewAnimatedColors' and vice versa 27 | // the return value of 'popUpViewColor' is the currently displayed value 28 | // this will vary if 'popUpViewAnimatedColors' is set (see below) 29 | @property (strong, nonatomic) UIColor *popUpViewColor; 30 | 31 | // pass an array of 2 or more UIColors to animate the color change as the slider moves 32 | @property (strong, nonatomic) NSArray *popUpViewAnimatedColors; 33 | 34 | // the above @property distributes the colors evenly across the slider 35 | // to specify the exact position of colors on the slider scale, pass an NSArray of NSNumbers 36 | - (void)setPopUpViewAnimatedColors:(NSArray *)popUpViewAnimatedColors withPositions:(NSArray *)positions; 37 | 38 | // cornerRadius of the popUpView, default is 4.0 39 | @property (nonatomic) CGFloat popUpViewCornerRadius; 40 | 41 | // arrow height of the popUpView, default is 13.0 42 | @property (nonatomic) CGFloat popUpViewArrowLength; 43 | // width padding factor of the popUpView, default is 1.15 44 | @property (nonatomic) CGFloat popUpViewWidthPaddingFactor; 45 | // height padding factor of the popUpView, default is 1.1 46 | @property (nonatomic) CGFloat popUpViewHeightPaddingFactor; 47 | 48 | // changes the left handside of the UISlider track to match current popUpView color 49 | // the track color alpha is always set to 1.0, even if popUpView color is less than 1.0 50 | @property (nonatomic) BOOL autoAdjustTrackColor; // (default is YES) 51 | 52 | // when setting max FractionDigits the min value is automatically set to the same value 53 | // this ensures that the PopUpView frame maintains a consistent width 54 | - (void)setMaxFractionDigitsDisplayed:(NSUInteger)maxDigits; 55 | 56 | // take full control of the format dispayed with a custom NSNumberFormatter 57 | @property (copy, nonatomic) NSNumberFormatter *numberFormatter; 58 | 59 | // supply entirely customized strings for slider values using the datasource protocol - see below 60 | @property (weak, nonatomic) id dataSource; 61 | 62 | // delegate is only needed when used with a TableView or CollectionView - see below 63 | @property (weak, nonatomic) id delegate; 64 | @end 65 | 66 | 67 | 68 | // to supply custom text to the popUpView label, implement 69 | // the dataSource will be messaged each time the slider value changes 70 | @protocol ASValueTrackingSliderDataSource 71 | - (NSString *)slider:(ASValueTrackingSlider *)slider stringForValue:(float)value; 72 | @end 73 | 74 | // when embedding an ASValueTrackingSlider inside a TableView or CollectionView 75 | // you need to ensure that the cell it resides in is brought to the front of the view hierarchy 76 | // to prevent the popUpView from being obscured 77 | @protocol ASValueTrackingSliderDelegate 78 | - (void)sliderWillDisplayPopUpView:(ASValueTrackingSlider *)slider; 79 | 80 | @optional 81 | - (void)sliderWillHidePopUpView:(ASValueTrackingSlider *)slider; 82 | - (void)sliderDidHidePopUpView:(ASValueTrackingSlider *)slider; 83 | @end 84 | 85 | /* 86 | // the recommended technique for use with a tableView is to create a UITableViewCell subclass ↓ 87 | 88 | @interface SliderCell : UITableViewCell 89 | @property (weak, nonatomic) IBOutlet ASValueTrackingSlider *slider; 90 | @end 91 | 92 | @implementation SliderCell 93 | - (void)awakeFromNib 94 | { 95 | [super awakeFromNib]; 96 | self.slider.delegate = self; 97 | } 98 | 99 | - (void)sliderWillDisplayPopUpView:(ASValueTrackingSlider *)slider; 100 | { 101 | [self.superview bringSubviewToFront:self]; 102 | } 103 | @end 104 | */ 105 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/DZMBatteryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMBatteryView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by 邓泽淼 on 2017/5/11. 6 | // Copyright © 2017年 DZM. All rights reserved. 7 | // 8 | 9 | 10 | /// 电池宽推荐使用宽高 11 | var DZMBatterySize:CGSize = CGSize(width: 25, height: 10) 12 | 13 | /// 电池量宽度 跟图片的比例 14 | private var HJBatteryLevelViewW:CGFloat = 20 15 | private var HJBatteryLevelViewScale = HJBatteryLevelViewW / DZMBatterySize.width 16 | 17 | import UIKit 18 | 19 | class DZMBatteryView: UIImageView { 20 | 21 | /// 颜色 22 | override var tintColor: UIColor! { 23 | 24 | didSet{ batteryLevelView.backgroundColor = tintColor } 25 | } 26 | 27 | /// BatteryLevel 28 | var batteryLevel:Float = 0 { 29 | 30 | didSet{ setNeedsLayout() } 31 | } 32 | 33 | /// BatteryLevelView 34 | private var batteryLevelView:UIView! 35 | 36 | /// 初始化 37 | convenience init() { 38 | 39 | self.init(frame: CGRect(x: 0, y: 0, width: DZMBatterySize.width, height: DZMBatterySize.height)) 40 | } 41 | 42 | /// 初始化 43 | override init(frame: CGRect) { 44 | 45 | super.init(frame: CGRect(x: 0, y: 0, width: DZMBatterySize.width, height: DZMBatterySize.height)) 46 | 47 | addSubviews() 48 | } 49 | 50 | func addSubviews() { 51 | 52 | // 进度 53 | batteryLevelView = UIView() 54 | batteryLevelView.layer.masksToBounds = true 55 | addSubview(batteryLevelView) 56 | 57 | // 设置样式 58 | image = UIImage(named: "battery_black")?.withRenderingMode(.alwaysTemplate) 59 | tintColor = UIColor.white 60 | } 61 | 62 | override func layoutSubviews() { 63 | super.layoutSubviews() 64 | 65 | let spaceW:CGFloat = 1 * (frame.width / DZMBatterySize.width) * HJBatteryLevelViewScale 66 | let spaceH:CGFloat = 1 * (frame.height / DZMBatterySize.height) * HJBatteryLevelViewScale 67 | 68 | let batteryLevelViewY:CGFloat = 2.1*spaceH 69 | let batteryLevelViewX:CGFloat = 1.4*spaceW 70 | let batteryLevelViewH:CGFloat = frame.height - 3.4*spaceH 71 | let batteryLevelViewW:CGFloat = frame.width * HJBatteryLevelViewScale 72 | let batteryLevelViewWScale:CGFloat = batteryLevelViewW / 100 73 | 74 | // 判断电量 75 | var tempBatteryLevel = batteryLevel 76 | 77 | if batteryLevel < 0 { 78 | 79 | tempBatteryLevel = 0 80 | 81 | }else if batteryLevel > 1 { 82 | 83 | tempBatteryLevel = 1 84 | 85 | }else{} 86 | 87 | batteryLevelView.frame = CGRect(x: batteryLevelViewX , y: batteryLevelViewY, width: CGFloat(tempBatteryLevel * 100) * batteryLevelViewWScale, height: batteryLevelViewH) 88 | batteryLevelView.layer.cornerRadius = batteryLevelViewH * 0.125 89 | } 90 | 91 | required init?(coder aDecoder: NSCoder) { 92 | fatalError("init(coder:) has not been implemented") 93 | } 94 | 95 | 96 | } 97 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/DZMCoverController/DZMCoverController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DZMCoverController.h 3 | // DZMCoverDemo 4 | // 5 | // Created by 邓泽淼 on 16/10/8. 6 | // Copyright © 2016年 DZM. All rights reserved. 7 | // 8 | 9 | /* 10 | 11 | 可以用于小说覆盖翻页样式使用。 现在支持的是代理返回控制器 。 12 | 13 | Show Finder 该工程文件里面有 返回View的使用 14 | 15 | */ 16 | 17 | #import 18 | 19 | @class DZMCoverController; 20 | 21 | @protocol DZMCoverControllerDelegate 22 | 23 | @optional 24 | 25 | /** 26 | * 切换是否完成 27 | * 28 | * @param coverController coverController 29 | * @param currentController 当前正在显示的控制器 30 | * @param isFinish 切换是否成功 31 | */ 32 | - (void)coverController:(DZMCoverController * _Nonnull)coverController currentController:(UIViewController * _Nullable)currentController finish:(BOOL)isFinish; 33 | 34 | /** 35 | * 将要显示的控制器 36 | * 37 | * @param coverController coverController 38 | * @param pendingController 将要显示的控制器 39 | */ 40 | - (void)coverController:(DZMCoverController * _Nonnull)coverController willTransitionToPendingController:(UIViewController * _Nullable)pendingController; 41 | 42 | /** 43 | * 获取上一个控制器 44 | * 45 | * @param coverController coverController 46 | * @param currentController 当前正在显示的控制器 47 | * 48 | * @return 返回当前显示控制器的上一个控制器 49 | */ 50 | - (UIViewController * _Nullable)coverController:(DZMCoverController * _Nonnull)coverController getAboveControllerWithCurrentController:(UIViewController * _Nullable)currentController; 51 | 52 | /** 53 | * 获取下一个控制器 54 | * 55 | * @param coverController coverController 56 | * @param currentController 当前正在显示的控制器 57 | * 58 | * @return 返回当前显示控制器的下一个控制器 59 | */ 60 | - (UIViewController * _Nullable)coverController:(DZMCoverController * _Nonnull)coverController getBelowControllerWithCurrentController:(UIViewController * _Nullable)currentController; 61 | 62 | @end 63 | 64 | @interface DZMCoverController : UIViewController 65 | 66 | /** 67 | * 代理 68 | */ 69 | @property (nonatomic,weak,nullable) id delegate; 70 | 71 | /** 72 | * 手势启用状态 default:YES 73 | */ 74 | @property (nonatomic,assign) BOOL gestureRecognizerEnabled; 75 | 76 | /** 77 | * Tap手势启用状态 default:YES 78 | */ 79 | @property (nonatomic,assign) BOOL tapGestureRecognizerEnabled; 80 | 81 | /** 82 | * 当前手势操作是否带动画效果 default: YES 83 | */ 84 | @property (nonatomic,assign) BOOL openAnimate; 85 | 86 | /** 87 | * 当前控制器 88 | */ 89 | @property (nonatomic,strong,readonly,nullable) UIViewController *currentController; 90 | 91 | /** 92 | * 手动设置显示控制器 无动画 93 | * 94 | * @param controller 设置显示的控制器 95 | */ 96 | - (void)setController:(UIViewController * _Nullable)controller; 97 | 98 | /** 99 | * 手动设置显示控制器 100 | * 101 | * @param controller 设置显示的控制器 102 | * @param animated 是否需要动画 103 | * @param isAbove 动画是否从上面显示 YES 从下面显示 NO 104 | */ 105 | - (void)setController:(UIViewController * _Nullable)controller animated:(BOOL)animated isAbove:(BOOL)isAbove; 106 | 107 | @end 108 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/DZMMagnifierView/DZMMagnifierView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DZMMagnifierView.h 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/30. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface DZMMagnifierView : UIWindow 14 | 15 | /// 目标视图Window (注意: 传视图的Window 例子: self.view.window) 16 | @property (nonatomic, weak, nullable) UIView *targetWindow; 17 | 18 | /// 目标视图展示位置 (放大镜需要展示的位置) 19 | @property (nonatomic, assign) CGPoint targetPoint; 20 | 21 | /// 放大镜位置偏移调整 (调整放大镜在原始位置上的偏移 默认: CGPointMake(0, -40)) 22 | @property (nonatomic, assign) CGPoint offsetPoint; 23 | 24 | /// 放大比例 默认: DZM_MV_SCALE 25 | @property (nonatomic, assign) CGFloat scale; 26 | 27 | /// 弱引用接收对象 (内部已经强引用,如果外部也强引用需要自己释放) 28 | + (nonnull instancetype)magnifierView; 29 | 30 | /// 移除 (移除对象 并释放内部强引用) 31 | - (void)remove:(nullable void (^)(void))complete; 32 | 33 | @end 34 | 35 | NS_ASSUME_NONNULL_END 36 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/DZMMagnifierView/DZMMagnifierView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DZMMagnifierView.m 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/30. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | /// 动画时间 10 | #define DZM_MV_AD_TIME 0.08 11 | 12 | /// 放大比例 13 | #define DZM_MV_SCALE 1.3 14 | 15 | /// 放大区域 16 | #define DZM_MV_WH 120 17 | 18 | #import "DZMMagnifierView.h" 19 | 20 | @interface DZMMagnifierView () 21 | 22 | @property (nonatomic, strong) DZMMagnifierView *strongSelf; 23 | 24 | @property (nonatomic, weak) CALayer *contentLayer; 25 | 26 | @property (nonatomic, weak) UIImageView *coverOne; 27 | 28 | @property (nonatomic, weak) UIImageView *coverTwo; 29 | 30 | @end 31 | 32 | 33 | @implementation DZMMagnifierView 34 | 35 | + (instancetype)magnifierView { 36 | 37 | DZMMagnifierView *mv = [[DZMMagnifierView alloc] init]; 38 | 39 | mv.strongSelf = mv; 40 | 41 | return mv; 42 | } 43 | 44 | - (id)initWithFrame:(CGRect)frame { 45 | 46 | self = [super initWithFrame:frame]; 47 | 48 | if (self) { } 49 | 50 | return self; 51 | } 52 | 53 | - (id)init { 54 | 55 | self = [super init]; 56 | 57 | if (self) { 58 | 59 | BOOL isIpad = [self isIpad]; 60 | 61 | self.offsetPoint = CGPointMake(0, -40); 62 | self.scale = DZM_MV_SCALE; 63 | 64 | self.frame = CGRectMake(0, 0, DZM_MV_WH, DZM_MV_WH); 65 | self.layer.cornerRadius = DZM_MV_WH / 2; 66 | self.layer.masksToBounds = YES; 67 | self.windowLevel = UIWindowLevelAlert; 68 | // iOS13 以后苹果增加了 SceneDelegate 来管理窗口,必须将自定义 Window 注册到 SceneDelegate 中 69 | if (@available(iOS 13.0, *)) { 70 | for (UIWindowScene *windowScene in [UIApplication sharedApplication].connectedScenes) { 71 | if (windowScene.activationState == UISceneActivationStateForegroundActive) { 72 | self.windowScene = windowScene; 73 | break; 74 | } 75 | } 76 | } 77 | if (isIpad) { 78 | self.layer.borderWidth = 1; 79 | self.layer.borderColor = [[[UIColor grayColor] colorWithAlphaComponent:0.9] CGColor]; 80 | } 81 | 82 | CALayer *contentLayer = [CALayer layer]; 83 | contentLayer.frame = self.bounds; 84 | contentLayer.delegate = self; 85 | contentLayer.contentsScale = [[UIScreen mainScreen] scale]; 86 | [self.layer addSublayer:contentLayer]; 87 | self.contentLayer = contentLayer; 88 | 89 | self.transform = CGAffineTransformMakeScale(0.5, 0.5); 90 | 91 | // ipad 会奔溃,所以先不使用图片 92 | if (!isIpad) { 93 | 94 | UIImageView *coverOne = [[UIImageView alloc] init]; 95 | coverOne.image = [UIImage imageNamed:@"magnifier_0"]; 96 | coverOne.frame = CGRectMake(0, 0, DZM_MV_WH, DZM_MV_WH); 97 | [self addSubview: coverOne]; 98 | self.coverOne = coverOne; 99 | 100 | UIImageView *coverTwo = [[UIImageView alloc] init]; 101 | coverTwo.image = [UIImage imageNamed:@"magnifier_1"]; 102 | coverTwo.frame = CGRectMake(0, 0, DZM_MV_WH, DZM_MV_WH); 103 | [self addSubview:coverTwo]; 104 | self.coverTwo = coverTwo; 105 | } 106 | 107 | } 108 | 109 | return self; 110 | } 111 | 112 | - (void)setOffsetPoint:(CGPoint)offsetPoint { 113 | 114 | _offsetPoint = offsetPoint; 115 | 116 | [self setTargetPoint:self.targetPoint]; 117 | } 118 | 119 | - (void)setScale:(CGFloat)scale { 120 | 121 | _scale = scale; 122 | 123 | [self.contentLayer setNeedsDisplay]; 124 | } 125 | 126 | - (void)setTargetWindow:(UIView *)targetWindow { 127 | 128 | _targetWindow = targetWindow; 129 | 130 | [self makeKeyAndVisible]; 131 | 132 | __weak DZMMagnifierView *weakSelf = self; 133 | 134 | [UIView animateWithDuration:DZM_MV_AD_TIME animations:^{ 135 | 136 | weakSelf.transform = CGAffineTransformMakeScale(1.0, 1.0); 137 | }]; 138 | 139 | [self setTargetPoint:self.targetPoint]; 140 | } 141 | 142 | - (void)setTargetPoint:(CGPoint)targetPoint { 143 | 144 | _targetPoint = targetPoint; 145 | 146 | if (self.targetWindow) { 147 | 148 | CGPoint center = CGPointMake(targetPoint.x, self.center.y); 149 | 150 | if (targetPoint.y > CGRectGetHeight(self.bounds) * 0.5) { 151 | 152 | center.y = targetPoint.y - CGRectGetHeight(self.bounds) / 2; 153 | } 154 | 155 | self.center = CGPointMake(center.x + self.offsetPoint.x, center.y + self.offsetPoint.y); 156 | 157 | [self.contentLayer setNeedsDisplay]; 158 | } 159 | } 160 | 161 | - (void)remove:(void (^)(void))complete { 162 | 163 | __weak DZMMagnifierView *weakSelf = self; 164 | 165 | [UIView animateWithDuration:DZM_MV_AD_TIME animations:^{ 166 | 167 | weakSelf.coverOne.alpha = 0; 168 | 169 | weakSelf.coverTwo.alpha = 0; 170 | 171 | weakSelf.alpha = 0; 172 | 173 | weakSelf.transform = CGAffineTransformMakeScale(0.2, 0.2); 174 | 175 | } completion:^(BOOL finished) { 176 | 177 | [weakSelf.coverOne removeFromSuperview]; 178 | 179 | [weakSelf.coverTwo removeFromSuperview]; 180 | 181 | [weakSelf removeFromSuperview]; 182 | 183 | weakSelf.strongSelf = nil; 184 | 185 | if (complete != nil) { complete(); } 186 | }]; 187 | } 188 | 189 | - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx 190 | { 191 | CGContextTranslateCTM(ctx, DZM_MV_WH / 2, DZM_MV_WH / 2); 192 | 193 | CGContextScaleCTM(ctx, self.scale, self.scale); 194 | 195 | CGContextTranslateCTM(ctx, -1 * self.targetPoint.x, -1 * self.targetPoint.y); 196 | 197 | [self.targetWindow.layer renderInContext:ctx]; 198 | } 199 | 200 | - (BOOL)isIpad { 201 | 202 | NSString *deviceType = [UIDevice currentDevice].model; 203 | 204 | if([deviceType isEqualToString:@"iPad"]) { return YES; } 205 | 206 | return NO; 207 | } 208 | 209 | - (void)dealloc 210 | { 211 | [self.contentLayer removeFromSuperlayer]; 212 | 213 | self.contentLayer = nil; 214 | } 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/DZMMagnifierView/magnifier_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/DZMeBookRead/other/thirdParty/DZMMagnifierView/magnifier_0.png -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/DZMMagnifierView/magnifier_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/DZMeBookRead/other/thirdParty/DZMMagnifierView/magnifier_1.png -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/DZMSegmentedControl/DZMSegmentedControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // DZMSegmentedControl.h 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/25. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// 滑条宽度自适应填充 14 | static CGFloat const DZMSegmentedControlSliderWidthFill = -1; 15 | 16 | @class DZMSegmentedControl; 17 | 18 | @protocol DZMSegmentedControlDelegate 19 | 20 | @optional 21 | 22 | /// 点击了按钮 23 | - (void)segmentedControl:(DZMSegmentedControl *)segmentedControl clickIndex:(NSInteger)index; 24 | 25 | /// 当前滑动到的索引 26 | - (void)segmentedControl:(DZMSegmentedControl *)segmentedControl scrollIndex:(NSInteger)index; 27 | 28 | @end 29 | 30 | @interface DZMSegmentedControl : UIView 31 | 32 | /// 代理 33 | @property (nonatomic, weak) id delegate; 34 | 35 | /// 默认字体 36 | @property (nonatomic, strong) UIFont *normalFont; 37 | 38 | /// 选中字体 39 | @property (nonatomic, strong) UIFont *selectFont; 40 | 41 | /// 默认字体颜色 42 | @property (nonatomic, strong) UIColor *normalColor; 43 | 44 | /// 选中字体颜色 45 | @property (nonatomic, strong) UIColor *selectColor; 46 | 47 | /// 滑条颜色 48 | @property (nonatomic, strong) UIColor *sliderColor; 49 | 50 | /// 滑条高度 51 | @property (nonatomic, assign) CGFloat sliderHeight; 52 | 53 | /// 滑条底部 54 | @property (nonatomic, assign) CGFloat sliderBottom; 55 | 56 | /// 滑条宽度 (小于0为自适应填充, 推荐使用 DZMSegmentedControlSliderWidthFill ) 57 | @property (nonatomic, assign) CGFloat sliderWidth; 58 | 59 | /// 按钮间距 60 | @property (nonatomic, assign) CGFloat itemSpace; 61 | 62 | /// 四周间距 63 | @property (nonatomic, assign) UIEdgeInsets insets; 64 | 65 | /// 当前选中索引 66 | @property (nonatomic, assign, readonly) NSInteger selectIndex; 67 | 68 | /// ----- 注意下面操作在确保 ToolBar Frame 有值之后调用 69 | 70 | /// 刷新(列表 | 布局 | 默认选中0) 71 | - (void)reloadTitles:(NSArray *)titles; 72 | 73 | /// 刷新(列表 | 布局) 74 | - (void)reloadTitles:(NSArray *)titles index:(NSInteger)index; 75 | 76 | /// 刷新(布局) 77 | - (void)reloadUI; 78 | 79 | /// 滚动到指定索引 80 | - (void)scrollIndex:(NSInteger)index animated:(BOOL)animated; 81 | 82 | /// 滑动滑条(暂时不支持,待开发) 83 | - (void)scrollSlider:(UIScrollView *)scrollView; 84 | @end 85 | 86 | NS_ASSUME_NONNULL_END 87 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/DZMSegmentedControl/DZMSegmentedControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // DZMSegmentedControl.m 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/25. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | #import "DZMSegmentedControl.h" 10 | 11 | @interface DZMSegmentedControl() 12 | 13 | /// 默认字体 14 | @property (nonatomic, strong) NSMutableArray *items; 15 | 16 | /// 滑动条 17 | @property (nonatomic, strong) UIView *sliderView; 18 | 19 | /// 当前选中按钮 20 | @property (nonatomic, weak) UIButton *selectItem; 21 | 22 | @end 23 | 24 | @implementation DZMSegmentedControl 25 | 26 | - (NSMutableArray *)items { 27 | 28 | if (!_items) { 29 | 30 | _items = [NSMutableArray array]; 31 | } 32 | 33 | return _items; 34 | } 35 | 36 | - (instancetype)initWithFrame:(CGRect)frame { 37 | 38 | if (self = [super initWithFrame:frame]) { 39 | 40 | self.normalFont = [UIFont systemFontOfSize:14]; 41 | self.selectFont = [UIFont boldSystemFontOfSize:16]; 42 | self.normalColor = [UIColor grayColor]; 43 | self.selectColor = [UIColor redColor]; 44 | self.sliderColor = [UIColor redColor]; 45 | self.sliderHeight = 2; 46 | self.sliderBottom = 0; 47 | self.sliderWidth = DZMSegmentedControlSliderWidthFill; 48 | self.itemSpace = 0; 49 | self.sliderView = [[UIView alloc] init]; 50 | self.insets = UIEdgeInsetsZero; 51 | _selectIndex = -1; 52 | } 53 | 54 | return self; 55 | } 56 | 57 | /// 刷新列表 58 | - (void)reloadTitles:(NSArray *)titles { 59 | 60 | [self reloadTitles:titles index:0]; 61 | } 62 | 63 | /// 刷新列表 并 选中指定按钮 64 | - (void)reloadTitles:(NSArray *)titles index:(NSInteger)index { 65 | 66 | if (titles.count <= 0) { return ; } 67 | 68 | for (UIButton *item in self.items) { 69 | 70 | [item removeFromSuperview]; 71 | } 72 | 73 | [self.items removeAllObjects]; 74 | 75 | NSInteger count = titles.count; 76 | 77 | for (int i = 0; i < count; i++) { 78 | 79 | UIButton *item = [UIButton buttonWithType:UIButtonTypeCustom]; 80 | 81 | item.tag = i; 82 | 83 | [item setTitle:titles[i] forState:UIControlStateNormal]; 84 | 85 | [item setTitle:titles[i] forState:UIControlStateSelected]; 86 | 87 | [self.items addObject:item]; 88 | 89 | [self addSubview:item]; 90 | 91 | [item addTarget:self action:@selector(clickItem:) forControlEvents:UIControlEventTouchUpInside]; 92 | } 93 | 94 | [self addSubview:self.sliderView]; 95 | 96 | [self scrollIndex:index animated:NO]; 97 | 98 | [self reloadUI]; 99 | } 100 | 101 | /// 选中索引 102 | - (void)scrollIndex:(NSInteger)index animated:(BOOL)animated { 103 | 104 | [self selectItem:index]; 105 | 106 | if (_selectIndex == index) { return ; } 107 | 108 | _selectIndex = index; 109 | 110 | if (animated) { 111 | 112 | __weak DZMSegmentedControl *weakSelf = self; 113 | 114 | [UIView animateWithDuration:0.2 animations:^{ 115 | 116 | weakSelf.sliderView.frame = CGRectMake(weakSelf.selectItem.center.x - weakSelf.sliderView.frame.size.width / 2, weakSelf.frame.size.height - weakSelf.sliderHeight + self.sliderBottom, weakSelf.sliderView.frame.size.width, weakSelf.sliderHeight); 117 | 118 | }]; 119 | 120 | }else{ 121 | 122 | self.sliderView.frame = CGRectMake(self.selectItem.center.x - self.sliderView.frame.size.width / 2, self.frame.size.height - self.sliderHeight + self.sliderBottom, self.sliderView.frame.size.width, self.sliderHeight); 123 | } 124 | 125 | if ([self.delegate respondsToSelector:@selector(segmentedControl:scrollIndex:)]) { 126 | 127 | [self.delegate segmentedControl:self scrollIndex:index]; 128 | } 129 | } 130 | 131 | /// 选中按钮 132 | - (void)selectItem:(NSInteger)index { 133 | 134 | self.selectItem.selected = NO; 135 | 136 | self.selectItem.titleLabel.font = self.normalFont; 137 | 138 | UIButton *item = self.items[index]; 139 | 140 | item.selected = YES; 141 | 142 | item.titleLabel.font = self.selectFont; 143 | 144 | self.selectItem = item; 145 | } 146 | 147 | /// 刷新当前页面布局 148 | - (void)reloadUI { 149 | 150 | NSInteger count = self.items.count; 151 | 152 | if (count <= 0) { return ; } 153 | 154 | CGFloat itemX = self.insets.left; 155 | 156 | CGFloat itemY = self.insets.top; 157 | 158 | CGFloat itemW = ((self.frame.size.width - self.insets.left - self.insets.right) - ((count - 1) * self.itemSpace)) / count; 159 | 160 | CGFloat itemH = self.frame.size.height - self.insets.top - self.insets.bottom; 161 | 162 | for (int i = 0; i < count; i++) { 163 | 164 | UIButton *item = self.items[i]; 165 | 166 | item.frame = CGRectMake(itemX + i * (itemW + self.itemSpace), itemY, itemW, itemH); 167 | 168 | if (item.isSelected) { item.titleLabel.font = self.selectFont; 169 | 170 | }else{ item.titleLabel.font = self.normalFont; } 171 | 172 | [item setTitleColor:self.normalColor forState:UIControlStateNormal]; 173 | 174 | [item setTitleColor:self.selectColor forState:UIControlStateSelected]; 175 | } 176 | 177 | CGFloat sliderWidth = (self.sliderWidth < 0) ? self.frame.size.width : self.sliderWidth; 178 | 179 | sliderWidth = MIN(sliderWidth, itemW); 180 | 181 | self.sliderView.backgroundColor = self.sliderColor; 182 | 183 | self.sliderView.frame = CGRectMake(self.selectItem.center.x - sliderWidth / 2, self.frame.size.height - self.sliderHeight + self.sliderBottom, sliderWidth, self.sliderHeight); 184 | } 185 | 186 | /// 滑动滑条 187 | - (void)scrollSlider:(UIScrollView *)scrollView { 188 | 189 | } 190 | 191 | - (void)clickItem:(UIButton *)item { 192 | 193 | if (self.selectIndex == item.tag) { return ; } 194 | 195 | [self scrollIndex:item.tag animated:YES]; 196 | 197 | if ([self.delegate respondsToSelector:@selector(segmentedControl:clickIndex:)]) { 198 | 199 | [self.delegate segmentedControl:self clickIndex:item.tag]; 200 | } 201 | } 202 | 203 | @end 204 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/other/thirdParty/FDFullscreenPopGesture/UINavigationController+FDFullscreenPopGesture.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015-2016 forkingdog ( https://github.com/forkingdog ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | /// "UINavigation+FDFullscreenPopGesture" extends UINavigationController's swipe- 26 | /// to-pop behavior in iOS 7+ by supporting fullscreen pan gesture. Instead of 27 | /// screen edge, you can now swipe from any place on the screen and the onboard 28 | /// interactive pop transition works seamlessly. 29 | /// 30 | /// Adding the implementation file of this category to your target will 31 | /// automatically patch UINavigationController with this feature. 32 | @interface UINavigationController (FDFullscreenPopGesture) 33 | 34 | /// The gesture recognizer that actually handles interactive pop. 35 | @property (nonatomic, strong, readonly) UIPanGestureRecognizer *fd_fullscreenPopGestureRecognizer; 36 | 37 | /// A view controller is able to control navigation bar's appearance by itself, 38 | /// rather than a global way, checking "fd_prefersNavigationBarHidden" property. 39 | /// Default to YES, disable it if you don't want so. 40 | @property (nonatomic, assign) BOOL fd_viewControllerBasedNavigationBarAppearanceEnabled; 41 | 42 | @end 43 | 44 | /// Allows any view controller to disable interactive pop gesture, which might 45 | /// be necessary when the view controller itself handles pan gesture in some 46 | /// cases. 47 | @interface UIViewController (FDFullscreenPopGesture) 48 | 49 | /// Whether the interactive pop gesture is disabled when contained in a navigation 50 | /// stack. 51 | @property (nonatomic, assign) BOOL fd_interactivePopDisabled; 52 | 53 | /// Indicate this view controller prefers its navigation bar hidden or not, 54 | /// checked when view controller based navigation bar's appearance is enabled. 55 | /// Default to NO, bars are more likely to show. 56 | @property (nonatomic, assign) BOOL fd_prefersNavigationBarHidden; 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/view/DZMReadContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadContentView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// contentView 宽高 12 | let DZM_READ_CONTENT_VIEW_WIDTH:CGFloat = ScreenWidth 13 | let DZM_READ_CONTENT_VIEW_HEIGHT:CGFloat = ScreenHeight 14 | 15 | @objc protocol DZMReadContentViewDelegate:NSObjectProtocol { 16 | 17 | /// 点击遮罩 18 | @objc optional func contentViewClickCover(contentView:DZMReadContentView) 19 | } 20 | 21 | class DZMReadContentView: UIView { 22 | 23 | /// 代理 24 | weak var delegate:DZMReadContentViewDelegate! 25 | 26 | /// 遮盖 27 | private var cover:UIControl! 28 | 29 | /// 是否显示遮盖 30 | private var isShowCover:Bool = false 31 | 32 | override init(frame: CGRect) { 33 | 34 | super.init(frame: frame) 35 | 36 | addSubviews() 37 | } 38 | 39 | private func addSubviews() { 40 | 41 | cover = UIControl() 42 | cover.alpha = 0 43 | cover.isUserInteractionEnabled = false 44 | cover.backgroundColor = UIColor.black.withAlphaComponent(0.7) 45 | cover.addTarget(self, action: #selector(clickCover), for: .touchUpInside) 46 | addSubview(cover) 47 | } 48 | 49 | @objc private func clickCover() { 50 | 51 | cover.isUserInteractionEnabled = false 52 | 53 | delegate?.contentViewClickCover?(contentView: self) 54 | 55 | showCover(isShow: false) 56 | } 57 | 58 | /// 遮盖展示 59 | func showCover(isShow:Bool) { 60 | 61 | if isShowCover == isShow { return } 62 | 63 | if isShow { 64 | 65 | bringSubviewToFront(cover) 66 | 67 | cover.isUserInteractionEnabled = true 68 | } 69 | 70 | isShowCover = isShow 71 | 72 | UIView.animate(withDuration: DZM_READ_AD_TIME) { [weak self] () in 73 | 74 | self?.cover.alpha = CGFloat(NSNumber(value: isShow).floatValue) 75 | } 76 | } 77 | 78 | override func layoutSubviews() { 79 | 80 | super.layoutSubviews() 81 | 82 | cover.frame = bounds 83 | } 84 | 85 | required init?(coder aDecoder: NSCoder) { 86 | 87 | fatalError("init(coder:) has not been implemented") 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/view/DZMReadLeftView/DZMReadCatalogCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadCatalogCell.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/25. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DZMReadCatalogCell: UITableViewCell { 12 | 13 | private(set) var chapterName:UILabel! 14 | 15 | private(set) var spaceLine:UIView! 16 | 17 | class func cell(_ tableView:UITableView) ->DZMReadCatalogCell { 18 | 19 | var cell = tableView.dequeueReusableCell(withIdentifier: "DZMReadCatalogCell") 20 | 21 | if cell == nil { 22 | 23 | cell = DZMReadCatalogCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "DZMReadCatalogCell") 24 | } 25 | 26 | return cell as! DZMReadCatalogCell 27 | } 28 | 29 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 30 | 31 | super.init(style: style, reuseIdentifier: reuseIdentifier) 32 | 33 | selectionStyle = .none 34 | 35 | backgroundColor = UIColor.clear 36 | 37 | addSubviews() 38 | } 39 | 40 | private func addSubviews() { 41 | 42 | chapterName = UILabel() 43 | chapterName.font = DZM_FONT_SA_14 44 | chapterName.textColor = DZM_COLOR_145_145_145 45 | contentView.addSubview(chapterName) 46 | 47 | spaceLine = SpaceLine(contentView, DZM_COLOR_230_230_230) 48 | } 49 | 50 | override func layoutSubviews() { 51 | 52 | super.layoutSubviews() 53 | 54 | let w = frame.size.width 55 | let h = frame.size.height 56 | 57 | chapterName.frame = CGRect(x: DZM_SPACE_SA_15, y: 0, width: w - DZM_SPACE_SA_30, height: h) 58 | 59 | spaceLine.frame = CGRect(x: DZM_SPACE_SA_15, y: h - DZM_SPACE_LINE, width: w - DZM_SPACE_SA_30, height: DZM_SPACE_LINE) 60 | } 61 | 62 | required init?(coder aDecoder: NSCoder) { 63 | 64 | fatalError("init(coder:) has not been implemented") 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/view/DZMReadLeftView/DZMReadCatalogView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadCatalogView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/25. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol DZMReadCatalogViewDelegate:NSObjectProtocol { 12 | 13 | /// 点击章节 14 | @objc optional func catalogViewClickChapter(catalogView:DZMReadCatalogView, chapterListModel:DZMReadChapterListModel) 15 | } 16 | 17 | class DZMReadCatalogView: UIView,UITableViewDelegate,UITableViewDataSource { 18 | 19 | /// 代理 20 | weak var delegate:DZMReadCatalogViewDelegate! 21 | 22 | /// 数据源 23 | var readModel:DZMReadModel! { 24 | 25 | didSet{ 26 | 27 | tableView.reloadData() 28 | 29 | scrollRecord() 30 | } 31 | } 32 | 33 | private(set) var tableView:DZMTableView! 34 | 35 | override init(frame: CGRect) { 36 | 37 | super.init(frame: frame) 38 | 39 | addSubviews() 40 | } 41 | 42 | private func addSubviews() { 43 | 44 | tableView = DZMTableView() 45 | tableView.delegate = self 46 | tableView.dataSource = self 47 | addSubview(tableView) 48 | } 49 | 50 | /// 滚动到阅读记录 51 | func scrollRecord() { 52 | 53 | if readModel != nil { 54 | 55 | tableView.reloadData() 56 | 57 | if !readModel.chapterListModels.isEmpty { 58 | 59 | var row = -1 60 | 61 | for (index, item) in readModel.chapterListModels.enumerated() { 62 | 63 | if (item.id == readModel.recordModel.chapterModel.id) { 64 | 65 | row = index 66 | 67 | break 68 | } 69 | } 70 | 71 | if row != -1 { 72 | 73 | tableView.scrollToRow(at: IndexPath(row: row, section: 0), at: .middle, animated: false) 74 | } 75 | } 76 | } 77 | } 78 | 79 | override func layoutSubviews() { 80 | 81 | super.layoutSubviews() 82 | 83 | tableView.frame = bounds 84 | } 85 | 86 | // MARK: UITableViewDelegate,UITableViewDataSource 87 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 88 | 89 | if readModel != nil { return readModel.chapterListModels.count } 90 | 91 | return 0 92 | } 93 | 94 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 95 | 96 | let cell = DZMReadCatalogCell.cell(tableView) 97 | 98 | // 章节 99 | let chapterListModel = readModel.chapterListModels[indexPath.row] 100 | 101 | // 章节名 102 | cell.chapterName.text = readModel.chapterListModels[indexPath.row].name 103 | 104 | // 日夜间 105 | if DZMUserDefaults.bool(DZM_READ_KEY_MODE_DAY_NIGHT) { 106 | 107 | cell.spaceLine.backgroundColor = DZM_COLOR_230_230_230.withAlphaComponent(0.1) 108 | 109 | }else{ cell.spaceLine.backgroundColor = DZM_COLOR_230_230_230 } 110 | 111 | // 阅读记录 112 | if readModel.recordModel.chapterModel.id == chapterListModel.id { 113 | 114 | cell.chapterName.textColor = DZM_READ_COLOR_MAIN 115 | 116 | }else{ cell.chapterName.textColor = DZM_COLOR_145_145_145 } 117 | 118 | return cell 119 | } 120 | 121 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 122 | 123 | return DZM_SPACE_SA_50 124 | } 125 | 126 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 127 | 128 | delegate?.catalogViewClickChapter?(catalogView: self, chapterListModel: readModel.chapterListModels[indexPath.row]) 129 | } 130 | 131 | required init?(coder aDecoder: NSCoder) { 132 | 133 | fatalError("init(coder:) has not been implemented") 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/view/DZMReadLeftView/DZMReadLeftView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadLeftView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// leftView 宽高度 12 | let DZM_READ_LEFT_VIEW_WIDTH:CGFloat = DZM_SPACE_SA_335 13 | let DZM_READ_LEFT_VIEW_HEIGHT:CGFloat = ScreenHeight 14 | 15 | /// leftView headerView 高度 16 | let DZM_READ_LEFT_HEADER_VIEW_HEIGHT:CGFloat = DZM_SPACE_SA_50 17 | 18 | class DZMReadLeftView: UIView,DZMSegmentedControlDelegate { 19 | 20 | // 功能栏 21 | private var segmentedControl:DZMSegmentedControl! 22 | 23 | // 分割线 24 | private var spaceLine:UIView! 25 | 26 | // 目录 27 | private(set) var catalogView:DZMReadCatalogView! 28 | 29 | // 书签 30 | private(set) var markView:DZMReadMarkView! 31 | 32 | override init(frame: CGRect) { 33 | 34 | super.init(frame: frame) 35 | 36 | addSubviews() 37 | } 38 | 39 | private func addSubviews() { 40 | 41 | // 工具栏 42 | segmentedControl = DZMSegmentedControl(frame: CGRect(x: 0, y: SA(isX: StatusBarHeight, 0), width: DZM_READ_LEFT_VIEW_WIDTH, height: DZM_READ_LEFT_HEADER_VIEW_HEIGHT)) 43 | segmentedControl.delegate = self 44 | segmentedControl.normalFont = DZM_FONT_SA_14 45 | segmentedControl.normalColor = DZM_COLOR_145_145_145 46 | segmentedControl.selectFont = DZM_FONT_SA_14 47 | segmentedControl.selectColor = DZM_READ_COLOR_MAIN 48 | segmentedControl.sliderColor = DZM_READ_COLOR_MAIN 49 | segmentedControl.sliderWidth = DZM_SPACE_SA_30 50 | segmentedControl.sliderHeight = DZM_SPACE_SA_2 51 | addSubview(segmentedControl) 52 | segmentedControl.reloadTitles(["目录","书签"]) 53 | 54 | // 分割线 55 | spaceLine = SpaceLine(self, DZM_COLOR_230_230_230) 56 | spaceLine.frame = CGRect(x: 0, y: segmentedControl.frame.maxY, width: DZM_READ_LEFT_VIEW_WIDTH, height: DZM_SPACE_LINE) 57 | 58 | // 目录 59 | catalogView = DZMReadCatalogView() 60 | addSubview(catalogView) 61 | catalogView.frame = CGRect(x: 0, y: spaceLine.frame.maxY, width: DZM_READ_LEFT_VIEW_WIDTH, height: DZM_READ_LEFT_VIEW_HEIGHT - spaceLine.frame.maxY) 62 | 63 | // 书签 64 | markView = DZMReadMarkView() 65 | markView.alpha = 0 66 | addSubview(markView) 67 | markView.frame = catalogView.frame 68 | 69 | // 更新当前UI 70 | updateUI() 71 | } 72 | 73 | /// 刷新UI 例如: 日夜间可以根据需求判断修改目录背景颜色,文字颜色等等 74 | func updateUI() { 75 | 76 | // 日夜间切换修改 77 | if DZMUserDefaults.bool(DZM_READ_KEY_MODE_DAY_NIGHT) { 78 | 79 | spaceLine.backgroundColor = DZM_COLOR_230_230_230.withAlphaComponent(0.2) 80 | 81 | backgroundColor = DZM_COLOR_46_46_46 82 | 83 | }else{ 84 | 85 | spaceLine.backgroundColor = DZM_COLOR_230_230_230 86 | 87 | backgroundColor = UIColor.white 88 | } 89 | 90 | // 刷新分割线颜色(如果不需要刷新分割线颜色可以去掉,目前我是做了日夜间修改分割线颜色的操作) 91 | catalogView.tableView.reloadData() 92 | markView.tableView.reloadData() 93 | } 94 | 95 | // MARK: DZMSegmentedControlDelegate 96 | func segmentedControl(_ segmentedControl: DZMSegmentedControl, click index: Int) { 97 | 98 | UIView.animate(withDuration: DZM_READ_AD_TIME) { [weak self] () in 99 | 100 | if index == 0 { // 显示目录 101 | 102 | self?.bringSubviewToFront(self!.catalogView) 103 | self?.catalogView.alpha = 1 104 | self?.markView.alpha = 0 105 | 106 | }else{ // 显示书签 107 | 108 | self?.bringSubviewToFront(self!.markView) 109 | self?.catalogView.alpha = 0 110 | self?.markView.alpha = 1 111 | } 112 | } 113 | } 114 | 115 | required init?(coder aDecoder: NSCoder) { 116 | 117 | fatalError("init(coder:) has not been implemented") 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/view/DZMReadLeftView/DZMReadMarkCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadMarkCell.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/25. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// mark cell 高度 12 | let DZM_READ_MARK_CELL_HEIGHT:CGFloat = DZM_SPACE_SA_100 13 | 14 | class DZMReadMarkCell: UITableViewCell { 15 | 16 | private var title:UILabel! 17 | 18 | private var time:UILabel! 19 | 20 | private var content:UILabel! 21 | 22 | private(set) var spaceLine:UIView! 23 | 24 | var markModel:DZMReadMarkModel! { 25 | 26 | didSet{ 27 | 28 | title.text = markModel.name 29 | 30 | time.text = TimerString(markModel.time.intValue) 31 | 32 | content.attributedText = TextLineSpacing(markModel.content, DZM_SPACE_5) 33 | } 34 | } 35 | 36 | class func cell(_ tableView:UITableView) ->DZMReadMarkCell { 37 | 38 | var cell = tableView.dequeueReusableCell(withIdentifier: "DZMReadMarkCell") 39 | 40 | if cell == nil { 41 | 42 | cell = DZMReadMarkCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "DZMReadMarkCell") 43 | } 44 | 45 | return cell as! DZMReadMarkCell 46 | } 47 | 48 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 49 | 50 | super.init(style: style, reuseIdentifier: reuseIdentifier) 51 | 52 | selectionStyle = .none 53 | 54 | backgroundColor = UIColor.clear 55 | 56 | addSubviews() 57 | } 58 | 59 | private func addSubviews() { 60 | 61 | title = UILabel() 62 | title.font = DZM_FONT_SA_14 63 | title.textColor = DZM_COLOR_145_145_145 64 | contentView.addSubview(title) 65 | 66 | time = UILabel() 67 | time.font = DZM_FONT_SA_12 68 | time.textColor = DZM_COLOR_145_145_145 69 | time.textAlignment = .right 70 | contentView.addSubview(time) 71 | 72 | content = UILabel() 73 | content.font = DZM_FONT_SA_14 74 | content.textColor = DZM_COLOR_145_145_145 75 | content.numberOfLines = 0 76 | contentView.addSubview(content) 77 | 78 | spaceLine = SpaceLine(contentView, DZM_COLOR_230_230_230) 79 | } 80 | 81 | override func layoutSubviews() { 82 | 83 | super.layoutSubviews() 84 | 85 | let w = frame.size.width 86 | let h = frame.size.height 87 | 88 | let itemX = DZM_SPACE_SA_15 89 | let itemY = DZM_SPACE_SA_15 90 | let itemH = DZM_SPACE_SA_20 91 | let contentW = w - 2*itemX 92 | let contentY = itemY + itemH + DZM_SPACE_SA_10 93 | let itemW = (contentW - DZM_SPACE_SA_10) / 2 94 | 95 | 96 | title.frame = CGRect(x: itemX, y: itemY, width: itemW, height: itemH) 97 | 98 | time.frame = CGRect(x: w - itemW - itemX, y: itemY, width: itemW, height: itemH) 99 | 100 | content.frame = CGRect(x: itemX, y: contentY, width: contentW, height: DZM_READ_MARK_CELL_HEIGHT - contentY - DZM_SPACE_SA_15) 101 | 102 | spaceLine.frame = CGRect(x: DZM_SPACE_SA_15, y: h - DZM_SPACE_LINE, width: w - DZM_SPACE_SA_30, height: DZM_SPACE_LINE) 103 | } 104 | 105 | required init?(coder aDecoder: NSCoder) { 106 | 107 | fatalError("init(coder:) has not been implemented") 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /DZMeBookRead/DZMeBookRead/view/DZMReadLeftView/DZMReadMarkView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMReadMarkView.swift 3 | // DZMeBookRead 4 | // 5 | // Created by dengzemiao on 2019/4/25. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol DZMReadMarkViewDelegate:NSObjectProtocol { 12 | 13 | /// 点击章节 14 | @objc optional func markViewClickMark(markView:DZMReadMarkView, markModel:DZMReadMarkModel) 15 | } 16 | 17 | class DZMReadMarkView: UIView,UITableViewDelegate,UITableViewDataSource { 18 | 19 | /// 代理 20 | weak var delegate:DZMReadMarkViewDelegate! 21 | 22 | /// 数据源 23 | var readModel:DZMReadModel! { 24 | 25 | didSet{ tableView.reloadData() } 26 | } 27 | 28 | private(set) var tableView:DZMTableView! 29 | 30 | override init(frame: CGRect) { 31 | 32 | super.init(frame: frame) 33 | 34 | addSubviews() 35 | } 36 | 37 | private func addSubviews() { 38 | 39 | tableView = DZMTableView() 40 | tableView.delegate = self 41 | tableView.dataSource = self 42 | addSubview(tableView) 43 | } 44 | 45 | override func layoutSubviews() { 46 | 47 | super.layoutSubviews() 48 | 49 | tableView.frame = bounds 50 | } 51 | 52 | // MARK: UITableViewDelegate,UITableViewDataSource 53 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 54 | 55 | if readModel != nil { return readModel.markModels.count } 56 | 57 | return 0 58 | } 59 | 60 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 61 | 62 | let cell = DZMReadMarkCell.cell(tableView) 63 | 64 | // 设置数据 65 | cell.markModel = readModel.markModels[indexPath.row] 66 | 67 | // 日夜间 68 | if DZMUserDefaults.bool(DZM_READ_KEY_MODE_DAY_NIGHT) { 69 | 70 | cell.spaceLine.backgroundColor = DZM_COLOR_230_230_230.withAlphaComponent(0.1) 71 | 72 | }else{ cell.spaceLine.backgroundColor = DZM_COLOR_230_230_230 } 73 | 74 | return cell 75 | } 76 | 77 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 78 | 79 | return DZM_READ_MARK_CELL_HEIGHT 80 | } 81 | 82 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 83 | 84 | delegate?.markViewClickMark?(markView: self, markModel: readModel.markModels[indexPath.row]) 85 | } 86 | 87 | func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle { 88 | 89 | return .delete 90 | } 91 | 92 | func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { 93 | 94 | _ = readModel.removeMark(index: indexPath.row) 95 | 96 | if readModel.markModels.isEmpty { tableView.reloadData() 97 | 98 | }else{ tableView.deleteRows(at: [indexPath], with: .fade) } 99 | } 100 | 101 | required init?(coder aDecoder: NSCoder) { 102 | 103 | fatalError("init(coder:) has not been implemented") 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /DZMeBookRead/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | DZMeBookRead 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.2 21 | CFBundleVersion 22 | 1.2 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarHidden 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD+DZM.h: -------------------------------------------------------------------------------- 1 | // 2 | // MBProgressHUD+DZM.h 3 | // MBProgressHUD+DZM 4 | // 5 | // Created by 邓泽淼 on 2018/5/2. 6 | // Copyright © 2018年 邓泽淼. All rights reserved. 7 | // 8 | 9 | #import "MBProgressHUD.h" 10 | 11 | @interface MBProgressHUD (DZM) 12 | 13 | #pragma MARK Success 14 | 15 | /// Success 16 | + (nonnull MBProgressHUD *)showSuccess; 17 | 18 | /// Success - 可附带:文字 19 | + (nonnull MBProgressHUD *)showSuccess:(NSString * _Nullable)message; 20 | 21 | /// Success - 可附带:文字 22 | + (nullable MBProgressHUD *)showSuccess:(NSString * _Nullable)message toView:(UIView * _Nullable)view; 23 | 24 | #pragma MARK Error 25 | 26 | /// Error 27 | + (nonnull MBProgressHUD *)showError; 28 | 29 | /// Error - 可附带:文字 30 | + (nonnull MBProgressHUD *)showError:(NSString * _Nullable)message; 31 | 32 | /// Error - 可附带:文字 33 | + (nullable MBProgressHUD *)showError:(NSString * _Nullable)message toView:(UIView * _Nullable)view; 34 | 35 | #pragma MARK Message 36 | 37 | /// Message - 可附带:文字 38 | + (nonnull MBProgressHUD *)showMessage:(NSString * _Nullable)message; 39 | 40 | /// Message - 可附带:文字 41 | + (nullable MBProgressHUD *)showMessage:(NSString * _Nullable)message toView:(UIView * _Nullable)view; 42 | 43 | /// Message - 可附带:文字+偏移调整 44 | + (nullable MBProgressHUD *)showMessage:(NSString * _Nullable)message offset:(CGPoint)offset toView:(UIView * _Nullable)view; 45 | 46 | #pragma MARK Loading 47 | 48 | /// Loading (MBProgressHUDModeIndeterminate) 49 | + (nonnull MBProgressHUD *)showLoading; 50 | 51 | /// Loading (MBProgressHUDModeIndeterminate) 52 | + (nullable MBProgressHUD *)showLoading:(UIView * _Nullable)view; 53 | 54 | /// Loading (MBProgressHUDModeIndeterminate) - 可附带:文字 55 | + (nullable MBProgressHUD *)showLoading:(NSString * _Nullable)message toView:(UIView * _Nullable)view; 56 | 57 | #pragma MARK Hide 58 | 59 | /// Hide 60 | + (void)hide; 61 | 62 | /// Hide 63 | + (void)hide:(UIView * _Nullable)view; 64 | 65 | #pragma MARK 公用HUD对象 66 | 67 | /// HUD 68 | + (nullable MBProgressHUD *)HUD:(UIView * _Nullable)view; 69 | 70 | /// HUD - 可附带:自定义View 71 | + (nullable MBProgressHUD *)HUD:(UIView * _Nullable)view customView:(UIView * _Nullable)customView; 72 | 73 | /// HUD (自动隐藏) - 可附带:文字+图片 74 | + (nullable MBProgressHUD *)HUD:(UIView * _Nullable)view text:(NSString * _Nullable)text icon:(NSString * _Nullable)icon; 75 | 76 | #pragma MARK CustomView 77 | 78 | /// CustomView - ImageView 79 | + (nonnull UIImageView *)Custom_ImageView:(NSString *)icon; 80 | 81 | /// CustomView - Right 82 | + (nonnull UIView *)Custom_Right; 83 | 84 | /// CustomView - Error 85 | + (nonnull UIView *)Custom_Error; 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD+DZM.m: -------------------------------------------------------------------------------- 1 | // 2 | // MBProgressHUD+DZM.m 3 | // MBProgressHUD+DZM 4 | // 5 | // Created by 邓泽淼 on 2018/5/2. 6 | // Copyright © 2018年 邓泽淼. All rights reserved. 7 | // 8 | 9 | /// 图片地址 10 | #define HUD_ICON_PATH(icon) [NSString stringWithFormat:@"MBProgressHUD.bundle/%@",icon] 11 | 12 | /// 隐藏延迟 13 | #define HUD_HIDE_AFTERDELAY 2.0 14 | 15 | /// 默认现实目标视图 16 | #define HUD_TO_VIEW [UIApplication sharedApplication].keyWindow 17 | 18 | #import "MBProgressHUD+DZM.h" 19 | 20 | @implementation MBProgressHUD (DZM) 21 | 22 | #pragma MARK 公用HUD对象 23 | 24 | /// HUD 25 | + (nullable MBProgressHUD *)HUD:(UIView * _Nullable)view { 26 | 27 | if (view == nil) { return nil; } 28 | 29 | MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES]; 30 | 31 | hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor; 32 | 33 | hud.contentColor = [UIColor whiteColor]; 34 | 35 | hud.bezelView.color = [[UIColor blackColor] colorWithAlphaComponent:0.6]; 36 | 37 | return hud; 38 | } 39 | 40 | /// HUD - 可附带:自定义View 41 | + (nullable MBProgressHUD *)HUD:(UIView * _Nullable)view customView:(UIView * _Nullable)customView { 42 | 43 | if (view == nil) { return nil; } 44 | 45 | MBProgressHUD *hud = [MBProgressHUD HUD:view]; 46 | 47 | hud.mode = MBProgressHUDModeCustomView; 48 | 49 | hud.customView = customView; 50 | 51 | return hud; 52 | } 53 | 54 | /// HUD (自动隐藏) - 可附带:文字+图片 55 | + (nullable MBProgressHUD *)HUD:(UIView * _Nullable)view text:(NSString * _Nullable)text icon:(NSString * _Nullable)icon { 56 | 57 | if (view == nil) { return nil; } 58 | 59 | MBProgressHUD *hud = [MBProgressHUD HUD:view customView:[MBProgressHUD Custom_ImageView:icon]]; 60 | 61 | hud.label.text = text; 62 | 63 | [hud hideAnimated:YES afterDelay:HUD_HIDE_AFTERDELAY]; 64 | 65 | return hud; 66 | } 67 | 68 | #pragma MARK CustomView 69 | 70 | /// CustomView - ImageView 71 | + (nonnull UIImageView *)Custom_ImageView:(NSString *)icon { 72 | 73 | UIImage *image = [[UIImage imageNamed:icon] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; 74 | 75 | UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; 76 | 77 | return imageView; 78 | } 79 | 80 | /// CustomView - Right 81 | + (nonnull UIView *)Custom_Right { 82 | 83 | return [MBProgressHUD Custom_ImageView:HUD_ICON_PATH(@"right")]; 84 | } 85 | 86 | /// CustomView - Error 87 | + (nonnull UIView *)Custom_Error { 88 | 89 | return [MBProgressHUD Custom_ImageView:HUD_ICON_PATH(@"error")]; 90 | } 91 | 92 | #pragma MARK Success 93 | 94 | /// Success 95 | + (nonnull MBProgressHUD *)showSuccess { 96 | 97 | return [MBProgressHUD showSuccess:nil]; 98 | } 99 | 100 | /// Success 101 | + (nonnull MBProgressHUD *)showSuccess:(NSString * _Nullable )message { 102 | 103 | return [MBProgressHUD showSuccess:message toView:HUD_TO_VIEW]; 104 | } 105 | 106 | /// Success - 可附带:文字 107 | + (nullable MBProgressHUD *)showSuccess:(NSString * _Nullable)message toView:(UIView * _Nullable)view { 108 | 109 | MBProgressHUD *hud = [MBProgressHUD HUD:view text:message icon:HUD_ICON_PATH(@"right")]; 110 | 111 | hud.userInteractionEnabled = NO; 112 | 113 | return hud; 114 | } 115 | 116 | #pragma MARK Error 117 | 118 | /// Error 119 | + (nonnull MBProgressHUD *)showError { 120 | 121 | return [MBProgressHUD showError:nil]; 122 | } 123 | 124 | /// Error 125 | + (nonnull MBProgressHUD *)showError:(NSString * _Nullable)message { 126 | 127 | return [MBProgressHUD showError:message toView:HUD_TO_VIEW]; 128 | } 129 | 130 | /// Error - 可附带:文字 131 | + (nullable MBProgressHUD *)showError:(NSString * _Nullable)message toView:(UIView * _Nullable)view { 132 | 133 | MBProgressHUD *hud = [MBProgressHUD HUD:view text:message icon:HUD_ICON_PATH(@"error")]; 134 | 135 | hud.userInteractionEnabled = NO; 136 | 137 | return hud; 138 | } 139 | 140 | #pragma MARK Message 141 | 142 | /// Message - 可附带:文字 143 | + (nonnull MBProgressHUD *)showMessage:(NSString * _Nullable)message { 144 | 145 | return [MBProgressHUD showMessage:message toView:HUD_TO_VIEW]; 146 | } 147 | 148 | /// Message - 可附带:文字 149 | + (nullable MBProgressHUD *)showMessage:(NSString * _Nullable)message toView:(UIView * _Nullable)view { 150 | 151 | return [MBProgressHUD showMessage:message offset:CGPointZero toView:view]; 152 | } 153 | 154 | /// Message - 可附带:文字+偏移调整 155 | + (nullable MBProgressHUD *)showMessage:(NSString * _Nullable)message offset:(CGPoint)offset toView:(UIView * _Nullable)view { 156 | 157 | if (view == nil) { return nil; } 158 | 159 | MBProgressHUD *hud = [MBProgressHUD HUD:view]; 160 | 161 | hud.mode = MBProgressHUDModeText; 162 | 163 | hud.label.text = message; 164 | 165 | hud.offset = offset; // 例子: CGPointMake(0.f, MBProgressMaxOffset) 166 | 167 | hud.userInteractionEnabled = NO; 168 | 169 | [hud hideAnimated:YES afterDelay:HUD_HIDE_AFTERDELAY]; 170 | 171 | return hud; 172 | } 173 | 174 | #pragma MARK Loading 175 | 176 | /// Loading (MBProgressHUDModeIndeterminate) 177 | + (nonnull MBProgressHUD *)showLoading { 178 | 179 | return [MBProgressHUD showLoading:HUD_TO_VIEW]; 180 | } 181 | 182 | /// Loading (MBProgressHUDModeIndeterminate) 183 | + (nullable MBProgressHUD *)showLoading:(UIView * _Nullable)view { 184 | 185 | return [MBProgressHUD showLoading:nil toView:view]; 186 | } 187 | 188 | /// Loading (MBProgressHUDModeIndeterminate) - 可附带:文字 189 | + (nullable MBProgressHUD *)showLoading:(NSString * _Nullable)message toView:(UIView * _Nullable)view { 190 | 191 | if (view == nil) { return nil; } 192 | 193 | MBProgressHUD *tempHUD = [MBProgressHUD HUD:view];; 194 | 195 | tempHUD.label.text = message; 196 | 197 | return tempHUD; 198 | } 199 | 200 | #pragma MARK Hide 201 | 202 | /// Hide 203 | + (void)hide { 204 | 205 | [MBProgressHUD hide:HUD_TO_VIEW]; 206 | } 207 | 208 | /// Hide 209 | + (void)hide:(UIView * _Nullable)view { 210 | 211 | if (view == nil) { return ; } 212 | 213 | [MBProgressHUD hideHUDForView:view animated:YES]; 214 | } 215 | 216 | @end 217 | -------------------------------------------------------------------------------- /DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/error.png -------------------------------------------------------------------------------- /DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/error@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/error@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/error@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/error@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/right.png -------------------------------------------------------------------------------- /DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/right@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/right@2x.png -------------------------------------------------------------------------------- /DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/right@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/MBProgressHUD+DZM/MBProgressHUD.bundle/right@3x.png -------------------------------------------------------------------------------- /DZMeBookRead/求魔.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/DZMeBookRead/求魔.txt -------------------------------------------------------------------------------- /DZMeBookReadTests/DZMeBookReadTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMeBookReadTests.swift 3 | // DZMeBookReadTests 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import DZMeBookRead 11 | 12 | class DZMeBookReadTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /DZMeBookReadTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /DZMeBookReadUITests/DZMeBookReadUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DZMeBookReadUITests.swift 3 | // DZMeBookReadUITests 4 | // 5 | // Created by dengzemiao on 2019/4/17. 6 | // Copyright © 2019年 DZM. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class DZMeBookReadUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 20 | XCUIApplication().launch() 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | func testExample() { 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /DZMeBookReadUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 dengzemiao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DZMeBookRead 2 | 3 | ![Version](https://img.shields.io/badge/Version-1.3-orange.svg) 4 | ![Swift Version](https://img.shields.io/badge/Swift-4.2-orange.svg) 5 | ![Xcode Version](https://img.shields.io/badge/Xcode-12.4-orange.svg) 6 | ![Author](https://img.shields.io/badge/Author-DZM-blue.svg) 7 | 8 | --- 9 | 10 | 技术 wx 群: dengzemiao (大前端交流群) 11 | 12 | --- 13 | 14 | #### 一、DEMO 效果(导入项目流程看 `第四步`) 15 | 16 | ![DEMO效果](gif_0.gif) 17 | 18 | --- 19 | 20 | #### 二、简介与版本记录 21 | 22 | ``` 23 | 提示: 24 | 25 | 下载不同版本可在上面的 Branch 选项中选择下载版本, master 分支为最新版本。 26 | 27 | v1.3 (Swift4.2) { 28 | 29 | 2021-12-28 更新: 支持 DZMCoreText 获取指定页(内容)获取所有断尾 NSRange 或者 CGRect,方便做断尾评论功能。 30 | 31 | 2021-11-16 更新: 修复长按选中一个段落,页头页尾无限循环问题,解决 ipad iOS15 放大镜崩溃问题。 32 | 33 | 2021-11-1 更新: 支持长按选中一个段落内容,或者选中一行内容。 34 | 35 | 2021-4-9 更新: 解决放大镜在 iOS 13 之后系统中长按不显示问题,iOS13 以后苹果增加了 SceneDelegate 来管理窗口,必须将自定义 Window 注册到 SceneDelegate 中。 36 | 37 | 2021-4-9 更新: 优化长按弹出操作菜单代码,解决隐藏操作菜单闪动问题,更新为 Xcode 12.4 38 | 39 | 2021-4-8 更新: 页尾留白问题解决,新问题:英文阅读单纯被分割,但是按单词分割,页尾则会留白,参考 DZMReadConfigure.swift 文件 242 行。 40 | 41 | 2020-11-25 更新: 感谢 @dreeye 小伙伴的帮助!!修复滚动模式滚动内容崩溃问题,之前是由于存放章节字典有线程安全问题导致章节对象中途释放出现野指针崩溃,现在已经修复,滚动模式可以正常使用了。 42 | 43 | 2020-11-25 更新:滚动模式崩溃问题:“现在有个BUG在iOS12.2以后, iOS12.2以前到没出现,我这边测试机用的X才会系列会出现,其他机型好像也不会,滚动模式 DZMReadViewScrollController -> chapterModels 字段里面章节model会提前释放,不会被强引用,很是郁闷, 低版本没有问题。拿到Demo的可以测试一下滚动模式下会不会有问题。其实就是章节Model提前释放了,但是我存放的是字典对象,理论上是强引用对象的,现在12.2却出现这样的问题” 44 | 45 | 2020-6-15 更新: 加入【平移翻页模式】 46 | } 47 | 48 | v1.2 (Swift4.2) { (同上版本) 49 | 50 | 2019-5-20 BUG: 滚动模式在iOS12版本以上会出现闪退,原因是字典里面的章节内容对象提前释放了,iOS12以下却没有问题,暂时没有更好的存储方式所以先替换为之前版本存储方式。 51 | 52 | 2019-5-17 修复: 书籍首页添加标签BUG, 第一次进入创建阅读页多次BUG。 53 | 54 | 2019-5-16 更新: 加入TXT全本快速进入阅读。 55 | 56 | 2020-3-17 由于系统 UIPageViewController 的点击左右翻页范围太小,且不可自由控制,增加自定义手势开关支持随意控制仿真模式左右翻页菜单的点击区域控制。 57 | } 58 | 59 | v1.1 (Swift4.2) { (TXT,有书籍首页) 60 | 61 | 2019-5-16 更新: 解析文本,代码细节优化。 62 | 63 | 2019-5-10 修复: 无效果快速点击BUG。 64 | 65 | 2019-5-7 更新: 加入书籍首页支持。 66 | } 67 | 68 | v1.0 (Swift4.2) { (TXT,无书籍首页) 69 | 70 | 2019-4-29 更新: 重做Demo, 升级Swift4.2, 解决遗留问题, 优化代码使用。 71 | } 72 | ``` 73 | 74 | --- 75 | 76 | #### 三、导入项目流程 77 | 78 | ![文件介绍1](icon_0.png) 79 | 80 | --- 81 | 82 | #### 四、epub 支持提示 83 | 84 | ``` 85 | DTCoreText同样也可以解析txt,epub...分页相关的功能,很方便。不需要像我这个Demo中一样复杂的解析处理。当然有喜欢研究CoreText可以参考下我的Demo。 86 | 87 | DTCoreText对于epub来说,主要功能就是能够将我们输入的HTML文件进行解析,并自动关联相对应的css样式(也帮我们解析好了),我们需要做的就是输入一个HTML文件, 88 | 89 | 他就会给我们输出带有排版样式的NSAttributedString,然后我们直接使用CoreText进行画这个NSAttributedString就可以啦! 90 | 91 | Github地址:https://github.com/Cocoanetics/DTCoreText 92 | ``` 93 | 94 | --- 95 | 96 | #### 五、项目思路理解: 97 | 98 | 1. 将一个完整的TXT或者文本解析成一章一章的章节模型,通过归档的模式进行缓存起来(归档解析速度比数据库要快)。 99 | 100 | 归档缓存方式: 101 | 102 | 通过 bookID 和 chapterID 作为文件夹以及文件名进行缓存, 103 | 104 | 这样做的话那么只需要面对 bookID 和 chapterID 就可以知道本地是否有这个章节存在以及获取阅读, 105 | 106 | 同时在网络小说的情况下,你需要通过 bookID 和 chapterID 就在任何位置,或者后台进行缓存下载章节,并同时进行流畅阅读, 107 | 108 | 只需要在阅读或者下载的时候判断下本地是否存在该章节归档文件就可以避免重复下载。 109 | 110 | 2. 创建一个 readModel, readModel 里面存放的是这本小说的公用属性, 比如阅读记录, 书签, 等等... 111 | 112 | 在阅读过程中就是起到一个针对这本小说需要公用属性以及记录属性的作用 113 | 114 | 那么怎么使用它进入阅读呢? 你只需要通过一个 bookID 获得一个 readModel 对象, 然后使用它里面修改阅读记录的方法, 115 | 116 | 将你需要阅读的章节的 chapterID 传进去修改为当前这个 readModel 的阅读记录对象即可, 你修改阅读记录的章节必须存在, 117 | 118 | 也就是说你是网络小说就要先将要阅读的章节下载到本地归档好, 本地小说就先解析一章到本地归档好, 在使用 readModel 进行修改阅读记录, 119 | 120 | 然后传给控制器就可以进行阅读了。 121 | 122 | 3. readModel里面的 chapterListModels 是可有可无的, 阅读过程中不依赖章节列表, 也就是说你可以先设置章节列表, 也可以删除不使用, 123 | 124 | 也可以在 DZMRMLeftView 里面去单独请求这个章节列表的数据, 它的作用只是用于手动选章节的使用得到一个 chapterID 进行加载并缓存阅读而已, 125 | 126 | 一般 chapterModel 里面就已经带好了当前章节ID以及上下章章节ID。 127 | 128 | 4. 网络小说使用: 129 | 130 | 1).进入阅读页的时候获取一个你要阅读的 chapterID, 这个 chapterID 的章节内容需要存在本地归档文件里面, 并通过 readModel 修改为阅读记录对象,就可以传入控制器进行阅读了。 131 | 132 | 2).然后在上下翻页里面根据 chapterID 判断是否有归档文件, 没有就下周并缓存, 修改为阅读记录进行继续阅读, 如果不知道在哪里修改, 全局搜索 "网络小说操作提示", 可以看注释。 133 | 134 | 3).边下载边看这个操作就可以看上面 1. 的提示了。 135 | 136 | 4).章节归档处理的 增删改查 都已经封装, 可以直接使用 在 DZMKeyedArchiver.swift 文件中下面。 137 | 138 | --- 139 | 140 | #### 六、附带信息 141 | 142 | - [OC 如何集成](https://github.com/dengzemiao/OCDZMeBookRead) 143 | 144 | - 小说相关库或文章 145 | 146 | - [放大镜](https://github.com/dengzemiao/DZMMagnifierView) 147 | 148 | - [阅读打开书籍效果](https://github.com/dengzemiao/DZMAnimatedTransitioning) 149 | 150 | - [小说《覆盖效果》](https://github.com/dengzemiao/DZMCoverAnimation) 151 | 152 | - [UIPageViewController 翻页背景颜色修改](https://juejin.cn/post/6877454461199974408) 153 | 154 | - 上架 APP 155 | 156 | - [简阅](https://apps.apple.com/cn/app/id1494994480) 157 | 158 | - [搜书大师](https://apps.apple.com/cn/app/id1523194349) 159 | 160 | - [笔趣阁](https://apps.apple.com/cn/app/id1367152987) 161 | 162 | - 抖音上很多小说 APP 都有使用或借鉴到本 DEMO 163 | 164 | - DEMO 老版本 165 | 166 | ``` 167 | 2019-4-29 (停止更新) 168 | Swift4.0 - https://github.com/dengzemiao/DZMeBookRead-Swift4.0 169 | 170 | 2018-9-6 (停止更新) 171 | Swift3.x - https://github.com/dengzemiao/DZMeBookRead-Swift3.x 172 | ``` 173 | 174 | - [阅读打开书籍效果](https://github.com/dengzemiao/DZMAnimatedTransitioning) 175 | 176 | ![阅读打开书籍效果](gif_1.gif) 177 | -------------------------------------------------------------------------------- /gif_0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/gif_0.gif -------------------------------------------------------------------------------- /gif_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/gif_1.gif -------------------------------------------------------------------------------- /icon_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dengzemiao/DZMeBookRead/d27d122a38469f7e7ff7adb6632d439c2428d1af/icon_0.png --------------------------------------------------------------------------------