├── Demo └── LearnCoreText │ ├── LearnCoreText.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── LearnCoreText │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── abc.imageset │ │ ├── Contents.json │ │ └── abc.png │ ├── first.imageset │ │ ├── Contents.json │ │ └── first.pdf │ └── second.imageset │ │ ├── Contents.json │ │ └── second.pdf │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── FirstViewController.h │ ├── FirstViewController.m │ ├── Info.plist │ ├── SecondViewController.h │ ├── SecondViewController.m │ ├── UIView+LYLayout.h │ ├── UIView+LYLayout.m │ ├── abc.png │ └── main.m ├── PageEffect └── LearnCoreText │ ├── LearnCoreText.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── LearnCoreText │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── BackViewController.h │ ├── BackViewController.m │ ├── Base.lproj │ └── Main.storyboard │ ├── FirstViewController.h │ ├── FirstViewController.m │ ├── PageCurlViewController.h │ ├── PageCurlViewController.m │ ├── PanShiftViewController.h │ ├── PanShiftViewController.m │ ├── PreHeader.h │ ├── SSScrollViewController.h │ ├── SSScrollViewController.m │ ├── SSSlideViewController.h │ ├── SSSlideViewController.m │ ├── SecondViewController.h │ ├── SecondViewController.m │ ├── UIGestureRecognizer+SSUtil.h │ ├── UIGestureRecognizer+SSUtil.m │ ├── UIView+LYLayout.h │ ├── UIView+LYLayout.m │ ├── abc.png │ ├── main.m │ └── res │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── abc.imageset │ │ ├── Contents.json │ │ └── abc.png │ ├── first.imageset │ │ ├── Contents.json │ │ └── first.pdf │ └── second.imageset │ │ ├── Contents.json │ │ └── second.pdf │ ├── Base.lproj │ └── LaunchScreen.storyboard │ └── Info.plist └── Reader ├── LearnCoreText.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── LearnCoreText ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── BookshelfViewController.h ├── BookshelfViewController.m ├── ContentViewController.h ├── ContentViewController.m ├── DataModel │ ├── data │ │ ├── SSAdvertisingData.h │ │ ├── SSAdvertisingData.m │ │ ├── SSChapterData.h │ │ └── SSChapterData.m │ ├── layout │ │ ├── SSLayoutChapterData.h │ │ ├── SSLayoutChapterData.m │ │ ├── SSLayoutPageData.h │ │ └── SSLayoutPageData.m │ └── reading │ │ ├── SSConfigData.h │ │ ├── SSConfigData.m │ │ ├── SSPageControllData.h │ │ ├── SSPageControllData.m │ │ ├── SSReadingContextData.h │ │ └── SSReadingContextData.m ├── Info.plist ├── Manager │ ├── SSDataManager.h │ ├── SSDataManager.m │ ├── SSLayoutManager.h │ ├── SSLayoutManager.m │ ├── SSPageControllManager.h │ ├── SSPageControllManager.m │ ├── SSTransformManager.h │ └── SSTransformManager.m ├── SSTabBarViewController.h ├── SSTabBarViewController.m ├── View │ ├── SSPageView.h │ └── SSPageView.m ├── ViewController │ ├── SSAdViewController.h │ ├── SSAdViewController.m │ ├── SSBackViewController.h │ ├── SSBackViewController.m │ ├── SSBasePageViewController.h │ ├── SSBasePageViewController.m │ ├── SSContentViewController.h │ ├── SSContentViewController.m │ ├── SSLoadingViewController.h │ ├── SSLoadingViewController.m │ ├── SSPageViewController.h │ └── SSPageViewController.m ├── main.m ├── support │ ├── PrefixHeader.pch │ ├── SSCommonMacro.h │ └── panda.png └── vender │ └── view │ ├── UIView+LYLayout.h │ └── UIView+LYLayout.m ├── LearnCoreTextTests ├── Info.plist └── LearnCoreTextTests.m └── LearnCoreTextUITests ├── Info.plist └── LearnCoreTextUITests.m /Demo/LearnCoreText/LearnCoreText.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FDD1C61F22268BEE00B1FBA1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FDD1C61E22268BEE00B1FBA1 /* AppDelegate.m */; }; 11 | FDD1C62222268BEE00B1FBA1 /* FirstViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FDD1C62122268BEE00B1FBA1 /* FirstViewController.m */; }; 12 | FDD1C62522268BEE00B1FBA1 /* SecondViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FDD1C62422268BEE00B1FBA1 /* SecondViewController.m */; }; 13 | FDD1C62822268BEE00B1FBA1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FDD1C62622268BEE00B1FBA1 /* Main.storyboard */; }; 14 | FDD1C62A22268BF000B1FBA1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FDD1C62922268BF000B1FBA1 /* Assets.xcassets */; }; 15 | FDD1C62D22268BF000B1FBA1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FDD1C62B22268BF000B1FBA1 /* LaunchScreen.storyboard */; }; 16 | FDD1C63022268BF000B1FBA1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FDD1C62F22268BF000B1FBA1 /* main.m */; }; 17 | FDD1C63822268EA500B1FBA1 /* UIView+LYLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = FDD1C63722268EA500B1FBA1 /* UIView+LYLayout.m */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | FDD1C61A22268BEE00B1FBA1 /* LearnCoreText.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LearnCoreText.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | FDD1C61D22268BEE00B1FBA1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 23 | FDD1C61E22268BEE00B1FBA1 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 24 | FDD1C62022268BEE00B1FBA1 /* FirstViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FirstViewController.h; sourceTree = ""; }; 25 | FDD1C62122268BEE00B1FBA1 /* FirstViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FirstViewController.m; sourceTree = ""; }; 26 | FDD1C62322268BEE00B1FBA1 /* SecondViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecondViewController.h; sourceTree = ""; }; 27 | FDD1C62422268BEE00B1FBA1 /* SecondViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SecondViewController.m; sourceTree = ""; }; 28 | FDD1C62722268BEE00B1FBA1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 29 | FDD1C62922268BF000B1FBA1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 30 | FDD1C62C22268BF000B1FBA1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 31 | FDD1C62E22268BF000B1FBA1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 32 | FDD1C62F22268BF000B1FBA1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 33 | FDD1C63622268EA500B1FBA1 /* UIView+LYLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+LYLayout.h"; sourceTree = ""; }; 34 | FDD1C63722268EA500B1FBA1 /* UIView+LYLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+LYLayout.m"; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | FDD1C61722268BEE00B1FBA1 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | FDD1C61122268BEE00B1FBA1 = { 49 | isa = PBXGroup; 50 | children = ( 51 | FDD1C61C22268BEE00B1FBA1 /* LearnCoreText */, 52 | FDD1C61B22268BEE00B1FBA1 /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | FDD1C61B22268BEE00B1FBA1 /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | FDD1C61A22268BEE00B1FBA1 /* LearnCoreText.app */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | FDD1C61C22268BEE00B1FBA1 /* LearnCoreText */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | FDD1C63622268EA500B1FBA1 /* UIView+LYLayout.h */, 68 | FDD1C63722268EA500B1FBA1 /* UIView+LYLayout.m */, 69 | FDD1C61D22268BEE00B1FBA1 /* AppDelegate.h */, 70 | FDD1C61E22268BEE00B1FBA1 /* AppDelegate.m */, 71 | FDD1C62022268BEE00B1FBA1 /* FirstViewController.h */, 72 | FDD1C62122268BEE00B1FBA1 /* FirstViewController.m */, 73 | FDD1C62322268BEE00B1FBA1 /* SecondViewController.h */, 74 | FDD1C62422268BEE00B1FBA1 /* SecondViewController.m */, 75 | FDD1C62622268BEE00B1FBA1 /* Main.storyboard */, 76 | FDD1C62922268BF000B1FBA1 /* Assets.xcassets */, 77 | FDD1C62B22268BF000B1FBA1 /* LaunchScreen.storyboard */, 78 | FDD1C62E22268BF000B1FBA1 /* Info.plist */, 79 | FDD1C62F22268BF000B1FBA1 /* main.m */, 80 | ); 81 | path = LearnCoreText; 82 | sourceTree = ""; 83 | }; 84 | /* End PBXGroup section */ 85 | 86 | /* Begin PBXNativeTarget section */ 87 | FDD1C61922268BEE00B1FBA1 /* LearnCoreText */ = { 88 | isa = PBXNativeTarget; 89 | buildConfigurationList = FDD1C63322268BF000B1FBA1 /* Build configuration list for PBXNativeTarget "LearnCoreText" */; 90 | buildPhases = ( 91 | FDD1C61622268BEE00B1FBA1 /* Sources */, 92 | FDD1C61722268BEE00B1FBA1 /* Frameworks */, 93 | FDD1C61822268BEE00B1FBA1 /* Resources */, 94 | ); 95 | buildRules = ( 96 | ); 97 | dependencies = ( 98 | ); 99 | name = LearnCoreText; 100 | productName = LearnCoreText; 101 | productReference = FDD1C61A22268BEE00B1FBA1 /* LearnCoreText.app */; 102 | productType = "com.apple.product-type.application"; 103 | }; 104 | /* End PBXNativeTarget section */ 105 | 106 | /* Begin PBXProject section */ 107 | FDD1C61222268BEE00B1FBA1 /* Project object */ = { 108 | isa = PBXProject; 109 | attributes = { 110 | LastUpgradeCheck = 1010; 111 | ORGANIZATIONNAME = Loying; 112 | TargetAttributes = { 113 | FDD1C61922268BEE00B1FBA1 = { 114 | CreatedOnToolsVersion = 10.1; 115 | }; 116 | }; 117 | }; 118 | buildConfigurationList = FDD1C61522268BEE00B1FBA1 /* Build configuration list for PBXProject "LearnCoreText" */; 119 | compatibilityVersion = "Xcode 9.3"; 120 | developmentRegion = en; 121 | hasScannedForEncodings = 0; 122 | knownRegions = ( 123 | en, 124 | Base, 125 | ); 126 | mainGroup = FDD1C61122268BEE00B1FBA1; 127 | productRefGroup = FDD1C61B22268BEE00B1FBA1 /* Products */; 128 | projectDirPath = ""; 129 | projectRoot = ""; 130 | targets = ( 131 | FDD1C61922268BEE00B1FBA1 /* LearnCoreText */, 132 | ); 133 | }; 134 | /* End PBXProject section */ 135 | 136 | /* Begin PBXResourcesBuildPhase section */ 137 | FDD1C61822268BEE00B1FBA1 /* Resources */ = { 138 | isa = PBXResourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | FDD1C62D22268BF000B1FBA1 /* LaunchScreen.storyboard in Resources */, 142 | FDD1C62A22268BF000B1FBA1 /* Assets.xcassets in Resources */, 143 | FDD1C62822268BEE00B1FBA1 /* Main.storyboard in Resources */, 144 | ); 145 | runOnlyForDeploymentPostprocessing = 0; 146 | }; 147 | /* End PBXResourcesBuildPhase section */ 148 | 149 | /* Begin PBXSourcesBuildPhase section */ 150 | FDD1C61622268BEE00B1FBA1 /* Sources */ = { 151 | isa = PBXSourcesBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | FDD1C62522268BEE00B1FBA1 /* SecondViewController.m in Sources */, 155 | FDD1C61F22268BEE00B1FBA1 /* AppDelegate.m in Sources */, 156 | FDD1C63822268EA500B1FBA1 /* UIView+LYLayout.m in Sources */, 157 | FDD1C63022268BF000B1FBA1 /* main.m in Sources */, 158 | FDD1C62222268BEE00B1FBA1 /* FirstViewController.m in Sources */, 159 | ); 160 | runOnlyForDeploymentPostprocessing = 0; 161 | }; 162 | /* End PBXSourcesBuildPhase section */ 163 | 164 | /* Begin PBXVariantGroup section */ 165 | FDD1C62622268BEE00B1FBA1 /* Main.storyboard */ = { 166 | isa = PBXVariantGroup; 167 | children = ( 168 | FDD1C62722268BEE00B1FBA1 /* Base */, 169 | ); 170 | name = Main.storyboard; 171 | sourceTree = ""; 172 | }; 173 | FDD1C62B22268BF000B1FBA1 /* LaunchScreen.storyboard */ = { 174 | isa = PBXVariantGroup; 175 | children = ( 176 | FDD1C62C22268BF000B1FBA1 /* Base */, 177 | ); 178 | name = LaunchScreen.storyboard; 179 | sourceTree = ""; 180 | }; 181 | /* End PBXVariantGroup section */ 182 | 183 | /* Begin XCBuildConfiguration section */ 184 | FDD1C63122268BF000B1FBA1 /* Debug */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | CLANG_ANALYZER_NONNULL = YES; 189 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 190 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 191 | CLANG_CXX_LIBRARY = "libc++"; 192 | CLANG_ENABLE_MODULES = YES; 193 | CLANG_ENABLE_OBJC_ARC = YES; 194 | CLANG_ENABLE_OBJC_WEAK = YES; 195 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 196 | CLANG_WARN_BOOL_CONVERSION = YES; 197 | CLANG_WARN_COMMA = YES; 198 | CLANG_WARN_CONSTANT_CONVERSION = YES; 199 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 201 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 202 | CLANG_WARN_EMPTY_BODY = YES; 203 | CLANG_WARN_ENUM_CONVERSION = YES; 204 | CLANG_WARN_INFINITE_RECURSION = YES; 205 | CLANG_WARN_INT_CONVERSION = YES; 206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 208 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 211 | CLANG_WARN_STRICT_PROTOTYPES = YES; 212 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 213 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 214 | CLANG_WARN_UNREACHABLE_CODE = YES; 215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 216 | CODE_SIGN_IDENTITY = "iPhone Developer"; 217 | COPY_PHASE_STRIP = NO; 218 | DEBUG_INFORMATION_FORMAT = dwarf; 219 | ENABLE_STRICT_OBJC_MSGSEND = YES; 220 | ENABLE_TESTABILITY = YES; 221 | GCC_C_LANGUAGE_STANDARD = gnu11; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_NO_COMMON_BLOCKS = YES; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "DEBUG=1", 227 | "$(inherited)", 228 | ); 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 236 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 237 | MTL_FAST_MATH = YES; 238 | ONLY_ACTIVE_ARCH = YES; 239 | SDKROOT = iphoneos; 240 | }; 241 | name = Debug; 242 | }; 243 | FDD1C63222268BF000B1FBA1 /* Release */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_ANALYZER_NONNULL = YES; 248 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 249 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 250 | CLANG_CXX_LIBRARY = "libc++"; 251 | CLANG_ENABLE_MODULES = YES; 252 | CLANG_ENABLE_OBJC_ARC = YES; 253 | CLANG_ENABLE_OBJC_WEAK = YES; 254 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 255 | CLANG_WARN_BOOL_CONVERSION = YES; 256 | CLANG_WARN_COMMA = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 259 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 260 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 261 | CLANG_WARN_EMPTY_BODY = YES; 262 | CLANG_WARN_ENUM_CONVERSION = YES; 263 | CLANG_WARN_INFINITE_RECURSION = YES; 264 | CLANG_WARN_INT_CONVERSION = YES; 265 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 266 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 269 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 270 | CLANG_WARN_STRICT_PROTOTYPES = YES; 271 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 272 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 273 | CLANG_WARN_UNREACHABLE_CODE = YES; 274 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 275 | CODE_SIGN_IDENTITY = "iPhone Developer"; 276 | COPY_PHASE_STRIP = NO; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | ENABLE_NS_ASSERTIONS = NO; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | GCC_C_LANGUAGE_STANDARD = gnu11; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 289 | MTL_ENABLE_DEBUG_INFO = NO; 290 | MTL_FAST_MATH = YES; 291 | SDKROOT = iphoneos; 292 | VALIDATE_PRODUCT = YES; 293 | }; 294 | name = Release; 295 | }; 296 | FDD1C63422268BF000B1FBA1 /* Debug */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 300 | CODE_SIGN_STYLE = Automatic; 301 | DEVELOPMENT_TEAM = A478ENRUX2; 302 | INFOPLIST_FILE = LearnCoreText/Info.plist; 303 | LD_RUNPATH_SEARCH_PATHS = ( 304 | "$(inherited)", 305 | "@executable_path/Frameworks", 306 | ); 307 | PRODUCT_BUNDLE_IDENTIFIER = com.loying.lin.LearnCoreText; 308 | PRODUCT_NAME = "$(TARGET_NAME)"; 309 | TARGETED_DEVICE_FAMILY = "1,2"; 310 | }; 311 | name = Debug; 312 | }; 313 | FDD1C63522268BF000B1FBA1 /* Release */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 317 | CODE_SIGN_STYLE = Automatic; 318 | DEVELOPMENT_TEAM = A478ENRUX2; 319 | INFOPLIST_FILE = LearnCoreText/Info.plist; 320 | LD_RUNPATH_SEARCH_PATHS = ( 321 | "$(inherited)", 322 | "@executable_path/Frameworks", 323 | ); 324 | PRODUCT_BUNDLE_IDENTIFIER = com.loying.lin.LearnCoreText; 325 | PRODUCT_NAME = "$(TARGET_NAME)"; 326 | TARGETED_DEVICE_FAMILY = "1,2"; 327 | }; 328 | name = Release; 329 | }; 330 | /* End XCBuildConfiguration section */ 331 | 332 | /* Begin XCConfigurationList section */ 333 | FDD1C61522268BEE00B1FBA1 /* Build configuration list for PBXProject "LearnCoreText" */ = { 334 | isa = XCConfigurationList; 335 | buildConfigurations = ( 336 | FDD1C63122268BF000B1FBA1 /* Debug */, 337 | FDD1C63222268BF000B1FBA1 /* Release */, 338 | ); 339 | defaultConfigurationIsVisible = 0; 340 | defaultConfigurationName = Release; 341 | }; 342 | FDD1C63322268BF000B1FBA1 /* Build configuration list for PBXNativeTarget "LearnCoreText" */ = { 343 | isa = XCConfigurationList; 344 | buildConfigurations = ( 345 | FDD1C63422268BF000B1FBA1 /* Debug */, 346 | FDD1C63522268BF000B1FBA1 /* Release */, 347 | ); 348 | defaultConfigurationIsVisible = 0; 349 | defaultConfigurationName = Release; 350 | }; 351 | /* End XCConfigurationList section */ 352 | }; 353 | rootObject = FDD1C61222268BEE00B1FBA1 /* Project object */; 354 | } 355 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/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 | } -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/Assets.xcassets/abc.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "abc.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 | } -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/Assets.xcassets/abc.imageset/abc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/Demo/LearnCoreText/LearnCoreText/Assets.xcassets/abc.imageset/abc.png -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/Assets.xcassets/first.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/Assets.xcassets/first.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/Demo/LearnCoreText/LearnCoreText/Assets.xcassets/first.imageset/first.pdf -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/Assets.xcassets/second.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/Assets.xcassets/second.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/Demo/LearnCoreText/LearnCoreText/Assets.xcassets/second.imageset/second.pdf -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/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 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/Base.lproj/Main.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 | 33 | 34 | 35 | 36 | 37 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/FirstViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FirstViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarTintParameters 32 | 33 | UINavigationBar 34 | 35 | Style 36 | UIBarStyleDefault 37 | Translucent 38 | 39 | 40 | 41 | UISupportedInterfaceOrientations 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | UISupportedInterfaceOrientations~ipad 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationPortraitUpsideDown 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/SecondViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SecondViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/SecondViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "SecondViewController.h" 10 | 11 | @interface SecondViewController () 12 | 13 | @end 14 | 15 | @implementation SecondViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/UIView+LYLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LYLayout.h 3 | // LYFrameworkDemo 4 | // 5 | // Created by loyinglin on 2018/10/25. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (LYLayout) 12 | 13 | @property (nonatomic) CGFloat left; ///< Shortcut for frame.origin.x. 14 | @property (nonatomic) CGFloat top; ///< Shortcut for frame.origin.y 15 | @property (nonatomic) CGFloat right; ///< Shortcut for frame.origin.x + frame.size.width 16 | @property (nonatomic) CGFloat bottom; ///< Shortcut for frame.origin.y + frame.size.height 17 | @property (nonatomic) CGFloat width; ///< Shortcut for frame.size.width. 18 | @property (nonatomic) CGFloat height; ///< Shortcut for frame.size.height. 19 | @property (nonatomic) CGFloat centerX; ///< Shortcut for center.x 20 | @property (nonatomic) CGFloat centerY; ///< Shortcut for center.y 21 | @property (nonatomic) CGPoint origin; ///< Shortcut for frame.origin. 22 | @property (nonatomic) CGSize size; ///< Shortcut for frame.size. 23 | 24 | @end 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/UIView+LYLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LYLayout.m 3 | // LYFrameworkDemo 4 | // 5 | // Created by loyinglin on 2018/10/25. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "UIView+LYLayout.h" 10 | 11 | @implementation UIView (LYLayout) 12 | 13 | - (CGFloat)left { 14 | 15 | return self.frame.origin.x; 16 | } 17 | 18 | - (void)setLeft:(CGFloat)x { 19 | 20 | CGRect frame = self.frame; 21 | frame.origin.x = x; 22 | self.frame = frame; 23 | } 24 | 25 | - (CGFloat)top { 26 | 27 | return self.frame.origin.y; 28 | } 29 | 30 | - (void)setTop:(CGFloat)y { 31 | 32 | CGRect frame = self.frame; 33 | frame.origin.y = y; 34 | self.frame = frame; 35 | } 36 | 37 | - (CGFloat)right { 38 | 39 | return self.frame.origin.x + self.frame.size.width; 40 | } 41 | 42 | - (void)setRight:(CGFloat)right { 43 | CGRect frame = self.frame; 44 | frame.origin.x = right - frame.size.width; 45 | self.frame = frame; 46 | } 47 | 48 | - (CGFloat)bottom { 49 | return self.frame.origin.y + self.frame.size.height; 50 | } 51 | 52 | - (void)setBottom:(CGFloat)bottom { 53 | CGRect frame = self.frame; 54 | frame.origin.y = bottom - frame.size.height; 55 | self.frame = frame; 56 | } 57 | 58 | - (CGFloat)width { 59 | return self.frame.size.width; 60 | } 61 | 62 | - (void)setWidth:(CGFloat)width { 63 | CGRect frame = self.frame; 64 | frame.size.width = width; 65 | self.frame = frame; 66 | } 67 | 68 | - (CGFloat)height { 69 | return self.frame.size.height; 70 | } 71 | 72 | - (void)setHeight:(CGFloat)height { 73 | CGRect frame = self.frame; 74 | frame.size.height = height; 75 | self.frame = frame; 76 | } 77 | 78 | - (CGFloat)centerX { 79 | 80 | return self.center.x; 81 | } 82 | 83 | - (void)setCenterX:(CGFloat)centerX { 84 | 85 | self.center = CGPointMake(centerX, self.center.y); 86 | } 87 | 88 | - (CGFloat)centerY { 89 | 90 | return self.center.y; 91 | } 92 | 93 | - (void)setCenterY:(CGFloat)centerY { 94 | self.center = CGPointMake(self.center.x, centerY); 95 | } 96 | 97 | - (CGPoint)origin { 98 | 99 | return self.frame.origin; 100 | } 101 | 102 | - (void)setOrigin:(CGPoint)origin { 103 | CGRect frame = self.frame; 104 | frame.origin = origin; 105 | self.frame = frame; 106 | } 107 | 108 | - (CGSize)size { 109 | 110 | return self.frame.size; 111 | } 112 | 113 | - (void)setSize:(CGSize)size { 114 | 115 | CGRect frame = self.frame; 116 | frame.size = size; 117 | self.frame = frame; 118 | } 119 | 120 | @end 121 | 122 | -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/abc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/Demo/LearnCoreText/LearnCoreText/abc.png -------------------------------------------------------------------------------- /Demo/LearnCoreText/LearnCoreText/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/BackViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BackViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/26. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface BackViewController : UIViewController 14 | 15 | - (void)updateWithViewController:(UIViewController *)viewController; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/BackViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BackViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/26. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "BackViewController.h" 10 | #import 11 | 12 | @interface BackViewController () 13 | 14 | @property (nonatomic, strong) UIImageView *imageView; 15 | @property (nonatomic, strong) UIImage *backgroundImage; 16 | 17 | @end 18 | 19 | @implementation BackViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | // Do any additional setup after loading the view. 24 | self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; 25 | self.imageView.image = self.backgroundImage; 26 | [self.view addSubview:self.imageView]; 27 | } 28 | 29 | - (void)updateWithViewController:(UIViewController *)viewController { 30 | self.backgroundImage = [self captureView:viewController.view]; 31 | } 32 | 33 | - (BOOL)checkNullRect:(UIView *)view { 34 | BOOL ret = CGRectIsNull(view.frame); 35 | for (UIView *subView in view.subviews) { 36 | ret = ret || [self checkNullRect:subView]; 37 | } 38 | return ret; 39 | } 40 | 41 | - (UIImage *)captureView:(UIView *)view { 42 | if ([self checkNullRect:view]) { 43 | return nil; 44 | } 45 | CGRect rect = view.bounds; 46 | UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0f); 47 | CGContextRef context = UIGraphicsGetCurrentContext(); 48 | 49 | CGAffineTransform transform = CGAffineTransformMake(-1.0, 0.0, 0.0, 1.0, rect.size.width, 0.0); 50 | CGContextConcatCTM(context,transform); 51 | [view.layer renderInContext:context]; 52 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 53 | UIGraphicsEndImageContext(); 54 | return image; 55 | } 56 | 57 | - (void)didReceiveMemoryWarning { 58 | [super didReceiveMemoryWarning]; 59 | // Dispose of any resources that can be recreated. 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/Base.lproj/Main.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 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 50 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 129 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/FirstViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface FirstViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/FirstViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "FirstViewController.h" 10 | #import "UIView+LYLayout.h" 11 | #import 12 | #import "SSSlideViewController.h" 13 | 14 | @interface FirstViewController () 15 | 16 | @end 17 | 18 | @implementation FirstViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | 24 | SSSlideViewController *vc = [[SSSlideViewController alloc] init]; 25 | vc.delegate = self; 26 | [self addChildViewController:vc]; 27 | [vc setInitVC:[self getRandomVCWithIndex:5]]; 28 | [self.view addSubview:vc.view]; 29 | 30 | } 31 | 32 | -(void)viewWillDisappear:(BOOL)animated{ 33 | [super viewWillDisappear:animated]; 34 | } 35 | 36 | - (void)viewWillAppear:(BOOL)animated { 37 | [super viewWillAppear:animated]; 38 | } 39 | 40 | - (UIViewController *)slideViewControllerGetLastVC:(SSSlideViewController *)scrollVC { 41 | UIViewController *ret; 42 | UIViewController *vc = [scrollVC.viewControllers firstObject]; 43 | if (vc) { 44 | NSInteger index = vc.view.tag; 45 | if (index > 0) { 46 | ret = [self getRandomVCWithIndex:index - 1]; 47 | } 48 | } 49 | return ret; 50 | } 51 | 52 | - (UIViewController *)slideViewControllerGetNextVC:(SSSlideViewController *)scrollVC { 53 | UIViewController *ret; 54 | UIViewController *vc = [scrollVC.viewControllers lastObject]; 55 | if (vc) { 56 | NSInteger index = vc.view.tag; 57 | if (index < 10) { 58 | ret = [self getRandomVCWithIndex:index + 1]; 59 | } 60 | } 61 | return ret; 62 | } 63 | 64 | - (void)slideViewController:(SSSlideViewController *)scrollVC willTransitionToViewControllers:(UIViewController *)pendingViewController { 65 | NSLog(@"info, willTransitionToViewControllers"); 66 | } 67 | 68 | - (void)slideViewController:(SSSlideViewController *)scrollVC previousViewController:(UIViewController *)previousViewController transitionCompleted:(BOOL)completed { 69 | NSLog(@"info, transitionCompleted:%d", completed); 70 | } 71 | 72 | - (UIViewController *)getRandomVCWithIndex:(NSInteger)index { 73 | UIViewController *vc = [[UIViewController alloc] init]; 74 | vc.view.backgroundColor = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 75 | green:arc4random_uniform(255) / 255.0 76 | blue:arc4random_uniform(255) / 255.0 77 | alpha:1]; 78 | vc.view.tag = index; 79 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; 80 | label.text = [NSString stringWithFormat:@"页数%ld", (long)index]; 81 | label.textColor = [UIColor blackColor]; 82 | [vc.view addSubview:label]; 83 | 84 | return vc; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/PageCurlViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PageCurlViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/26. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface PageCurlViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/PageCurlViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PageCurlViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/26. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "PageCurlViewController.h" 10 | #import "BackViewController.h" 11 | 12 | @interface PageCurlViewController () 13 | 14 | @property (nonatomic, strong) UIPageViewController *pageVC; 15 | 16 | @end 17 | 18 | @implementation PageCurlViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | 24 | self.pageVC = [[UIPageViewController alloc] 25 | initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl 26 | navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal 27 | options: 28 | @{ 29 | UIPageViewControllerOptionSpineLocationKey:@(UIPageViewControllerSpineLocationMin) 30 | }]; 31 | self.pageVC.delegate = self; 32 | self.pageVC.dataSource = self; 33 | self.pageVC.doubleSided = YES; 34 | [self addChildViewController:self.pageVC]; 35 | [self.view addSubview:self.pageVC.view]; 36 | 37 | [self customInitFirstPage]; 38 | } 39 | 40 | - (void)customInitFirstPage { 41 | UIViewController *vc = [self getRandomVCWithIndex:5]; 42 | NSArray *arr; 43 | arr = @[vc]; 44 | [self.pageVC setViewControllers:arr 45 | direction:UIPageViewControllerNavigationDirectionReverse 46 | animated:YES 47 | completion:^(BOOL finished) { 48 | }]; 49 | } 50 | 51 | 52 | - (void)manualChangePage { 53 | UIViewController *vc = [self getRandomVCWithIndex:5]; 54 | NSArray *arr; 55 | if (self.pageVC.doubleSided) { 56 | BackViewController *backVC = [[BackViewController alloc] init]; 57 | [backVC updateWithViewController:vc]; 58 | backVC.view.tag = vc.view.tag; 59 | arr = @[vc, backVC]; 60 | } 61 | else { 62 | arr = @[vc]; 63 | } 64 | [self.pageVC setViewControllers:arr 65 | direction:UIPageViewControllerNavigationDirectionReverse 66 | animated:YES 67 | completion:^(BOOL finished) { 68 | }]; 69 | } 70 | 71 | 72 | -(void)viewWillDisappear:(BOOL)animated{ 73 | [super viewWillDisappear:animated]; 74 | } 75 | 76 | - (void)viewWillAppear:(BOOL)animated { 77 | [super viewWillAppear:animated]; 78 | } 79 | 80 | 81 | #pragma mark - UIPageViewControllerDelegate 82 | 83 | - (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { 84 | UIViewController *ret; 85 | UIViewController *vc = viewController; // 注意这里不是pageViewController.viewControllers 86 | if (vc) { 87 | NSInteger index = vc.view.tag; 88 | if (index > 0) { 89 | if ([vc isKindOfClass:BackViewController.class]) { 90 | ret = [self getRandomVCWithIndex:index - 1]; 91 | } 92 | else { 93 | BackViewController *backVC = [[BackViewController alloc] init]; 94 | [backVC updateWithViewController:vc]; 95 | backVC.view.tag = vc.view.tag; 96 | ret = backVC; 97 | } 98 | } 99 | } 100 | return ret; 101 | } 102 | 103 | - (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { 104 | UIViewController *ret; 105 | UIViewController *vc = viewController; 106 | if (vc) { 107 | NSInteger index = vc.view.tag; 108 | if (index < 10) { 109 | if ([vc isKindOfClass:BackViewController.class]) { 110 | ret = [self getRandomVCWithIndex:index + 1]; 111 | } 112 | else { 113 | BackViewController *backVC = [[BackViewController alloc] init]; 114 | [backVC updateWithViewController:vc]; 115 | backVC.view.tag = vc.view.tag; 116 | ret = backVC; 117 | } 118 | } 119 | } 120 | return ret; 121 | } 122 | 123 | 124 | - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers { 125 | // do something 126 | } 127 | 128 | 129 | - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { 130 | // do something 131 | } 132 | 133 | - (UIViewController *)getRandomVCWithIndex:(NSInteger)index { 134 | UIViewController *vc = [[UIViewController alloc] init]; 135 | vc.view.backgroundColor = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 136 | green:arc4random_uniform(255) / 255.0 137 | blue:arc4random_uniform(255) / 255.0 138 | alpha:1]; 139 | vc.view.tag = index; 140 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(200, 100, 100, 100)]; 141 | label.text = [NSString stringWithFormat:@"页数%ld", (long)index]; 142 | label.textColor = [UIColor blackColor]; 143 | [vc.view addSubview:label]; 144 | 145 | return vc; 146 | } 147 | 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/PanShiftViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PanShiftViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/26. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface PanShiftViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/PanShiftViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PanShiftViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/26. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "PanShiftViewController.h" 10 | 11 | 12 | @interface PanShiftViewController () 13 | 14 | @property (nonatomic, strong) UIPageViewController *pageVC; 15 | 16 | @end 17 | 18 | @implementation PanShiftViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | 24 | self.pageVC = [[UIPageViewController alloc] 25 | initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll 26 | navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal 27 | options: 28 | @{ 29 | UIPageViewControllerOptionSpineLocationKey:@(UIPageViewControllerSpineLocationMin) 30 | }]; 31 | self.pageVC.delegate = self; 32 | self.pageVC.dataSource = self; 33 | [self addChildViewController:self.pageVC]; 34 | [self.view addSubview:self.pageVC.view]; 35 | 36 | [self customInitFirstPage]; 37 | } 38 | 39 | - (void)customInitFirstPage { 40 | UIViewController *vc = [self getRandomVCWithIndex:5]; 41 | 42 | [self.pageVC setViewControllers:@[vc] 43 | direction:UIPageViewControllerNavigationDirectionReverse 44 | animated:NO 45 | completion:^(BOOL finished) { 46 | }]; 47 | } 48 | 49 | -(void)viewWillDisappear:(BOOL)animated{ 50 | [super viewWillDisappear:animated]; 51 | } 52 | 53 | - (void)viewWillAppear:(BOOL)animated { 54 | [super viewWillAppear:animated]; 55 | } 56 | 57 | 58 | #pragma mark - UIPageViewControllerDelegate 59 | 60 | - (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { 61 | UIViewController *ret; 62 | UIViewController *vc = viewController; 63 | if (vc) { 64 | NSInteger index = vc.view.tag; 65 | if (index > 0) { 66 | ret = [self getRandomVCWithIndex:index - 1]; 67 | } 68 | } 69 | return ret; 70 | } 71 | 72 | - (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { 73 | UIViewController *ret; 74 | UIViewController *vc = viewController; 75 | if (vc) { 76 | NSInteger index = vc.view.tag; 77 | if (index < 10) { 78 | ret = [self getRandomVCWithIndex:index + 1]; 79 | } 80 | } 81 | return ret; 82 | } 83 | 84 | 85 | - (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers { 86 | // do something 87 | } 88 | 89 | 90 | - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { 91 | // do something 92 | } 93 | 94 | - (UIViewController *)getRandomVCWithIndex:(NSInteger)index { 95 | UIViewController *vc = [[UIViewController alloc] init]; 96 | vc.view.backgroundColor = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 97 | green:arc4random_uniform(255) / 255.0 98 | blue:arc4random_uniform(255) / 255.0 99 | alpha:1]; 100 | vc.view.tag = index; 101 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; 102 | label.text = [NSString stringWithFormat:@"页数%ld", (long)index]; 103 | label.textColor = [UIColor blackColor]; 104 | [vc.view addSubview:label]; 105 | 106 | return vc; 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/PreHeader.h: -------------------------------------------------------------------------------- 1 | // 2 | // PreHeader.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/21. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #ifndef PreHeader_h 10 | #define PreHeader_h 11 | 12 | #define SSLOG_INFO NSLog 13 | 14 | #endif /* PreHeader_h */ 15 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/SSScrollViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSScorllViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/18. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class SSScrollViewController; 12 | @protocol SSScrollViewControllerDelegate 13 | 14 | - (UIViewController *)scrollViewControllerGetLastVC:(SSScrollViewController *)scrollVC; 15 | 16 | - (UIViewController *)scrollViewControllerGetNextVC:(SSScrollViewController *)scrollVC; 17 | 18 | - (void)scrollViewController:(SSScrollViewController *)scrollVC currentShowViewController:(UIViewController *)viewController; 19 | 20 | @end 21 | 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface SSScrollViewController : UIViewController 26 | 27 | 28 | /** 29 | 是否对首屏内容进行填充,保证满屏显示;default是NO 30 | */ 31 | @property (nonatomic, assign) BOOL shouldFullFill; 32 | 33 | @property (nonatomic, weak) id delegate; 34 | 35 | @property(readonly, nonatomic) NSArray<__kindof UIViewController *> *viewControllers; 36 | 37 | - (void)setInitVC:(UIViewController *)vc; 38 | 39 | @end 40 | 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/SSScrollViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSScorllViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/18. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "SSScrollViewController.h" 10 | #import "UIView+LYLayout.h" 11 | #import "UIGestureRecognizer+SSUtil.h" 12 | 13 | @interface SSScrollViewController () 14 | 15 | @property (nonatomic, strong) UIScrollView *scrollView; 16 | @property (nonatomic, strong) NSMutableArray *vcArr; 17 | @property (nonatomic, weak) UIViewController *currentVC; 18 | 19 | @end 20 | 21 | @implementation SSScrollViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | self.vcArr = [NSMutableArray array]; 27 | // Do any additional setup after loading the view. 28 | 29 | self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; 30 | self.scrollView.showsVerticalScrollIndicator = NO; 31 | self.scrollView.delegate = self; 32 | self.scrollView.contentSize = CGSizeMake(self.view.width, self.view.height * 3); 33 | [self.view addSubview:self.scrollView]; 34 | 35 | if (@available(iOS 11.0, *)) { 36 | self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; 37 | } 38 | else{ 39 | self.automaticallyAdjustsScrollViewInsets = NO; 40 | } 41 | } 42 | 43 | #pragma mark - init 44 | 45 | #pragma mark - getter&setter 46 | 47 | - (void)setInitVC:(UIViewController *)vc { 48 | [self resetStatus]; 49 | 50 | [self addChildViewController:vc]; 51 | [self.vcArr addObject:vc]; 52 | vc.view.top = self.scrollView.contentOffset.y; 53 | [self.scrollView addSubview:vc.view]; 54 | if (self.shouldFullFill) { 55 | [self fullFillContent]; 56 | } 57 | self.currentVC = vc; 58 | } 59 | 60 | - (void)fullFillContent { 61 | CGFloat downFillY; 62 | if (self.viewControllers && self.viewControllers.count > 0) { 63 | UIViewController *vc = [self.viewControllers lastObject]; 64 | downFillY = vc.view.bottom; 65 | } 66 | else { 67 | downFillY = self.scrollView.contentOffset.y; 68 | } 69 | while (downFillY < self.scrollView.contentOffset.y + self.scrollView.height) { 70 | if (!self.delegate) { 71 | NSLog(@"error, empty delegate"); 72 | break; 73 | } 74 | UIViewController *vc = [self.delegate scrollViewControllerGetNextVC:self]; 75 | if (!vc) { 76 | NSLog(@"info, reach next end"); 77 | break; 78 | } 79 | 80 | [self.vcArr addObject:vc]; 81 | [self addChildViewController:vc]; 82 | [self.scrollView addSubview:vc.view]; 83 | vc.view.top = downFillY; 84 | downFillY = vc.view.bottom; 85 | NSLog(@"info, add next vc, frame:%@", NSStringFromCGRect(vc.view.frame)); 86 | } 87 | } 88 | 89 | - (NSArray *)viewControllers { 90 | return self.vcArr; 91 | } 92 | 93 | - (void)resetStatus { 94 | for (int i = 0; i < self.vcArr.count; ++i) { 95 | UIViewController *vc = [self.vcArr lastObject]; 96 | [vc removeFromParentViewController]; 97 | [vc.view removeFromSuperview]; 98 | } 99 | [self.vcArr removeAllObjects]; 100 | [self safeSetContentOffsetY:self.scrollView.height]; 101 | } 102 | #pragma mark - action 103 | 104 | - (void)updateAllViewsWithOffset:(CGFloat)offset { 105 | SSLOG_INFO(@"updateAllViewsWithOffset, offset:%f", offset); 106 | for (int i = 0; i < self.vcArr.count; ++i) { 107 | UIViewController *vc = [self.vcArr objectAtIndex:i]; 108 | vc.view.top += offset; 109 | } 110 | SSLOG_INFO(@"current offset:%@", NSStringFromCGPoint(self.scrollView.contentOffset)); 111 | [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, self.scrollView.contentOffset.y + offset)]; 112 | } 113 | 114 | - (CGFloat)getVCMaxY { 115 | CGFloat ret = 0; 116 | if (self.viewControllers.count > 0) { 117 | ret = [self.viewControllers lastObject].view.bottom; 118 | } 119 | return ret; 120 | } 121 | 122 | - (CGFloat)getVCMinY { 123 | CGFloat ret = 0; 124 | if (self.viewControllers.count > 0) { 125 | ret = [self.viewControllers firstObject].view.top; 126 | } 127 | return ret; 128 | } 129 | 130 | #pragma mark - delegate 131 | 132 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 133 | if (scrollView.contentOffset.y < [self getVCMinY]) { // 上面没vc了 134 | if (self.delegate) { 135 | UIViewController *vc = [self.delegate scrollViewControllerGetLastVC:self]; 136 | if (vc) { 137 | [self addChildViewController:vc]; 138 | vc.view.bottom = [self getVCMinY]; 139 | [self.scrollView addSubview:vc.view]; 140 | [self.vcArr insertObject:vc atIndex:0]; 141 | } 142 | } 143 | if (scrollView.contentOffset.y < [self getVCMinY]) { 144 | [self safeSetContentOffsetY:[self getVCMinY]]; 145 | [scrollView.panGestureRecognizer cancelCurrentGestureReccongizing]; 146 | } 147 | } 148 | else if (scrollView.contentOffset.y + scrollView.height > [self getVCMaxY]) { 149 | if (self.delegate) { 150 | UIViewController *vc = [self.delegate scrollViewControllerGetNextVC:self]; 151 | if (vc) { 152 | [self addChildViewController:vc]; 153 | [self.scrollView addSubview:vc.view]; 154 | vc.view.top = [self getVCMaxY]; 155 | [self.vcArr addObject:vc]; 156 | } 157 | } 158 | if (scrollView.contentOffset.y + scrollView.height > [self getVCMaxY]) { 159 | [self safeSetContentOffsetY:[self getVCMaxY] - scrollView.height]; 160 | } 161 | } 162 | 163 | // check top 164 | if (self.vcArr && self.vcArr.count > 0) { 165 | UIViewController *vc = [self.vcArr firstObject]; 166 | if (vc.view.bottom < scrollView.contentOffset.y) { // 看不见 167 | SSLOG_INFO(@"remove firstVC"); 168 | [self.vcArr removeObject:vc]; 169 | [vc removeFromParentViewController]; 170 | [vc.view removeFromSuperview]; 171 | [self updateAllViewsWithOffset:-vc.view.height]; 172 | } 173 | } 174 | // 175 | // check bottom 176 | if (self.vcArr && self.vcArr.count > 0) { 177 | UIViewController *vc = [self.vcArr lastObject]; 178 | if (vc.view.top > scrollView.contentOffset.y + scrollView.height) { // 179 | SSLOG_INFO(@"remove lastVC"); 180 | [self.vcArr removeObject:vc]; 181 | [vc removeFromParentViewController]; 182 | [vc.view removeFromSuperview]; 183 | [self updateAllViewsWithOffset:vc.view.height]; 184 | } 185 | } 186 | 187 | [self checkCurrentVC]; 188 | } 189 | 190 | - (void)checkCurrentVC { 191 | for (int i = 0; i < self.vcArr.count; ++i) { 192 | if (self.vcArr[i].view.bottom > self.scrollView.contentOffset.y + self.scrollView.height / 2) { 193 | if (self.vcArr[i] != self.currentVC) { 194 | self.currentVC = self.vcArr[i]; 195 | if (self.delegate) { 196 | [self.delegate scrollViewController:self currentShowViewController:self.currentVC]; 197 | } 198 | } 199 | break; 200 | } 201 | } 202 | } 203 | 204 | - (void)safeSetContentOffsetY:(CGFloat)y { 205 | self.scrollView.delegate = nil; 206 | [self.scrollView setContentOffset:CGPointMake(self.scrollView.contentOffset.x, y) animated:NO]; 207 | self.scrollView.delegate = self; 208 | } 209 | #pragma mark - private 210 | 211 | @end 212 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/SSSlideViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageScrollViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/15. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class SSSlideViewController; 12 | @protocol SSSlideViewControllerDelegate 13 | 14 | - (UIViewController *)slideViewControllerGetLastVC:(SSSlideViewController *)scrollVC; 15 | 16 | - (UIViewController *)slideViewControllerGetNextVC:(SSSlideViewController *)scrollVC; 17 | 18 | - (void)slideViewController:(SSSlideViewController *)scrollVC previousViewController:(UIViewController *)previousViewController transitionCompleted:(BOOL)completed; 19 | 20 | - (void)slideViewController:(SSSlideViewController *)scrollVC willTransitionToViewControllers:(UIViewController *)pendingViewController; 21 | 22 | @end 23 | 24 | 25 | NS_ASSUME_NONNULL_BEGIN 26 | 27 | @interface SSSlideViewController : UIViewController 28 | 29 | @property (nonatomic, weak) id delegate; 30 | 31 | @property (readonly, nonatomic) NSArray<__kindof UIViewController *> *viewControllers; 32 | 33 | - (void)setInitVC:(UIViewController *)vc; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/SSSlideViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageScrollViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/3/15. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "SSSlideViewController.h" 10 | #import "UIView+LYLayout.h" 11 | #import "UIGestureRecognizer+SSUtil.h" 12 | 13 | typedef NS_ENUM(NSUInteger, SSReaderPageEffectViewStatus) { 14 | SSReaderPageEffectViewStatusDefault, // 没有页面移动 15 | SSReaderPageEffectViewStatusMovingToNextPage, // 移动旧的页面,则是翻下一页 16 | SSReaderPageEffectViewStatusMovingToLastPage, // 移动新的界面,则是返回上一页 17 | }; 18 | 19 | @interface SSSlideViewController () 20 | 21 | @property (nonatomic, assign) SSReaderPageEffectViewStatus currentStatus; 22 | @property (nonatomic, strong) UIViewController *moveVC; 23 | @property (nonatomic, strong) UIViewController *showVC; 24 | 25 | 26 | @property (nonatomic, strong) UIPanGestureRecognizer *panRecognizer; 27 | 28 | @end 29 | 30 | @implementation SSSlideViewController 31 | 32 | - (void)viewDidLoad { 33 | [super viewDidLoad]; 34 | // Do any additional setup after loading the view. 35 | [self customInit]; 36 | } 37 | 38 | 39 | - (void)customInit { 40 | UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPanGes:)]; 41 | [self.view addGestureRecognizer:panRecognizer]; 42 | panRecognizer.delegate = self; 43 | self.panRecognizer = panRecognizer; 44 | } 45 | 46 | #pragma mark - getter&setter 47 | 48 | - (void)setInitVC:(UIViewController *)vc { 49 | [self resetStatus]; 50 | if (vc) { 51 | [self addChildViewController:vc]; 52 | [self.view addSubview:vc.view]; 53 | self.showVC = vc; 54 | } 55 | else { 56 | SSLOG_INFO(@"error, set empty initVC"); 57 | } 58 | } 59 | 60 | - (void)resetStatus { 61 | if (self.showVC) { 62 | [self.showVC removeFromParentViewController]; 63 | [self.showVC.view removeFromSuperview]; 64 | } 65 | if (self.moveVC) { 66 | [self.moveVC removeFromParentViewController]; 67 | [self.moveVC.view removeFromSuperview]; 68 | } 69 | self.currentStatus = SSReaderPageEffectViewStatusDefault; 70 | } 71 | #pragma mark - action 72 | 73 | #pragma mark - delegate 74 | 75 | #pragma mark - private 76 | 77 | #define kCompleteRate (0.33) 78 | 79 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { 80 | if (gestureRecognizer == self.panRecognizer) { 81 | return self.currentStatus == SSReaderPageEffectViewStatusDefault; 82 | } 83 | return YES; 84 | } 85 | 86 | - (void)onPanGes:(UIPanGestureRecognizer *)rec { 87 | if (!self.showVC) { 88 | SSLOG_INFO(@"error, no current show vc"); 89 | return ; 90 | } 91 | CGPoint point = [rec translationInView:self.view]; 92 | static CGPoint startPoint; 93 | //手势开始 94 | if (rec.state == UIGestureRecognizerStateBegan) { 95 | startPoint = point; 96 | } 97 | CGFloat dis = (point.x - startPoint.x); 98 | if (self.currentStatus == SSReaderPageEffectViewStatusMovingToNextPage) { 99 | dis = MIN(dis, 0); 100 | } 101 | else if (self.currentStatus == SSReaderPageEffectViewStatusMovingToLastPage) { 102 | dis = MAX(dis, 0); 103 | } 104 | CGFloat rate = fabs(dis / (self.view.width * 0.5)); 105 | if (rate <= 0) { 106 | rate = 0; 107 | } 108 | else if (rate > 1) { 109 | rate = 1; 110 | } 111 | 112 | //手势进行 113 | if (rec.state == UIGestureRecognizerStateChanged) { 114 | if (self.currentStatus == SSReaderPageEffectViewStatusDefault) { // 用户开始移动,此时判断是左移还是右移 115 | if (point.x >= startPoint.x) { // 右移 116 | self.currentStatus = SSReaderPageEffectViewStatusMovingToLastPage; 117 | } 118 | else { 119 | self.currentStatus = SSReaderPageEffectViewStatusMovingToNextPage; 120 | } 121 | if (self.delegate) { 122 | if (self.currentStatus == SSReaderPageEffectViewStatusMovingToLastPage) { 123 | UIViewController *lastVC = [self.delegate slideViewControllerGetLastVC:self]; 124 | if (!lastVC) { 125 | [rec cancelCurrentGestureReccongizing]; 126 | self.currentStatus = SSReaderPageEffectViewStatusDefault; 127 | SSLOG_INFO(@"info, reach last end"); 128 | } 129 | else { 130 | [self addChildViewController:lastVC]; 131 | [self.view insertSubview:lastVC.view aboveSubview:self.showVC.view]; 132 | self.moveVC = lastVC; 133 | [self addMaskToVC:self.moveVC]; 134 | } 135 | } 136 | else if (self.currentStatus == SSReaderPageEffectViewStatusMovingToNextPage) { 137 | UIViewController *nextVC = [self.delegate slideViewControllerGetNextVC:self]; 138 | if (!nextVC) { 139 | [rec cancelCurrentGestureReccongizing]; 140 | self.currentStatus = SSReaderPageEffectViewStatusDefault; 141 | SSLOG_INFO(@"info, reach next end"); 142 | } 143 | else { 144 | [self addChildViewController:nextVC]; 145 | [self.view insertSubview:nextVC.view belowSubview:self.showVC.view]; 146 | self.moveVC = self.showVC; 147 | self.showVC = nextVC; 148 | [self addMaskToVC:self.moveVC]; 149 | } 150 | } 151 | 152 | if (self.currentStatus == SSReaderPageEffectViewStatusMovingToLastPage) { 153 | [self.delegate slideViewController:self willTransitionToViewControllers:self.moveVC]; 154 | } 155 | else if (self.currentStatus == SSReaderPageEffectViewStatusMovingToNextPage) { 156 | [self.delegate slideViewController:self willTransitionToViewControllers:self.showVC]; 157 | } 158 | } 159 | } 160 | if (self.currentStatus == SSReaderPageEffectViewStatusMovingToNextPage) { 161 | self.moveVC.view.right = self.view.width * (1 - rate); 162 | } 163 | else if (self.currentStatus == SSReaderPageEffectViewStatusMovingToLastPage) { 164 | self.moveVC.view.right = self.view.width * rate; 165 | } 166 | } 167 | //手势结束 168 | else if (rec.state == UIGestureRecognizerStateEnded || rec.state == UIGestureRecognizerStateCancelled) { 169 | SSLOG_INFO(@"info, gesture end with status:%lu", (unsigned long)self.currentStatus); 170 | CGPoint speed = [rec velocityInView:rec.view]; 171 | rate = (rate >= kCompleteRate || fabs(speed.x) > 200) ? 1 : 0; // 经验数值,多次尝试得出 172 | 173 | if (self.currentStatus == SSReaderPageEffectViewStatusMovingToLastPage) { 174 | if (self.moveVC) { 175 | [UIView animateWithDuration:0.2 animations:^{ 176 | self.moveVC.view.right = self.view.width * rate; 177 | } completion:^(BOOL finished) { 178 | if (self.delegate) { 179 | [self.delegate slideViewController:self previousViewController:self.showVC transitionCompleted:rate == 1]; 180 | } 181 | if (rate == 1) { 182 | [self.showVC removeFromParentViewController]; 183 | [self.showVC.view removeFromSuperview]; 184 | self.showVC = self.moveVC; 185 | } 186 | else { 187 | [self.moveVC removeFromParentViewController]; 188 | [self.moveVC.view removeFromSuperview]; 189 | } 190 | self.moveVC = nil; 191 | 192 | self.currentStatus = SSReaderPageEffectViewStatusDefault; 193 | }]; 194 | } 195 | } 196 | if (self.currentStatus == SSReaderPageEffectViewStatusMovingToNextPage) { 197 | if (self.moveVC) { 198 | [UIView animateWithDuration:0.2 animations:^{ 199 | self.moveVC.view.right = self.view.width * (1 - rate); 200 | } completion:^(BOOL finished) { 201 | if (self.delegate) { 202 | [self.delegate slideViewController:self previousViewController:self.moveVC transitionCompleted:rate == 1]; 203 | } 204 | if (rate == 1) { 205 | [self.moveVC removeFromParentViewController]; 206 | [self.moveVC.view removeFromSuperview]; 207 | } 208 | else { 209 | [self.showVC removeFromParentViewController]; 210 | [self.showVC.view removeFromSuperview]; 211 | self.showVC = self.moveVC; 212 | } 213 | self.moveVC = nil; 214 | 215 | self.currentStatus = SSReaderPageEffectViewStatusDefault; 216 | }]; 217 | } 218 | } 219 | else { 220 | SSLOG_INFO(@"status error"); 221 | } 222 | } 223 | } 224 | 225 | - (void)addMaskToVC:(UIViewController *)vc { 226 | vc.view.layer.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.8].CGColor; 227 | vc.view.layer.shadowOffset = CGSizeMake(5, 5); 228 | vc.view.layer.shadowOpacity = 0.8; 229 | vc.view.layer.shadowRadius = 6; 230 | } 231 | 232 | - (NSArray *)viewControllers { 233 | return self.childViewControllers; 234 | } 235 | 236 | - (void)onGestureEnd { 237 | 238 | } 239 | @end 240 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/SecondViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SecondViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/SecondViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import "SecondViewController.h" 10 | #import "SSScrollViewController.h" 11 | #import "UIView+LYLayout.h" 12 | 13 | @interface SecondViewController () 14 | 15 | @end 16 | 17 | @implementation SecondViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | 23 | SSScrollViewController *vc = [[SSScrollViewController alloc] init]; 24 | vc.shouldFullFill = YES; 25 | vc.delegate = self; 26 | [self addChildViewController:vc]; 27 | [self.view addSubview:vc.view]; 28 | 29 | [vc setInitVC:[self getRandomVCWithIndex:5]]; 30 | } 31 | 32 | 33 | 34 | - (UIViewController *)scrollViewControllerGetLastVC:(SSScrollViewController *)scrollVC { 35 | UIViewController *ret; 36 | UIViewController *vc = [scrollVC.viewControllers firstObject]; 37 | if (vc) { 38 | NSInteger index = vc.view.tag; 39 | if (index > 0) { 40 | ret = [self getRandomVCWithIndex:index - 1]; 41 | } 42 | } 43 | return ret; 44 | } 45 | 46 | - (UIViewController *)scrollViewControllerGetNextVC:(SSScrollViewController *)scrollVC { 47 | UIViewController *ret; 48 | UIViewController *vc = [scrollVC.viewControllers lastObject]; 49 | if (vc) { 50 | NSInteger index = vc.view.tag; 51 | if (index < 10) { 52 | ret = [self getRandomVCWithIndex:index + 1]; 53 | } 54 | } 55 | return ret; 56 | } 57 | 58 | - (void)scrollViewController:(SSScrollViewController *)scrollVC currentShowViewController:(UIViewController *)viewController { 59 | NSLog(@"currentShowViewController:%ld", (long) viewController.view.tag); 60 | } 61 | 62 | - (UIViewController *)getRandomVCWithIndex:(NSInteger)index { 63 | UIViewController *vc = [[UIViewController alloc] init]; 64 | vc.view.backgroundColor = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 65 | green:arc4random_uniform(255) / 255.0 66 | blue:arc4random_uniform(255) / 255.0 67 | alpha:1]; 68 | // vc.view.height = 333; 69 | vc.view.tag = index; 70 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; 71 | label.text = [NSString stringWithFormat:@"页数%ld", (long)index]; 72 | label.textColor = [UIColor blackColor]; 73 | [vc.view addSubview:label]; 74 | 75 | return vc; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/UIGestureRecognizer+SSUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+SSUtil.h 3 | // TTReading 4 | // 5 | // Created by loyinglin on 2019/3/17. 6 | // Copyright © 2019 ByteDance. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface UIGestureRecognizer (SSUtil) 14 | 15 | - (void)cancelCurrentGestureReccongizing; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/UIGestureRecognizer+SSUtil.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIGestureRecognizer+SSUtil.m 3 | // TTReading 4 | // 5 | // Created by loyinglin on 2019/3/17. 6 | // Copyright © 2019 ByteDance. All rights reserved. 7 | // 8 | 9 | #import "UIGestureRecognizer+SSUtil.h" 10 | 11 | @implementation UIGestureRecognizer (SSUtil) 12 | 13 | - (void)cancelCurrentGestureReccongizing { 14 | // disabled gesture recognizers will not receive touches. when changed to NO the gesture recognizer will be cancelled if it's currently recognizing a gesture 15 | self.enabled = NO; 16 | self.enabled = YES; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/UIView+LYLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LYLayout.h 3 | // LYFrameworkDemo 4 | // 5 | // Created by loyinglin on 2018/10/25. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (LYLayout) 12 | 13 | @property (nonatomic) CGFloat left; ///< Shortcut for frame.origin.x. 14 | @property (nonatomic) CGFloat top; ///< Shortcut for frame.origin.y 15 | @property (nonatomic) CGFloat right; ///< Shortcut for frame.origin.x + frame.size.width 16 | @property (nonatomic) CGFloat bottom; ///< Shortcut for frame.origin.y + frame.size.height 17 | @property (nonatomic) CGFloat width; ///< Shortcut for frame.size.width. 18 | @property (nonatomic) CGFloat height; ///< Shortcut for frame.size.height. 19 | @property (nonatomic) CGFloat centerX; ///< Shortcut for center.x 20 | @property (nonatomic) CGFloat centerY; ///< Shortcut for center.y 21 | @property (nonatomic) CGPoint origin; ///< Shortcut for frame.origin. 22 | @property (nonatomic) CGSize size; ///< Shortcut for frame.size. 23 | 24 | @end 25 | 26 | 27 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/UIView+LYLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LYLayout.m 3 | // LYFrameworkDemo 4 | // 5 | // Created by loyinglin on 2018/10/25. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "UIView+LYLayout.h" 10 | 11 | @implementation UIView (LYLayout) 12 | 13 | - (CGFloat)left { 14 | 15 | return self.frame.origin.x; 16 | } 17 | 18 | - (void)setLeft:(CGFloat)x { 19 | 20 | CGRect frame = self.frame; 21 | frame.origin.x = x; 22 | self.frame = frame; 23 | } 24 | 25 | - (CGFloat)top { 26 | 27 | return self.frame.origin.y; 28 | } 29 | 30 | - (void)setTop:(CGFloat)y { 31 | 32 | CGRect frame = self.frame; 33 | frame.origin.y = y; 34 | self.frame = frame; 35 | } 36 | 37 | - (CGFloat)right { 38 | 39 | return self.frame.origin.x + self.frame.size.width; 40 | } 41 | 42 | - (void)setRight:(CGFloat)right { 43 | CGRect frame = self.frame; 44 | frame.origin.x = right - frame.size.width; 45 | self.frame = frame; 46 | } 47 | 48 | - (CGFloat)bottom { 49 | return self.frame.origin.y + self.frame.size.height; 50 | } 51 | 52 | - (void)setBottom:(CGFloat)bottom { 53 | CGRect frame = self.frame; 54 | frame.origin.y = bottom - frame.size.height; 55 | self.frame = frame; 56 | } 57 | 58 | - (CGFloat)width { 59 | return self.frame.size.width; 60 | } 61 | 62 | - (void)setWidth:(CGFloat)width { 63 | CGRect frame = self.frame; 64 | frame.size.width = width; 65 | self.frame = frame; 66 | } 67 | 68 | - (CGFloat)height { 69 | return self.frame.size.height; 70 | } 71 | 72 | - (void)setHeight:(CGFloat)height { 73 | CGRect frame = self.frame; 74 | frame.size.height = height; 75 | self.frame = frame; 76 | } 77 | 78 | - (CGFloat)centerX { 79 | 80 | return self.center.x; 81 | } 82 | 83 | - (void)setCenterX:(CGFloat)centerX { 84 | 85 | self.center = CGPointMake(centerX, self.center.y); 86 | } 87 | 88 | - (CGFloat)centerY { 89 | 90 | return self.center.y; 91 | } 92 | 93 | - (void)setCenterY:(CGFloat)centerY { 94 | self.center = CGPointMake(self.center.x, centerY); 95 | } 96 | 97 | - (CGPoint)origin { 98 | 99 | return self.frame.origin; 100 | } 101 | 102 | - (void)setOrigin:(CGPoint)origin { 103 | CGRect frame = self.frame; 104 | frame.origin = origin; 105 | self.frame = frame; 106 | } 107 | 108 | - (CGSize)size { 109 | 110 | return self.frame.size; 111 | } 112 | 113 | - (void)setSize:(CGSize)size { 114 | 115 | CGRect frame = self.frame; 116 | frame.size = size; 117 | self.frame = frame; 118 | } 119 | 120 | @end 121 | 122 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/abc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/PageEffect/LearnCoreText/LearnCoreText/abc.png -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2019/2/27. 6 | // Copyright © 2019 Loying. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/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 | } -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/abc.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "abc.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 | } -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/abc.imageset/abc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/abc.imageset/abc.png -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/first.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/first.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/first.imageset/first.pdf -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/second.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/second.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/PageEffect/LearnCoreText/LearnCoreText/res/Assets.xcassets/second.imageset/second.pdf -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/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 | -------------------------------------------------------------------------------- /PageEffect/LearnCoreText/LearnCoreText/res/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarTintParameters 32 | 33 | UINavigationBar 34 | 35 | Style 36 | UIBarStyleDefault 37 | Translucent 38 | 39 | 40 | 41 | UISupportedInterfaceOrientations 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | UISupportedInterfaceOrientations~ipad 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationPortraitUpsideDown 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Reader/LearnCoreText.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/23. 6 | // Copyright © 2018年 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (nonatomic, strong) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/23. 6 | // Copyright © 2018年 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/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 | } -------------------------------------------------------------------------------- /Reader/LearnCoreText/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Reader/LearnCoreText/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 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Base.lproj/Main.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 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/BookshelfViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BookshelfViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/26. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface BookshelfViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/BookshelfViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BookshelfViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/26. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "BookshelfViewController.h" 10 | 11 | @interface BookshelfViewController () 12 | 13 | @end 14 | 15 | @implementation BookshelfViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view. 20 | } 21 | 22 | - (void)viewWillAppear:(BOOL)animated { 23 | [super viewWillAppear:animated]; 24 | 25 | self.tabBarController.tabBar.hidden = NO; 26 | } 27 | /* 28 | #pragma mark - Navigation 29 | 30 | // In a storyboard-based application, you will often want to do a little preparation before navigation 31 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 32 | // Get the new view controller using [segue destinationViewController]. 33 | // Pass the selected object to the new view controller. 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ContentViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.h 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/23. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface ContentViewController : UIViewController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ContentViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ContentViewController.m 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/23. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "ContentViewController.h" 10 | #import "SSTransformManager.h" 11 | #import "SSPageView.h" 12 | @import CoreText; 13 | 14 | @interface ContentViewController () 15 | 16 | @end 17 | 18 | @implementation ContentViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | NSString *string = @"

