├── .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 | 
4 | 
5 | 
6 | 
7 |
8 | ---
9 |
10 | 技术 wx 群: dengzemiao (大前端交流群)
11 |
12 | ---
13 |
14 | #### 一、DEMO 效果(导入项目流程看 `第四步`)
15 |
16 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------