第64章 保护起来

萧慕白淡然一笑:“哦,是吗?那真是很巧,我也是。”

"; 27 | 28 | UILabel *label = [[UILabel alloc] init]; 29 | label.text = string; 30 | label.numberOfLines = 0; 31 | [label setFrame:CGRectMake(50, 100, 300, 200)]; 32 | [label sizeToFit]; 33 | 34 | [self.view addSubview:label]; 35 | 36 | // NSLog(@"%@", string); 37 | 38 | [self testHTMLWithStr:string]; 39 | 40 | SSPageView *pageView = [[SSPageView alloc] initWithFrame:CGRectMake(0, 0, 300, 300)]; 41 | [self.view addSubview:pageView]; 42 | } 43 | 44 | /* 45 | #pragma mark - Navigation 46 | 47 | // In a storyboard-based application, you will often want to do a little preparation before navigation 48 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 49 | // Get the new view controller using [segue destinationViewController]. 50 | // Pass the selected object to the new view controller. 51 | } 52 | */ 53 | 54 | - (void)testHTMLWithStr:(NSString *)string { 55 | 56 | // string = @"我的aafaf微我的aafaf微"; 57 | // string = @"恭喜您获得8.1元现金奖励 "; 58 | // string = @"
    \ 59 | //
  • 苹果
  • \ 60 | //
  • 水果
  • \ 61 | //
  • 桃子
  • \ 62 | //
"; 63 | 64 | string = @"
第64章 保护起来
\n\n\n

第64章 保护起来

萧慕白淡然一笑:“哦,是吗?那真是很巧,我也是。”

俩人四目相对的一瞬间,站在一旁的苏墨瞳只感觉电光火石,剑拔弩张,战争一触即发。

可是这么坦白的两个人,怎么能让苏墨瞳招架的住啊。趁着两人暗暗较劲的空档,她捂住顾美的嘴,托着顾美还有她自己的行李箱,一溜烟的跑了。

惹不起只好躲得起了,苏墨瞳一路狂奔,顾美口中发出呜呜的挣扎声,苏墨瞳也置之不理,当务之急,是马上离开这个地方。

天大地大,竟然没有她苏墨瞳的容身之所吗?她才不信这个邪。

顾美跑不动了,挣脱开了苏墨瞳的禁锢,体力不支的她坐在街边的花坛上大口的喘着粗气,“墨瞳,不行了,别跑了,再跑我就心脏脱落猝死了。"; 65 | 66 | SSTransformManager *transformManager = [[SSTransformManager alloc] init]; 67 | NSAttributedString *attrStr = [transformManager htmlStrConvertToAttributeStr:string]; 68 | 69 | UILabel *label = [[UILabel alloc] init]; 70 | label.attributedText = attrStr; 71 | [label setFrame:CGRectMake(100, 180, 200, 200)]; 72 | label.numberOfLines = 0; 73 | [label setFrame:CGRectMake(100, 180, 200, 200)]; 74 | [label sizeToFit]; 75 | 76 | [self.view addSubview:label]; 77 | } 78 | 79 | 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/data/SSAdvertisingData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSAdvertisingData.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SSAdvertisingData : NSObject 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/data/SSAdvertisingData.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSAdvertisingData.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSAdvertisingData.h" 10 | 11 | @implementation SSAdvertisingData 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/data/SSChapterData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSChapterData.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SSChapterData : NSObject 14 | 15 | @property (nonatomic, strong) NSString *chapterId; // 章节id(对应章节目录) 16 | 17 | @property (nonatomic, strong) NSString *chapterTitle; // 章节名 18 | 19 | @property (nonatomic, strong) NSString *strContent; // 章节内容 20 | 21 | @property (nonatomic, strong) NSString *lastChapterId; // 上一章节的id 22 | 23 | @property (nonatomic, strong) NSString *nextChapterId; // 下一章节的id 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/data/SSChapterData.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSChapterData.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSChapterData.h" 10 | 11 | @implementation SSChapterData 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/layout/SSLayoutChapterData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSLayoutChapterData.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SSChapterData.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | @interface SSLayoutChapterData : NSObject 16 | 17 | @property (nonatomic, strong) SSChapterData *chapterData; // real data 18 | 19 | @property (nonatomic, strong) NSArray *pagesArr; // 分页后的页面数组 20 | 21 | @property (nonatomic, strong) NSAttributedString *attrStr; // 章节富文本 22 | 23 | @property (nonatomic, assign) CGSize pageSize; 24 | 25 | - (instancetype)initWithChapterData:(SSChapterData *)chapterData pagesArray:(NSArray *)pagesArr attrStr:(NSAttributedString *)attrStr pageSize:(CGSize)pageSize; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/layout/SSLayoutChapterData.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSLayoutChapterData.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSLayoutChapterData.h" 10 | 11 | @implementation SSLayoutChapterData 12 | 13 | - (instancetype)initWithChapterData:(SSChapterData *)chapterData pagesArray:(NSArray *)pagesArr attrStr:(NSAttributedString *)attrStr pageSize:(CGSize)pageSize { 14 | self = [super init]; 15 | if (self) { 16 | self.chapterData = chapterData; 17 | self.pagesArr = pagesArr; 18 | self.attrStr = attrStr; 19 | self.pageSize = pageSize; 20 | } 21 | return self; 22 | } 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/layout/SSLayoutPageData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageData.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | 13 | 14 | @interface SSLayoutPageData : NSObject 15 | 16 | @property (nonatomic, strong) NSString *chapterId; 17 | @property (nonatomic, strong) NSString *chapterTitle; 18 | @property (nonatomic, assign) NSUInteger pageCount; 19 | @property (nonatomic, assign) NSUInteger pageIndex; 20 | 21 | @property (nonatomic, assign) CTFrameRef frameRef; // 当前页面的排版结果 22 | NS_ASSUME_NONNULL_BEGIN 23 | @property (nonatomic, assign) NSRange range; // current string in chapter 24 | @property (nonatomic, strong) NSAttributedString *attrStr; // content 25 | NS_ASSUME_NONNULL_END 26 | 27 | 28 | /** 29 | 初始化的接口,传入必须的数据 30 | 31 | @param frameRef 排版结果 32 | @param range 区域 33 | @param attrStr 富文本 34 | @return 页面数据 35 | */ 36 | - (instancetype)initWithCTFrame:(CTFrameRef)frameRef range:(NSRange)range attributeStr:(NSAttributedString *)attrStr; 37 | 38 | - (void)addChapterInfoWithChapterId:(NSString *)chapterId chapterTitle:(NSString *)chapterTitle pageCount:(NSUInteger)pageCount pageIndex:(NSUInteger)pageIndex; 39 | 40 | @end 41 | 42 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/layout/SSLayoutPageData.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageData.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSLayoutPageData.h" 10 | 11 | @implementation SSLayoutPageData 12 | 13 | -(instancetype)initWithCTFrame:(CTFrameRef)frameRef range:(NSRange)range attributeStr:(NSAttributedString *)attrStr { 14 | self = [super init]; 15 | if (self) { 16 | self.frameRef = frameRef; 17 | self.range = range; 18 | self.attrStr = attrStr; 19 | } 20 | return self; 21 | } 22 | 23 | - (void)addChapterInfoWithChapterId:(NSString *)chapterId chapterTitle:(NSString *)chapterTitle pageCount:(NSUInteger)pageCount pageIndex:(NSUInteger)pageIndex { 24 | self.chapterId = chapterId; 25 | self.chapterTitle = chapterTitle; 26 | self.pageCount = pageCount; 27 | self.pageIndex = pageIndex; 28 | } 29 | 30 | - (void)dealloc { 31 | if (self.frameRef) { 32 | CFRelease(self.frameRef); 33 | self.frameRef = NULL; 34 | } 35 | } 36 | 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/reading/SSConfigData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSConfigData.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface SSConfigData : NSObject 15 | 16 | @property (nonatomic, strong) UIFont *font; // 字体 17 | @property (nonatomic, strong) UIFont *titleFont; // 字体 18 | @property (nonatomic, strong) UIColor *textColor; // 字体颜色 19 | @property (nonatomic, assign) CGFloat paragraph; // 段间距 20 | @property (nonatomic, assign) CGFloat character; // 字间距 21 | @property (nonatomic, assign) CGFloat line; // 行间距 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/reading/SSConfigData.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSConfigData.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSConfigData.h" 10 | 11 | @implementation SSConfigData 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/reading/SSPageControllData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageControllData.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSLayoutPageData.h" 11 | #import "SSAdvertisingData.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | typedef NS_ENUM(NSUInteger, SSPageControllType) { 16 | SSPageControllTypeNormal = 1, // 正常渲染 17 | SSPageControllTypeAd, // 广告页, 暂不翻页逻辑耦合, 18 | SSPageControllTypeLoading, // 空页面,显示loading 19 | SSPageControllTypePay, // 渲染部分文字,同时覆盖购买界面 20 | }; 21 | 22 | 23 | /** 24 | 当前页面显示的数据类型; 25 | 前期不做继承封装,后期可以抽出基类,每新增一种页面类型,只需要创建新的类 26 | */ 27 | @interface SSPageControllData : NSObject 28 | 29 | @property (nonatomic, assign) SSPageControllType pageControllType; 30 | 31 | // SSPageControllTypeNormal 32 | @property (nonatomic, strong) SSLayoutPageData *layoutPageData; // 页面渲染的数据 33 | 34 | // SSPageControllTypeLoading 35 | @property (nonatomic, strong) NSString *loadingChapterId; // 等待加载的章节id 36 | 37 | // SSPageControllTypeAd 38 | @property (nonatomic, strong) SSAdvertisingData *advertisingData; // 广告数据 39 | 40 | - (instancetype)initWithType:(SSPageControllType)type; 41 | 42 | @end 43 | 44 | NS_ASSUME_NONNULL_END 45 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/reading/SSPageControllData.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageControllData.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSPageControllData.h" 10 | 11 | @implementation SSPageControllData 12 | 13 | - (instancetype)initWithType:(SSPageControllType)type { 14 | self = [super init]; 15 | if (self) { 16 | self.pageControllType = type; 17 | } 18 | return self; 19 | } 20 | 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/reading/SSReadingContextData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSReadingContextData.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SSPageControllData.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | 16 | #define KEY_READ_PAGE @"KEY_READ_PAGE" 17 | 18 | @interface SSReadingContextData : NSObject 19 | 20 | @property (nonatomic, strong) NSString *bookId; 21 | @property (nonatomic, strong) NSString *chapterId; 22 | @property (nonatomic, assign) NSUInteger curPage; 23 | @property (nonatomic, assign) CGSize pageSize; 24 | 25 | @property (nonatomic, strong) SSPageControllData *pageControllData; 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/DataModel/reading/SSReadingContextData.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSReadingContextData.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSReadingContextData.h" 10 | 11 | @implementation SSReadingContextData 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Manager/SSDataManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSDataManager.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSChapterData.h" 11 | 12 | typedef void(^SSDataCallback)(SSChapterData *chapterData); 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface SSDataManager : NSObject 17 | 18 | - (SSChapterData *)syncGetRemoteChatperDataWithBookId:(NSString *)bookId chapterId:(NSString *)chapterId; 19 | 20 | - (void)asyncGetRemoteChatperDataWithBookId:(NSString *)bookId chapterId:(NSString *)chapterId callback:(SSDataCallback)callback; 21 | 22 | @end 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Manager/SSDataManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSDataManager.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSDataManager.h" 10 | #import "SSTransformManager.h" 11 | 12 | @implementation SSDataManager 13 | 14 | - (SSChapterData *)syncGetRemoteChatperDataWithBookId:(NSString *)bookId chapterId:(NSString *)chapterId { 15 | return [self getFakeChapterDataWithId:chapterId]; 16 | } 17 | 18 | - (void)asyncGetRemoteChatperDataWithBookId:(NSString *)bookId chapterId:(NSString *)chapterId callback:(SSDataCallback)callback { 19 | SSChapterData *chapterData = [self getFakeChapterDataWithId:chapterId]; 20 | if (callback) { 21 | float delayTime = 0.01; 22 | // if ([chapterId intValue] != 1) { // 23 | // delayTime = 10; 24 | // } 25 | 26 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 27 | callback(chapterData); 28 | }); 29 | } 30 | } 31 | 32 | - (SSChapterData *)getFakeChapterDataWithId:(NSString *)chapterId { 33 | NSString *fakeStr = @"

萧慕白淡然一笑:“哦,是吗?那真是很巧,我也是。”

俩人四目相对的一瞬间,站在一旁的苏墨瞳只感觉电光火石,剑拔弩张,战争一触即发。

可是这么坦白的两个人,怎么能让苏墨瞳招架的住啊。趁着两人暗暗较劲的空档,她捂住顾美的嘴,托着顾美还有她自己的行李箱,一溜烟的跑了。

惹不起只好躲得起了,苏墨瞳一路狂奔,顾美口中发出呜呜的挣扎声,苏墨瞳也置之不理,当务之急,是马上离开这个地方。

天大地大,竟然没有她苏墨瞳的容身之所吗?她才不信这个邪。

顾美跑不动了,挣脱开了苏墨瞳的禁锢,体力不支的她坐在街边的花坛上大口的喘着粗气,“墨瞳,不行了,别跑了,再跑我就心脏脱落猝死了。”

苏墨瞳也是气喘如牛,她望了望四周,确定江一寒和萧慕白没有追上来后,才放心的挨着顾美坐下,上气不接下气的质问道:“是不是你把我出卖了?”

顾美连连摆手,一脸的委屈,“真的不是我,是江总偷听到了咱俩的通话,才临时做决定来上海的。”

“那你们怎么又会出现在这家酒店呢?”苏墨瞳继续追问道,她被顾美出卖很多次了,当然不会相信她的说辞。

顾美举着手保证,“墨瞳,在盛世豪景遇到你完全是个意外,是上海分部安排我和江总入住这家酒店的,你说,怎么就那么巧。”

苏墨瞳横眉,巧?哪里巧?你明明知道我也入住这家酒店,既然江一寒也要入住进来,你就不会给我通风报信的吗?你肯定是被江一寒收买了,还死活不肯承认?

苏墨瞳的眯着眼,怀疑的眼神上上下下的打量着顾美,顾美被盯的冷汗直冒,干笑了几声,心虚的垂下头。

“顾美,你先回去吧,要是萧慕白或者是江一寒和你问起我来,你就说我去了机场,要回汉川。”苏墨瞳站起身,扬起头呼出一口浊气,烈日的阳光照射在柏油马路上,升腾起的热气,让她的心情也越发的烦闷起来。

“墨瞳,你真的要回去啊?”顾美扯着苏墨瞳的衣角,怏怏的问道。

苏墨瞳眉头一拧,阴阳怪气的厉声叫道:“是又怎么样?”

一辆出租车正好停在了苏墨瞳的脚边,她冲着司机师傅示意了下,扔下顾美钻进车里,绝尘而去。她当然不会回汉川,既然上海七日游泡汤了,那就改苏杭七日游吧,想到这,她吩咐司机师傅朝着火车站的方向开去。

另一边,发现顾美和苏墨瞳已经不见踪影的的俩个男人也在和时间进行着一场赛跑。

俩个人并肩走向停车场,各自打开车门,相视一笑。

“我一定会比你先找到苏墨瞳的。”萧慕白单手支着车门,胸有成竹。

“那我们就拭目以待吧。”江一寒耸着肩膀,钻进了驾驶位。

两辆轿车一前一后的滑出停车场,一个向左,一个向右,消失在车水马龙的闹市区。

上海这么大,到底去哪里找苏墨瞳呢?

萧慕白焦急的一边转着方向盘,一边四处瞧望。苏墨瞳啊苏墨瞳,你知不知道你越是逃的远远的,越是能激起我把你抓回,然后绑在身边的欲望。

我已经弄丢了你一次,我怎么还能弄丢你第二次呢。

拐过一个街角,眼尖的他看到了坐在花坛旁,闷闷不乐的顾美,他认得她,是苏墨瞳的闺蜜,也是江一寒的秘书。

一个急刹车,萧慕白落下车窗喊了一声,“喂。”

顾美顺着声音抬起头,萧慕白剑眉一挑,沉声问道:“墨瞳去哪了,你一定知道吧。”

“那个……那个……”顾美语塞,她抬起微颤的手指随便指了个方向,“墨瞳朝那个方向走了。”

“谢谢。”

萧慕白扬起一个微笑,轿车再一次启动,车轮卷起的灰尘迷了顾美的眼。她一边揉着眼睛一边气的跺脚,就算我说的不是实话,你也不用现在就报复我吧。

苏墨瞳这时已经到达了火车站,她付好了车钱跳下了车,顿时被上海火车站这人山人海的场面吓的一呆。

话说现在也不是春运和长假啊,都挤在火车站干什么啊?

排队买票的队伍太长,一眼都望不到售票窗口。苏墨瞳百无聊赖的站在队尾,一边等,一边习惯性的用脚尖在大理石地面上画圈圈。

江一寒真的是特意来上海找她的吗?

这个念头一冒出来,苏墨瞳立刻鄙视了自己千百遍,不已经决定和他划清界限了吗?不已经决定和他老死不相往来了吗?怎么一见到他人,那颗坚定的心又动摇了?

哎!苏墨瞳你醒醒吧,求求你醒醒好不好?

手臂就是这时,突然被一双强有力的大手拽住,苏墨瞳侧目的瞬间,又被扯进了一个宽厚的怀抱,强有力的心跳声震击着她的耳膜,迷人的声线响在她的耳畔,“看你这回,还往哪里跑?”

又是江一寒!!

苏墨瞳心中小鹿乱撞,江一寒怎么就能轻而易举的找到她?她明明对顾美也说了谎呢。

苏墨瞳用尽了全力挣脱开了这个怀抱,退后了一步,和江一寒保持着安全的距离,冷若冰霜的面庞上没有丝毫的感情色彩。“江总,您找我有事吗?”

“我……”江一寒一时语塞,他找苏墨瞳有什么事吗?好像是没有的。能看到苏墨瞳神采奕奕完好无缺的站在这里他也就不虚此行了。就算苏墨瞳对他冷眼相对,也燃不起他胸口处气愤的火焰。

他忘记了唐洛川给他的那三拳,也忘记了唐洛川对他的警告。此刻他的眼,他的脑,他的心,唯有苏墨瞳。

“既然没事我就不奉陪了。”苏墨瞳微笑颔首,拖着行李,站在了另一侧的购票队伍中。

江一寒一怔,第一次觉得自己是这般无用,苏墨瞳还没怎么样呢,他先乱了阵脚。他跟上苏墨瞳,静静的站在她的身后,随着队伍一点点的向前移动。偶尔,会有看起来不像是善类的几个男子穿梭在队伍里。这时,江一寒都会用手臂圈成一个保护圈,把苏墨瞳牢牢的圈在中央,保护起来。

"; 34 | SSTransformManager *tranformMgr = [[SSTransformManager alloc] init]; 35 | NSAttributedString *attrStr = [tranformMgr htmlStrConvertToAttributeStr:fakeStr]; 36 | SSChapterData *chapterData = [[SSChapterData alloc] init]; 37 | chapterData.chapterId = chapterId; 38 | chapterData.lastChapterId = [chapterId intValue] > 0 ? [NSString stringWithFormat:@"%d", [chapterId intValue] - 1] : nil; 39 | chapterData.nextChapterId = [chapterId intValue] < 3 ? [NSString stringWithFormat:@"%d", [chapterId intValue] + 1] : nil; 40 | chapterData.chapterTitle = [NSString stringWithFormat:@"第%@章(测试标题)", chapterId]; 41 | chapterData.strContent = attrStr.string; 42 | 43 | return chapterData; 44 | } 45 | 46 | - (NSString *)stringByReplaceReturnWithString:(NSString *)str { 47 | return [str stringByReplacingOccurrencesOfString:@"\\s*\\n+\\s*" 48 | withString:@"\n  " 49 | options:NSRegularExpressionSearch // 匹配正则表达式 50 | range:NSMakeRange (0, str.length)]; 51 | } 52 | @end 53 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Manager/SSLayoutManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSLayoutManager.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import "SSConfigData.h" 13 | #import "SSLayoutPageData.h" 14 | #import "SSlayoutChapterData.h" 15 | 16 | NS_ASSUME_NONNULL_BEGIN 17 | 18 | 19 | @interface SSLayoutManager : NSObject 20 | 21 | - (SSLayoutChapterData *)getLayoutChapterDataWithChapterData:(SSChapterData *)chapterData configData:(SSConfigData *)configData pageSize:(CGSize)pageSize; 22 | 23 | - (SSLayoutPageData *)getLayoutPageDataWithLayoutChapterData:(SSLayoutChapterData *)layoutChapterData pageIndex:(NSUInteger)pageIndex; 24 | 25 | 26 | 27 | @end 28 | 29 | NS_ASSUME_NONNULL_END 30 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Manager/SSLayoutManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSLayoutManager.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSLayoutManager.h" 10 | 11 | @implementation SSLayoutManager 12 | 13 | 14 | - (SSLayoutChapterData *)getLayoutChapterDataWithChapterData:(SSChapterData *)chapterData configData:(SSConfigData *)configData pageSize:(CGSize)pageSize { 15 | /** 16 | 1、传入章节内容和格式数据,输出富文本; 17 | 2、传入富文本和页面大小,传出分页范围数组; 18 | 3、传入分页内容、页数、富文本,传出当页数据; 19 | */ 20 | NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] init]; 21 | [attrStr appendAttributedString:[self getTitleAttributeStrWithStr:[NSString stringWithFormat:@"\n第%@章 保护起来\n", chapterData.chapterId] configData:configData]]; 22 | [attrStr appendAttributedString:[self getAttributeStrWithStr:chapterData.strContent configData:configData]]; 23 | NSArray *pagesArr = [self getPagesArrtWithAttributeStr:attrStr pageSize:pageSize]; 24 | SSLayoutChapterData *layoutChapterData = [[SSLayoutChapterData alloc] initWithChapterData:chapterData pagesArray:pagesArr attrStr:attrStr pageSize:pageSize]; 25 | return layoutChapterData; 26 | } 27 | 28 | /** 29 | 计算富文本在特定size的分页情况 30 | @param attributeStr 富文本 31 | @param pageSize 页面大小 32 | @return NSValue,里面放着每页的NSRange 33 | */ 34 | - (NSArray *)getPagesArrtWithAttributeStr:(NSMutableAttributedString *)attributeStr pageSize:(CGSize)pageSize { 35 | NSMutableArray *resultRange = [NSMutableArray array]; // 返回结果数组 36 | CGRect rect = CGRectMake(0, 0, pageSize.width, pageSize.height); // 每页的显示区域大小 37 | NSUInteger curIndex = 0; // 分页起点,初始为第0个字符 38 | while (curIndex < attributeStr.length) { // 没有超过最后的字符串,表明至少剩余一个字符 39 | NSUInteger maxLength = MIN(1000, attributeStr.length - curIndex); // 1000为最小字体的每页最大数量,减少计算量 40 | NSAttributedString * subString = [attributeStr attributedSubstringFromRange:NSMakeRange(curIndex, maxLength)]; // 截取字符串 41 | 42 | if (curIndex > 0 && [attributeStr.string characterAtIndex:curIndex - 1] != '\n') { 43 | NSMutableParagraphStyle *style = [attributeStr attribute:NSParagraphStyleAttributeName atIndex:curIndex effectiveRange:NULL]; 44 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 45 | paragraphStyle.firstLineHeadIndent = 0; // 跨页的逻辑处理太过复杂, 用换行的空格来替代 46 | // paragraphStyle.headIndent = 5; 47 | paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping; 48 | paragraphStyle.lineSpacing = style.lineSpacing; 49 | paragraphStyle.paragraphSpacing = style.paragraphSpacing; 50 | paragraphStyle.alignment = NSTextAlignmentJustified; 51 | [attributeStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(curIndex, 1)]; 52 | } 53 | 54 | 55 | CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) subString); // 根据富文本创建排版类CTFramesetterRef 56 | UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:rect]; 57 | CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), bezierPath.CGPath, NULL); // 创建排版数据,第个参数的range.length=0表示放字符直到区域填满 58 | CFRange visiableRange = CTFrameGetVisibleStringRange(frameRef); // 获取当前可见的字符串区域 59 | NSRange realRange = {curIndex, visiableRange.length}; // 当页在原始字符串中的区域 60 | [resultRange addObject:[NSValue valueWithRange:realRange]]; // 记录当页结果 61 | curIndex += realRange.length; //增加索引 62 | CFRelease(frameRef); 63 | CFRelease(frameSetter); 64 | }; 65 | return resultRange; 66 | } 67 | 68 | - (NSAttributedString *)getTitleAttributeStrWithStr:(NSString *)contentStr configData:(SSConfigData *)configData { 69 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; 70 | dict[NSFontAttributeName] = configData.titleFont; 71 | dict[NSForegroundColorAttributeName] = configData.textColor; 72 | dict[NSKernAttributeName] = @(configData.character); 73 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 74 | paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping; 75 | paragraphStyle.lineSpacing = configData.line; 76 | paragraphStyle.paragraphSpacingBefore = configData.paragraph; 77 | paragraphStyle.paragraphSpacing = configData.paragraph; 78 | paragraphStyle.alignment = NSTextAlignmentCenter; 79 | dict[NSParagraphStyleAttributeName] = paragraphStyle; 80 | 81 | NSAttributedString *ret = [[NSAttributedString alloc] initWithString:contentStr 82 | attributes:dict]; 83 | return ret; 84 | } 85 | 86 | - (NSAttributedString *)getAttributeStrWithStr:(NSString *)contentStr configData:(SSConfigData *)configData { 87 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; 88 | if (configData.font) { 89 | dict[NSFontAttributeName] = configData.font; 90 | } 91 | if (configData.textColor) { 92 | dict[NSForegroundColorAttributeName] = configData.textColor; 93 | } 94 | dict[NSKernAttributeName] = @(configData.character); 95 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 96 | 97 | paragraphStyle.firstLineHeadIndent = [configData.font pointSize] * 2; // 跨页的逻辑处理太过复杂, 用换行的空格来替代 98 | // paragraphStyle.headIndent = 5; 99 | 100 | paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping; 101 | paragraphStyle.lineSpacing = configData.line; 102 | 103 | paragraphStyle.paragraphSpacing = configData.paragraph; 104 | paragraphStyle.alignment = NSTextAlignmentJustified; 105 | dict[NSParagraphStyleAttributeName] = paragraphStyle; 106 | 107 | NSAttributedString *ret = [[NSAttributedString alloc] initWithString:contentStr 108 | attributes:dict]; 109 | return ret; 110 | } 111 | 112 | - (SSLayoutPageData *)getLayoutPageDataWithLayoutChapterData:(SSLayoutChapterData *)layoutChapterData pageIndex:(NSUInteger)pageIndex { 113 | NSAssert(pageIndex < layoutChapterData.pagesArr.count, @"out range pageIndex"); 114 | NSRange pageRange = [(NSValue *)[layoutChapterData.pagesArr objectAtIndex:pageIndex] rangeValue]; 115 | NSAttributedString * subString = [layoutChapterData.attrStr attributedSubstringFromRange:pageRange]; // 截取字符串 116 | CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) subString); // 根据富文本创建排版类CTFramesetterRef 117 | UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, layoutChapterData.pageSize.width, layoutChapterData.pageSize.height)]; 118 | CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), bezierPath.CGPath, NULL); // 创建排版数据,第个参数的range.length=0表示放字符直到区域填满 119 | CFRelease(frameSetter); 120 | 121 | SSLayoutPageData *pageData = [[SSLayoutPageData alloc] initWithCTFrame:frameRef range:pageRange attributeStr:subString]; 122 | [pageData addChapterInfoWithChapterId:layoutChapterData.chapterData.chapterId chapterTitle:layoutChapterData.chapterData.chapterTitle pageCount:layoutChapterData.pagesArr.count pageIndex:pageIndex]; 123 | return pageData; 124 | } 125 | 126 | // apple demo 127 | - (void)calculateLineWithView:(UIView *)view { 128 | double width; CGContextRef context; CGPoint textPosition; CFMutableAttributedStringRef attrString; 129 | // Initialize those variables. 130 | width = 100; 131 | context = UIGraphicsGetCurrentContext(); 132 | 133 | CGContextTranslateCTM(context, 0, view.bounds.size.height); 134 | CGContextScaleCTM(context, 1.0, -1.0); 135 | 136 | textPosition = CGPointMake(0, 0); 137 | // attrString = (__bridge CFAttributedStringRef)[[NSAttributedString alloc] initWithString:@"textPosition; CFAttributedStringRef attrString;// Initialize those variables."]; 138 | 139 | // Initialize a string. 140 | CFStringRef textString = CFSTR("Hello, World! I know nothing in the world that has as much power as a word. Sometimes I write one, and I look at it, until it begins to shine."); 141 | 142 | // Create a mutable attributed string with a max length of 0. 143 | // The max length is a hint as to how much internal storage to reserve. 144 | // 0 means no hint. 145 | attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); 146 | CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), 147 | textString); 148 | 149 | // Create a typesetter using the attributed string. 150 | CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString(attrString); 151 | 152 | // Find a break for line from the beginning of the string to the given width. 153 | CFIndex start = 0; 154 | CFIndex count = CTTypesetterSuggestLineBreak(typesetter, start, width); 155 | 156 | // Use the returned character count (to the break) to create the line. 157 | CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(start, count)); 158 | 159 | // Get the offset needed to center the line. 160 | float flush = 0.5; // centered 161 | double penOffset = CTLineGetPenOffsetForFlush(line, flush, width); 162 | 163 | // Move the given text drawing position by the calculated offset and draw the line. 164 | CGContextSetTextPosition(context, textPosition.x + penOffset, textPosition.y); 165 | CTLineDraw(line, context); 166 | 167 | // Move the index beyond the line break. 168 | start += count; 169 | } 170 | 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Manager/SSPageControllManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageControlManager.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/25. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSReadingContextData.h" 11 | #import "SSLayoutChapterData.h" 12 | #import "SSLayoutPageData.h" 13 | #import "SSChapterData.h" 14 | #import "SSConfigData.h" 15 | 16 | #define firstPage (0) 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | 20 | 21 | @protocol SSPageControllDelegate 22 | 23 | @required 24 | 25 | 26 | @optional 27 | 28 | - (void)onDataReadyWithChapterId:(NSString *)chapterId; 29 | 30 | @end 31 | 32 | 33 | /** 34 | 分页控制器,持有阅读器中所有章节数据 35 | 36 | 37 | * 进入新章节后,默认请求相邻章节的数据; 38 | * 处理上/下一页的逻辑: 39 | 1、判断目标页是否在当前章节,是则直接返回,否则进2; 40 | 2、请求相邻章节的本地缓存数据,如果存在,则进行排版分页,返回首页/末页数据; 41 | 3、不存在返回当前页数据,并请求数据; 42 | 43 | */ 44 | @interface SSPageControllManager : NSObject 45 | 46 | 47 | - (instancetype)initWithReadingContextData:(SSReadingContextData *)readingContextData configData:(SSConfigData *)configData; 48 | 49 | /** 50 | 进入新的页面阅读 51 | 更新阅读进度,预加载相邻章节 52 | */ 53 | - (void)onNewPageDidAppear:(SSPageControllData *)layoutPageData; 54 | 55 | 56 | /** 57 | 返回初始化章节第一个页面的数据,有数据则直接显示;否则显示loading; 58 | */ 59 | - (SSPageControllData *)getFirstPageControllData; 60 | 61 | /** 62 | 获取相邻页面的内容 63 | 64 | @param isNext YES表示获取下一页,NO表示获取上一页; 65 | @return 页面排版数据,为空表示还没数据 66 | */ 67 | - (SSPageControllData *)getNearPageDataWithIsNext:(BOOL)isNext; 68 | 69 | // todo 同步初始化,后期可以改成callback,在初始化h完毕后回调,期间可以由UI层显示loading; 70 | - (void)syncInitFirstPage; 71 | 72 | @end 73 | 74 | NS_ASSUME_NONNULL_END 75 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Manager/SSPageControllManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageControlManager.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/25. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSPageControllManager.h" 10 | #import "SSLayoutManager.h" 11 | #import "SSDataManager.h" 12 | 13 | 14 | @interface SSPageControllManager () 15 | 16 | @property (nonatomic, strong) SSReadingContextData *readingContextData; 17 | 18 | @property (nonatomic, strong) NSMutableDictionary *chapterDataDict; // chapterId => SSChapterData,章节数据 19 | 20 | @property (nonatomic, strong) SSLayoutChapterData *curLayoutChapterData; 21 | 22 | @property (nonatomic, strong) SSLayoutManager *layoutManager; 23 | 24 | @property (nonatomic, strong) SSDataManager *dataManager; 25 | 26 | @property (nonatomic, strong) SSConfigData *configData; 27 | @end 28 | 29 | 30 | @implementation SSPageControllManager 31 | 32 | - (instancetype)initWithReadingContextData:(SSReadingContextData *)readingContextData configData:(nonnull SSConfigData *)configData { 33 | if (self = [super init]) { 34 | self.readingContextData = readingContextData; 35 | self.layoutManager = [[SSLayoutManager alloc] init]; 36 | self.configData = configData; 37 | self.dataManager = [[SSDataManager alloc] init]; 38 | self.chapterDataDict = [[NSMutableDictionary alloc] init]; 39 | 40 | [self syncInitFirstPage]; 41 | } 42 | return self; 43 | } 44 | 45 | - (void)syncInitFirstPage { 46 | SSChapterData *chapterData = [self.dataManager syncGetRemoteChatperDataWithBookId:self.readingContextData.bookId chapterId:self.readingContextData.chapterId]; 47 | self.chapterDataDict[chapterData.chapterId] = chapterData; 48 | self.curLayoutChapterData = [self getLayoutChapterData:chapterData configData:self.configData]; 49 | } 50 | 51 | 52 | - (void)onNewPageDidAppear:(SSPageControllData *)pageControllData { 53 | // reset loading 54 | self.readingContextData.pageControllData = pageControllData; 55 | 56 | switch (pageControllData.pageControllType) { 57 | case SSPageControllTypeNormal: 58 | { 59 | SSLayoutPageData *layoutPageData = pageControllData.layoutPageData; 60 | if (layoutPageData.chapterId != self.curLayoutChapterData.chapterData.chapterId) { // 新出现章节 61 | SSChapterData *chapterData = self.chapterDataDict[layoutPageData.chapterId]; // 本地应该有数据,如果没有就算异常情况 62 | NSAssert(chapterData, @"emtpy data"); 63 | self.curLayoutChapterData = [self getLayoutChapterData:chapterData configData:self.configData]; 64 | } 65 | [[NSUserDefaults standardUserDefaults] setObject:@(layoutPageData.pageIndex) forKey:KEY_READ_PAGE]; // 缓存 66 | [[NSUserDefaults standardUserDefaults] synchronize]; 67 | 68 | self.readingContextData.curPage = layoutPageData.pageIndex; 69 | self.readingContextData.chapterId = layoutPageData.chapterId; 70 | 71 | // 为了防止网络异常情况,可以继续请求下相邻章节的数据 72 | SSChapterData *chapterData = self.chapterDataDict[layoutPageData.chapterId]; // 本地应该数据,如果没有就算异常情况 73 | if (chapterData.lastChapterId) { 74 | [self loadChapterDataWithChapterId:chapterData.lastChapterId]; 75 | } 76 | if (chapterData.nextChapterId) { 77 | [self loadChapterDataWithChapterId:chapterData.nextChapterId]; 78 | } 79 | break; 80 | } 81 | case SSPageControllTypeLoading: 82 | { 83 | break; 84 | } 85 | case SSPageControllTypePay: 86 | { 87 | break; 88 | } 89 | 90 | default: 91 | break; 92 | } 93 | } 94 | 95 | - (SSPageControllData *)getNearPageDataWithIsNext:(BOOL)isNext { 96 | SSPageControllData *ret; 97 | 98 | switch (self.readingContextData.pageControllData.pageControllType) { 99 | case SSPageControllTypeNormal: 100 | { 101 | NSInteger newIndex = isNext ? self.readingContextData.curPage + 1 : self.readingContextData.curPage - 1; 102 | if (newIndex < self.curLayoutChapterData.pagesArr.count) { // 本章节 103 | ret = [[SSPageControllData alloc] initWithType:SSPageControllTypeNormal]; 104 | ret.layoutPageData = [self.layoutManager getLayoutPageDataWithLayoutChapterData:self.curLayoutChapterData pageIndex:newIndex]; 105 | } 106 | else { 107 | NSString *newChapterId = isNext ? self.curLayoutChapterData.chapterData.nextChapterId : self.curLayoutChapterData.chapterData.lastChapterId; 108 | if (newChapterId) { 109 | if ([self.chapterDataDict objectForKey:newChapterId]) { 110 | SSChapterData *newChapterData = [self.chapterDataDict objectForKey:newChapterId]; 111 | SSLayoutChapterData *newLayoutChapterData = [self.layoutManager getLayoutChapterDataWithChapterData:newChapterData configData:self.configData pageSize:self.readingContextData.pageSize]; 112 | newIndex = isNext ? 0 : newLayoutChapterData.pagesArr.count - 1; 113 | 114 | ret = [[SSPageControllData alloc] initWithType:SSPageControllTypeNormal]; 115 | ret.layoutPageData = [self.layoutManager getLayoutPageDataWithLayoutChapterData:newLayoutChapterData pageIndex:newIndex]; 116 | } 117 | else { 118 | ret = [[SSPageControllData alloc] initWithType:SSPageControllTypeLoading]; 119 | ret.loadingChapterId = newChapterId; 120 | } 121 | } 122 | } 123 | break; 124 | } 125 | case SSPageControllTypeLoading: 126 | { 127 | if (isNext) { 128 | ret = nil; // no next when loading 129 | } 130 | else { 131 | ret = [[SSPageControllData alloc] initWithType:SSPageControllTypeNormal]; 132 | ret.layoutPageData = [self getLayoutPageDataWithIndex:self.readingContextData.curPage]; 133 | } 134 | break; 135 | } 136 | case SSPageControllTypePay: 137 | { 138 | break; 139 | } 140 | 141 | default: 142 | break; 143 | } 144 | 145 | 146 | return ret; 147 | } 148 | 149 | - (SSPageControllData *)getFirstPageControllData { 150 | SSPageControllData *ret; 151 | if ([self isChapterDataReady]) { 152 | ret = [[SSPageControllData alloc] initWithType:SSPageControllTypeNormal]; 153 | ret.layoutPageData = [self getLayoutPageDataWithIndex:firstPage]; 154 | } 155 | else { 156 | ret = [[SSPageControllData alloc] initWithType:SSPageControllTypeLoading]; 157 | ret.loadingChapterId = self.readingContextData.chapterId; 158 | } 159 | return ret; 160 | } 161 | 162 | - (BOOL)isChapterDataReady { 163 | return [self.chapterDataDict objectForKey:self.readingContextData.chapterId]; 164 | } 165 | 166 | - (SSLayoutPageData *)getLayoutPageDataWithIndex:(NSUInteger)pageIndex { 167 | SSLayoutPageData *ret; 168 | ret = [self.layoutManager getLayoutPageDataWithLayoutChapterData:self.curLayoutChapterData pageIndex:pageIndex]; 169 | return ret; 170 | } 171 | 172 | 173 | - (SSLayoutChapterData *)getLayoutChapterData:(SSChapterData *)chapterData configData:(SSConfigData *)configData { 174 | return [self.layoutManager getLayoutChapterDataWithChapterData:chapterData configData:configData pageSize:self.readingContextData.pageSize]; 175 | } 176 | 177 | 178 | - (void)loadChapterDataWithChapterId:(NSString *)chapterId { 179 | if (![self.chapterDataDict objectForKey:chapterId]) { 180 | [self.dataManager asyncGetRemoteChatperDataWithBookId:self.readingContextData.bookId chapterId:chapterId 181 | callback:^(SSChapterData *chapterData) { 182 | NSAssert(chapterData && chapterData.chapterId && chapterData.strContent, @"loadChapterDataWithChapterId chapterData is null"); 183 | if (!chapterData.chapterTitle) { 184 | NSLog(@"loadChapterDataWithChapterId title is null, but is not fatel"); 185 | } 186 | if (chapterData && chapterData.chapterId) { 187 | self.chapterDataDict[chapterData.chapterId] = chapterData; 188 | } 189 | }]; 190 | } 191 | } 192 | 193 | 194 | @end 195 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Manager/SSTransformManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSTransformManager.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface SSTransformManager : NSObject 17 | 18 | /** 19 | * 富文本转html字符串 20 | */ 21 | - (NSString *)attributeStrConvertToHtmlStr:(NSAttributedString *)attributeStr; 22 | 23 | 24 | /** 25 | * html字符串转富文本 26 | */ 27 | - (NSAttributedString *)htmlStrConvertToAttributeStr:(NSString *)htmlStr; 28 | 29 | 30 | @end 31 | 32 | NS_ASSUME_NONNULL_END 33 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/Manager/SSTransformManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSTransformManager.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSTransformManager.h" 10 | 11 | @implementation SSTransformManager 12 | 13 | 14 | - (NSString *)attributeStrConvertToHtmlStr:(NSAttributedString *)attributeStr { 15 | NSDictionary *dic = @{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType, 16 | NSCharacterEncodingDocumentAttribute:[NSNumber numberWithInt:NSUTF8StringEncoding]}; 17 | NSData *htmlData = [attributeStr dataFromRange:NSMakeRange(0, attributeStr.length) 18 | documentAttributes:dic 19 | error:nil]; 20 | 21 | return [[NSString alloc] initWithData:htmlData 22 | encoding:NSUTF8StringEncoding]; 23 | } 24 | 25 | - (NSAttributedString *)htmlStrConvertToAttributeStr:(NSString *)htmlStr { 26 | return [[NSAttributedString alloc] initWithData:[htmlStr dataUsingEncoding:NSUnicodeStringEncoding] 27 | options:@{NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType} 28 | documentAttributes:nil 29 | error:nil]; 30 | } 31 | 32 | 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/SSTabBarViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSTabBarViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/26. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SSTabBarViewController : UITabBarController 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/SSTabBarViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSTabBarViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/26. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSTabBarViewController.h" 10 | 11 | @implementation SSTabBarViewController 12 | 13 | - (void)viewDidLoad { 14 | [super viewDidLoad]; 15 | } 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/View/SSPageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageView.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SSLayoutPageData.h" 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | 16 | /** 17 | 显示一页内容的view 18 | */ 19 | @interface SSPageView : UIView 20 | 21 | @property (nonatomic, strong) SSLayoutPageData *pageData; 22 | 23 | @end 24 | 25 | NS_ASSUME_NONNULL_END 26 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/View/SSPageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageView.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSPageView.h" 10 | 11 | @implementation SSPageView 12 | { 13 | } 14 | 15 | - (instancetype)initWithFrame:(CGRect)frame { 16 | self = [super initWithFrame:frame]; 17 | self.backgroundColor = [UIColor whiteColor]; 18 | 19 | return self; 20 | } 21 | 22 | - (void)drawRect:(CGRect)rect { 23 | [super drawRect:rect]; 24 | 25 | // CGContextRef context = UIGraphicsGetCurrentContext(); 26 | // NSLog(@"CGContext default matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context))); 27 | // CGContextSaveGState(context); 28 | // CGContextTranslateCTM(context, 0, self.bounds.size.height); 29 | // CGContextScaleCTM(context, 1.0, -1.0); 30 | // NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:@"测试文本" attributes:@{ 31 | // NSForegroundColorAttributeName:[UIColor whiteColor], 32 | // NSFontAttributeName:[UIFont systemFontOfSize:14], 33 | // }]; 34 | // CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) attrStr); // 根据富文本创建排版类CTFramesetterRef 35 | // UIBezierPath * bezierPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 100, 20)]; 36 | // CTFrameRef frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), bezierPath.CGPath, NULL); // 创建排版数据 37 | // CTFrameDraw(frameRef, context); 38 | // CGContextRestoreGState(context); 39 | // 40 | // 41 | // NSLog(@"CGContext default CTM matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context))); 42 | // UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 20)]; 43 | // testLabel.text = @"测试文本"; 44 | // testLabel.font = [UIFont systemFontOfSize:14]; 45 | // testLabel.textColor = [UIColor whiteColor]; 46 | // [testLabel.layer renderInContext:context]; 47 | 48 | // return ; 49 | 50 | // UIImage *image = [UIImage imageNamed:@"panda.png"]; 51 | // CGContextDrawImage(context, CGRectMake(0, 0, image.size.width / 2, image.size.height / 2), image.CGImage); 52 | 53 | if (self.pageData && self.pageData.frameRef) { 54 | CGContextRef context = UIGraphicsGetCurrentContext(); 55 | // NSLog(@"CGContext default matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context))); 56 | // CGContextSetTextMatrix(context, CGAffineTransformIdentity); 57 | CGContextScaleCTM(context, 1.0, -1.0); 58 | CGContextTranslateCTM(context, 0, -self.bounds.size.height); 59 | 60 | // NSLog(@"CGContext reset matrix %@", NSStringFromCGAffineTransform(CGContextGetCTM(context))); 61 | 62 | CTFrameDraw(self.pageData.frameRef, context); 63 | } 64 | else { 65 | 66 | 67 | 68 | } 69 | } 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSAdViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSAdViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/26. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSBasePageViewController.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface SSAdViewController : SSBasePageViewController 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSAdViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSAdViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/26. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSAdViewController.h" 10 | 11 | @interface SSAdViewController () 12 | 13 | @property (nonatomic, strong) UIImageView *adImageView; 14 | 15 | @end 16 | 17 | @implementation SSAdViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view. 22 | 23 | UILabel *label = [[UILabel alloc] init]; 24 | label.text = @"我是广告页"; 25 | [label sizeToFit]; 26 | label.center = self.view.center; 27 | [self.view addSubview:label]; 28 | 29 | 30 | static int test; 31 | ++test; 32 | self.view.backgroundColor = test % 3 ? [UIColor orangeColor] : [UIColor greenColor]; 33 | } 34 | 35 | /* 36 | #pragma mark - Navigation 37 | 38 | // In a storyboard-based application, you will often want to do a little preparation before navigation 39 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 40 | // Get the new view controller using [segue destinationViewController]. 41 | // Pass the selected object to the new view controller. 42 | } 43 | */ 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSBackViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BackViewController.h 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface SSBackViewController : UIViewController 14 | 15 | - (void)updateWithViewController:(UIViewController *)viewController; 16 | 17 | @end 18 | 19 | NS_ASSUME_NONNULL_END 20 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSBackViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BackViewController.m 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | #import "SSBackViewController.h" 9 | #import 10 | 11 | @interface SSBackViewController () 12 | 13 | @property (nonatomic, strong) UIImageView *imageView; 14 | @property (nonatomic, strong) UIImage *backgroundImage; 15 | 16 | @end 17 | 18 | @implementation SSBackViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | self.imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; 24 | [self.imageView setImage:self.backgroundImage]; 25 | [self.view addSubview:self.imageView]; 26 | } 27 | 28 | - (void)updateWithViewController:(UIViewController *)viewController { 29 | self.backgroundImage = [self captureView:viewController.view]; 30 | } 31 | 32 | - (UIImage *)captureView:(UIView *)view { 33 | CGRect rect = view.bounds; 34 | UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0.0f); 35 | CGContextRef context = UIGraphicsGetCurrentContext(); 36 | 37 | CGAffineTransform transform = CGAffineTransformMake(-1.0, 0.0, 0.0, 1.0, rect.size.width, 0.0); 38 | CGContextConcatCTM(context,transform); 39 | [view.layer renderInContext:context]; 40 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 41 | UIGraphicsEndImageContext(); 42 | return image; 43 | } 44 | 45 | - (void)didReceiveMemoryWarning { 46 | [super didReceiveMemoryWarning]; 47 | // Dispose of any resources that can be recreated. 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSBasePageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // BasePageViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSPageControllData.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface SSBasePageViewController : UIViewController 15 | 16 | @property (nonatomic, strong, readonly) SSPageControllData *pageControllData; 17 | 18 | - (instancetype)initWithSSPageControllData:(SSPageControllData *)pageControllData; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSBasePageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // BasePageViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSBasePageViewController.h" 10 | 11 | @interface SSBasePageViewController () 12 | 13 | @end 14 | 15 | @implementation SSBasePageViewController 16 | 17 | - (instancetype)initWithSSPageControllData:(SSPageControllData *)pageControllData { 18 | if (self = [super init]) { 19 | NSAssert(pageControllData, @"initWithSSPageControllData data is null"); 20 | _pageControllData = pageControllData; 21 | } 22 | return self; 23 | } 24 | 25 | - (void)viewDidLoad { 26 | [super viewDidLoad]; 27 | // Do any additional setup after loading the view. 28 | } 29 | 30 | /* 31 | #pragma mark - Navigation 32 | 33 | // In a storyboard-based application, you will often want to do a little preparation before navigation 34 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 35 | // Get the new view controller using [segue destinationViewController]. 36 | // Pass the selected object to the new view controller. 37 | } 38 | */ 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSContentViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/23. 6 | // Copyright © 2018年 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SSContentViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSContentViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/23. 6 | // Copyright © 2018年 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSContentViewController.h" 11 | #import "SSBackViewController.h" 12 | #import "SSReadingContextData.h" 13 | #import "SSPageViewController.h" 14 | #import "SSDataManager.h" 15 | #import "SSConfigData.h" 16 | #import "SSLayoutManager.h" 17 | #import "SSLayoutChapterData.h" 18 | #import "SSPageControllManager.h" 19 | #import "SSAdViewController.h" 20 | #import "SSLoadingViewController.h" 21 | 22 | @interface SSContentViewController () 23 | 24 | @property(nonatomic, strong) UIPageViewController *pageViewController; 25 | 26 | @property (nonatomic, strong) SSPageControllManager *pageControllManager; 27 | @property (nonatomic, strong) SSConfigData *configData; 28 | 29 | @property (nonatomic, assign) NSUInteger scrollCount; 30 | @end 31 | 32 | @implementation SSContentViewController 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | // Do any additional setup after loading the view, typically from a nib. 37 | [self loadPageControllManager]; 38 | [self loadPageViewController]; 39 | [self customInitFirstPage]; 40 | [self setupGesture]; 41 | 42 | self.navigationController.navigationBarHidden = YES; 43 | self.tabBarController.tabBar.hidden = YES; 44 | } 45 | 46 | - (void)setupGesture { 47 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapPageView:)]; 48 | [self.pageViewController.view addGestureRecognizer:tap]; 49 | } 50 | 51 | #define TapGap (100) 52 | 53 | - (void)onTapPageView:(UITapGestureRecognizer *)tap { 54 | if (tap.view == self.pageViewController.view) { 55 | CGPoint point = [tap locationInView:tap.view]; 56 | if (point.x <= TapGap || point.x > CGRectGetWidth(tap.view.bounds) - TapGap) { 57 | [self manualChangePageWithIsNext:point.x > CGRectGetWidth(tap.view.bounds) - TapGap]; 58 | } 59 | else { 60 | self.navigationController.navigationBarHidden = !self.navigationController.navigationBarHidden; 61 | } 62 | } 63 | } 64 | 65 | - (void)manualChangePageWithIsNext:(BOOL)isNext { 66 | UIViewController *vc = [self getNearVCWithIsNext:isNext curViewController:self.pageViewController.viewControllers[0]]; 67 | if (!vc) { 68 | NSLog(@"manualChangePageWithIsNext empty, is next : %d", isNext); 69 | return ; 70 | } 71 | __weak typeof(self) weakSelf = self; 72 | NSArray *vcArr; 73 | if (self.pageViewController.transitionStyle == UIPageViewControllerTransitionStylePageCurl) { 74 | SSBackViewController *backVC = [[SSBackViewController alloc] init]; 75 | [backVC updateWithViewController:vc]; 76 | vcArr = @[vc, backVC]; 77 | } 78 | else { 79 | vcArr = @[vc]; 80 | } 81 | 82 | if ([vc isKindOfClass:[SSBasePageViewController class]]) { // 这段统计需要提前。 83 | [self.pageControllManager onNewPageDidAppear:[(SSBasePageViewController *)vc pageControllData]]; 84 | ++self.scrollCount; // 统计曝光 85 | } 86 | 87 | self.pageViewController.view.userInteractionEnabled = NO; 88 | NSLog(@"manualChangePageWithIsNext "); 89 | UIPageViewControllerNavigationDirection direction = isNext ? UIPageViewControllerNavigationDirectionForward : UIPageViewControllerNavigationDirectionReverse; 90 | [self.pageViewController setViewControllers:vcArr 91 | direction:direction 92 | animated:NO // 为NO,可以和微信读书对齐,同时也防止在连续点击的时候,出现异常 93 | completion:^(BOOL finished) { 94 | NSLog(@"manualChangePageWithIsNext setViewControllers finished:%d", finished); 95 | __strong typeof(self) strongSelf = weakSelf; 96 | strongSelf.pageViewController.view.userInteractionEnabled = YES; 97 | }]; 98 | } 99 | 100 | - (void)loadPageViewController { 101 | self.pageViewController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl 102 | navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal 103 | options:@{UIPageViewControllerOptionSpineLocationKey:@(UIPageViewControllerSpineLocationMin)}]; 104 | self.pageViewController.delegate = self; 105 | self.pageViewController.dataSource = self; 106 | self.pageViewController.doubleSided = YES; 107 | self.pageViewController.view.backgroundColor = BackgroundColor; 108 | [self.view addSubview:self.pageViewController.view]; 109 | [self addChildViewController:self.pageViewController]; 110 | } 111 | 112 | 113 | - (void)loadPageControllManager { 114 | SSReadingContextData *readingContextData = [self loadReadingContextData]; 115 | self.configData = [self loadConfigData]; 116 | self.pageControllManager = [[SSPageControllManager alloc] initWithReadingContextData:readingContextData configData:self.configData]; 117 | } 118 | 119 | 120 | - (void)customInitFirstPage { 121 | SSPageControllData *pageData = [self.pageControllManager getFirstPageControllData]; 122 | UIViewController *vc = [self getViewControllerWithPageControllData:pageData]; 123 | self.scrollCount = 0; 124 | NSArray *vcArr = @[vc]; 125 | // if (self.pageViewController.transitionStyle == UIPageViewControllerTransitionStylePageCurl) { 126 | // SSBackViewController *backVC = [[SSBackViewController alloc] init]; 127 | // [backVC updateWithViewController:vc]; 128 | // vcArr = @[vc, backVC]; 129 | // } 130 | 131 | __weak typeof(self) weakSelf = self; 132 | [self.pageViewController setViewControllers:vcArr 133 | direction:UIPageViewControllerNavigationDirectionForward 134 | animated:NO 135 | completion:^(BOOL finished) { 136 | __strong typeof(self) strongSelf = weakSelf; 137 | [strongSelf.pageControllManager onNewPageDidAppear:pageData]; 138 | }]; 139 | } 140 | 141 | 142 | - (SSReadingContextData *)loadReadingContextData { 143 | SSReadingContextData *ret = [[SSReadingContextData alloc] init]; 144 | if ([[NSUserDefaults standardUserDefaults] objectForKey:KEY_READ_PAGE]) { 145 | ret.curPage = [[[NSUserDefaults standardUserDefaults] objectForKey:KEY_READ_PAGE] intValue]; 146 | } 147 | else { 148 | ret.curPage = 0; 149 | } 150 | ret.chapterId = @"1"; 151 | ret.bookId = @"abc"; 152 | ret.pageSize = CGSizeMake(CGRectGetWidth(self.view.bounds) - 2 * PageHorizontalMargin, CGRectGetHeight(self.view.bounds) - PageTop - PageBottom); 153 | return ret; 154 | } 155 | 156 | - (SSConfigData *)loadConfigData { 157 | SSConfigData *configData = [[SSConfigData alloc] init]; 158 | configData.font = [UIFont systemFontOfSize:16]; 159 | configData.titleFont = [UIFont systemFontOfSize:20]; 160 | configData.textColor = [UIColor grayColor]; 161 | configData.character = 1; 162 | configData.paragraph = 10; 163 | configData.line = 5; 164 | return configData; 165 | } 166 | 167 | - (UIViewController *)getNearVCWithIsNext:(BOOL)isNext curViewController:(UIViewController *)curVC { 168 | UIViewController *ret; 169 | SSPageControllData *pageControllData = [self.pageControllManager getNearPageDataWithIsNext:isNext]; 170 | ret = [self getViewControllerWithPageControllData:pageControllData]; 171 | // if ([ret isKindOfClass:[SSPageViewController class]]) { 172 | // 173 | // } 174 | // else if (random() % 4 == 0) { 175 | // ret = [[SSAdViewController alloc] init]; 176 | // } 177 | // else if (random() % 4 == 1) { 178 | // ret = [[SSBackViewController alloc] init]; 179 | // } 180 | // else if (random() % 4 == 2) { 181 | // ret = [[SSPageViewController alloc] init]; 182 | // } 183 | // else if (random() % 4 == 3) { 184 | // ret = [[SSLoadingViewController alloc] init]; 185 | // } 186 | return ret; 187 | } 188 | 189 | - (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController { 190 | NSLog(@"viewControllerBeforeViewController"); 191 | UIViewController *ret; 192 | if ([viewController isKindOfClass:[SSPageViewController class]] && pageViewController.transitionStyle == UIPageViewControllerTransitionStylePageCurl) { 193 | SSBackViewController *backVC = [[SSBackViewController alloc] init]; 194 | [backVC updateWithViewController:viewController]; 195 | ret = backVC; 196 | } 197 | else { 198 | ret = [self getNearVCWithIsNext:NO curViewController:viewController]; 199 | } 200 | NSLog(@"return %@ ", [ret description]); 201 | return ret; 202 | } 203 | 204 | - (nullable UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController { 205 | NSLog(@"viewControllerAfterViewController"); 206 | UIViewController *ret; 207 | if ([viewController isKindOfClass:[SSPageViewController class]] && pageViewController.transitionStyle == UIPageViewControllerTransitionStylePageCurl) { 208 | SSBackViewController *backVC = [[SSBackViewController alloc] init]; 209 | [backVC updateWithViewController:viewController]; 210 | ret = backVC; 211 | } 212 | else { 213 | ret = [self getNearVCWithIsNext:YES curViewController:viewController]; 214 | } 215 | NSLog(@"return %@ ", [ret description]); 216 | return ret; 217 | } 218 | 219 | - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed { 220 | if (completed && [self.pageViewController.viewControllers[0] isKindOfClass:[SSBasePageViewController class]]) { 221 | SSBasePageViewController *pageViewController = (SSBasePageViewController *)self.pageViewController.viewControllers[0]; 222 | [self.pageControllManager onNewPageDidAppear:pageViewController.pageControllData]; 223 | ++self.scrollCount; // 统计曝光 224 | } 225 | } 226 | 227 | - (UIViewController *)getViewControllerWithPageControllData:(SSPageControllData *)pageControllData { 228 | UIViewController *ret; 229 | if (pageControllData) { 230 | switch (pageControllData.pageControllType) { 231 | case SSPageControllTypeNormal: 232 | { 233 | ret = [[SSPageViewController alloc] initWithSSPageControllData:pageControllData]; 234 | break; 235 | } 236 | case SSPageControllTypeLoading: 237 | { 238 | ret = [[SSLoadingViewController alloc] initWithSSPageControllData:pageControllData]; 239 | break; 240 | } 241 | case SSPageControllTypeAd: 242 | { 243 | ret = [[SSAdViewController alloc] initWithSSPageControllData:pageControllData]; 244 | break; 245 | } 246 | 247 | default: 248 | break; 249 | } 250 | } 251 | // NSLog(@"getViewControllerWithPageControllData vc:%@, view: %@", ret, ret.view); 252 | return ret; 253 | } 254 | 255 | @end 256 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSLoadingViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSLoadingViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSBasePageViewController.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface SSLoadingViewController : SSBasePageViewController 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSLoadingViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSLoadingViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSLoadingViewController.h" 10 | 11 | @interface SSLoadingViewController () 12 | 13 | @end 14 | 15 | @implementation SSLoadingViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view. 20 | 21 | UILabel *label = [[UILabel alloc] init]; 22 | label.text = @"loading..."; 23 | [label sizeToFit]; 24 | label.center = self.view.center; 25 | [self.view addSubview:label]; 26 | } 27 | 28 | /* 29 | #pragma mark - Navigation 30 | 31 | // In a storyboard-based application, you will often want to do a little preparation before navigation 32 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 33 | // Get the new view controller using [segue destinationViewController]. 34 | // Pass the selected object to the new view controller. 35 | } 36 | */ 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSPageViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageViewController.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SSBasePageViewController.h" 11 | #import "SSLayoutPageData.h" 12 | #import "SSPageView.h" 13 | 14 | #define PageTop (35) 15 | #define PageBottom (45) 16 | #define PageHorizontalMargin (10) 17 | 18 | NS_ASSUME_NONNULL_BEGIN 19 | 20 | /** 21 | 分页显示的控制器 22 | */ 23 | @interface SSPageViewController : SSBasePageViewController 24 | 25 | @end 26 | 27 | NS_ASSUME_NONNULL_END 28 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/ViewController/SSPageViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SSPageViewController.m 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "SSPageViewController.h" 10 | 11 | @interface SSPageViewController () 12 | 13 | @property (nonatomic, strong) SSPageView *pageView; // 渲染视图 14 | @property (nonatomic, strong) UILabel *titleLabel; 15 | @property (nonatomic, strong) UILabel *indexLabel; 16 | @property (nonatomic, strong) UILabel *batteryLabel; 17 | 18 | @property (nonatomic, strong) SSLayoutPageData *pageData; 19 | 20 | @end 21 | 22 | @implementation SSPageViewController 23 | 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | 27 | // return ; 28 | self.pageData = self.pageControllData.layoutPageData; 29 | 30 | // Do any additional setup after loading the view. 31 | self.pageView = [[SSPageView alloc] initWithFrame:CGRectMake(PageHorizontalMargin, PageTop, CGRectGetWidth(self.view.bounds) - PageHorizontalMargin * 2, 32 | CGRectGetHeight(self.view.bounds) - PageTop - PageBottom 33 | )]; 34 | self.pageView.pageData = self.pageData; 35 | [self.view addSubview:self.pageView]; 36 | self.view.backgroundColor = self.pageView.backgroundColor; 37 | 38 | self.titleLabel = [[UILabel alloc] init]; 39 | self.titleLabel.text = self.pageData.chapterTitle; 40 | [self.titleLabel sizeToFit]; 41 | self.titleLabel.width = self.view.width - 100; 42 | self.titleLabel.left = 10; 43 | self.titleLabel.top = 42; 44 | // [self.view addSubview:self.titleLabel]; 45 | 46 | self.indexLabel = [[UILabel alloc] init]; 47 | self.indexLabel.text = [NSString stringWithFormat:@"%lu/%lu", self.pageData.pageIndex + 1, self.pageData.pageCount]; 48 | self.indexLabel.textColor = [UIColor grayColor]; 49 | [self.indexLabel sizeToFit]; 50 | self.indexLabel.bottom = self.view.height - 10; 51 | self.indexLabel.left = 10; 52 | [self.view addSubview:self.indexLabel]; 53 | 54 | [UIDevice currentDevice].batteryMonitoringEnabled = YES; 55 | self.batteryLabel = [[UILabel alloc] init]; 56 | self.batteryLabel.text = [NSString stringWithFormat:@"电量%d%%", (int)[UIDevice currentDevice].batteryLevel * 100]; 57 | [self.batteryLabel sizeToFit]; 58 | self.batteryLabel.bottom = self.view.height - 10; 59 | self.batteryLabel.right = self.view.right - 10; 60 | // [self.view addSubview:self.batteryLabel]; 61 | 62 | [[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceBatteryLevelDidChangeNotification 63 | object:nil 64 | queue:[NSOperationQueue mainQueue] 65 | usingBlock:^(NSNotification *notification) { 66 | self.batteryLabel.text = [NSString stringWithFormat:@"电量%d%%", (int)[UIDevice currentDevice].batteryLevel * 100]; 67 | [self.batteryLabel sizeToFit]; 68 | self.batteryLabel.bottom = self.view.height - 10; 69 | self.batteryLabel.right = 10; 70 | }]; 71 | 72 | } 73 | 74 | 75 | - (void)dealloc { 76 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LearnHTMLtoNSString 4 | // 5 | // Created by loyinglin on 2018/10/23. 6 | // Copyright © 2018年 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/support/PrefixHeader.pch: -------------------------------------------------------------------------------- 1 | // 2 | // PrefixHeader.pch 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/26. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #ifndef PrefixHeader_pch 10 | #define PrefixHeader_pch 11 | 12 | #import "UIView+LYLayout.h" 13 | #import "SSCommonMacro.h" 14 | 15 | 16 | #endif /* PrefixHeader_pch */ 17 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/support/SSCommonMacro.h: -------------------------------------------------------------------------------- 1 | // 2 | // SSCommonMacro.h 3 | // LearnCoreText 4 | // 5 | // Created by loyinglin on 2018/10/31. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #ifndef SSCommonMacro_h 10 | #define SSCommonMacro_h 11 | 12 | 13 | 14 | #define BackgroundColor ([UIColor whiteColor]) 15 | 16 | #endif /* SSCommonMacro_h */ 17 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/support/panda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/loyinglin/LearnCoreText/e3e6a11df84f5c89b11fed5e6e9494dfdabf098e/Reader/LearnCoreText/support/panda.png -------------------------------------------------------------------------------- /Reader/LearnCoreText/vender/view/UIView+LYLayout.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LYLayout.h 3 | // LYFrameworkDemo 4 | // 5 | // Created by loyinglin on 2018/10/25. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (LYLayout) 12 | 13 | @property (nonatomic) CGFloat left; ///< Shortcut for frame.origin.x. 14 | @property (nonatomic) CGFloat top; ///< Shortcut for frame.origin.y 15 | @property (nonatomic) CGFloat right; ///< Shortcut for frame.origin.x + frame.size.width 16 | @property (nonatomic) CGFloat bottom; ///< Shortcut for frame.origin.y + frame.size.height 17 | @property (nonatomic) CGFloat width; ///< Shortcut for frame.size.width. 18 | @property (nonatomic) CGFloat height; ///< Shortcut for frame.size.height. 19 | @property (nonatomic) CGFloat centerX; ///< Shortcut for center.x 20 | @property (nonatomic) CGFloat centerY; ///< Shortcut for center.y 21 | @property (nonatomic) CGPoint origin; ///< Shortcut for frame.origin. 22 | @property (nonatomic) CGSize size; ///< Shortcut for frame.size. 23 | 24 | @end 25 | 26 | 27 | -------------------------------------------------------------------------------- /Reader/LearnCoreText/vender/view/UIView+LYLayout.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+LYLayout.m 3 | // LYFrameworkDemo 4 | // 5 | // Created by loyinglin on 2018/10/25. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import "UIView+LYLayout.h" 10 | 11 | @implementation UIView (LYLayout) 12 | 13 | - (CGFloat)left { 14 | 15 | return self.frame.origin.x; 16 | } 17 | 18 | - (void)setLeft:(CGFloat)x { 19 | 20 | CGRect frame = self.frame; 21 | frame.origin.x = x; 22 | self.frame = frame; 23 | } 24 | 25 | - (CGFloat)top { 26 | 27 | return self.frame.origin.y; 28 | } 29 | 30 | - (void)setTop:(CGFloat)y { 31 | 32 | CGRect frame = self.frame; 33 | frame.origin.y = y; 34 | self.frame = frame; 35 | } 36 | 37 | - (CGFloat)right { 38 | 39 | return self.frame.origin.x + self.frame.size.width; 40 | } 41 | 42 | - (void)setRight:(CGFloat)right { 43 | CGRect frame = self.frame; 44 | frame.origin.x = right - frame.size.width; 45 | self.frame = frame; 46 | } 47 | 48 | - (CGFloat)bottom { 49 | return self.frame.origin.y + self.frame.size.height; 50 | } 51 | 52 | - (void)setBottom:(CGFloat)bottom { 53 | CGRect frame = self.frame; 54 | frame.origin.y = bottom - frame.size.height; 55 | self.frame = frame; 56 | } 57 | 58 | - (CGFloat)width { 59 | return self.frame.size.width; 60 | } 61 | 62 | - (void)setWidth:(CGFloat)width { 63 | CGRect frame = self.frame; 64 | frame.size.width = width; 65 | self.frame = frame; 66 | } 67 | 68 | - (CGFloat)height { 69 | return self.frame.size.height; 70 | } 71 | 72 | - (void)setHeight:(CGFloat)height { 73 | CGRect frame = self.frame; 74 | frame.size.height = height; 75 | self.frame = frame; 76 | } 77 | 78 | - (CGFloat)centerX { 79 | 80 | return self.center.x; 81 | } 82 | 83 | - (void)setCenterX:(CGFloat)centerX { 84 | 85 | self.center = CGPointMake(centerX, self.center.y); 86 | } 87 | 88 | - (CGFloat)centerY { 89 | 90 | return self.center.y; 91 | } 92 | 93 | - (void)setCenterY:(CGFloat)centerY { 94 | self.center = CGPointMake(self.center.x, centerY); 95 | } 96 | 97 | - (CGPoint)origin { 98 | 99 | return self.frame.origin; 100 | } 101 | 102 | - (void)setOrigin:(CGPoint)origin { 103 | CGRect frame = self.frame; 104 | frame.origin = origin; 105 | self.frame = frame; 106 | } 107 | 108 | - (CGSize)size { 109 | 110 | return self.frame.size; 111 | } 112 | 113 | - (void)setSize:(CGSize)size { 114 | 115 | CGRect frame = self.frame; 116 | frame.size = size; 117 | self.frame = frame; 118 | } 119 | 120 | @end 121 | 122 | -------------------------------------------------------------------------------- /Reader/LearnCoreTextTests/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 | -------------------------------------------------------------------------------- /Reader/LearnCoreTextTests/LearnCoreTextTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LearnCoreTextTests.m 3 | // LearnCoreTextTests 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LearnCoreTextTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LearnCoreTextTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /Reader/LearnCoreTextUITests/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 | -------------------------------------------------------------------------------- /Reader/LearnCoreTextUITests/LearnCoreTextUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LearnCoreTextUITests.m 3 | // LearnCoreTextUITests 4 | // 5 | // Created by loyinglin on 2018/10/24. 6 | // Copyright © 2018 loyinglin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface LearnCoreTextUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation LearnCoreTextUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 24 | [[[XCUIApplication alloc] init] launch]; 25 | 26 | // 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. 27 | } 28 | 29 | - (void)tearDown { 30 | // Put teardown code here. This method is called after the invocation of each test method in the class. 31 | } 32 | 33 | - (void)testExample { 34 | // Use recording to get started writing UI tests. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | @end 39 | --------------------------------------------------------------------------------