├── .gitignore ├── Example ├── Example.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── xiangkuilin.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ └── xcschememanagement.plist └── Example │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ └── LaunchScreen.storyboard │ ├── Info.plist │ ├── RootViewController.h │ ├── RootViewController.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── LICENSE ├── README.md ├── SKAutoScrollLabel.podspec └── Source └── SKAutoScrollLabel ├── SKAutoScrollLabel.h └── SKAutoScrollLabel.m /.gitignore: -------------------------------------------------------------------------------- 1 | #################################### 2 | ######## OS generated files ######## 3 | #################################### 4 | .DS_Store 5 | .DS_Store? 6 | *.swp 7 | ._* 8 | .Spotlight-V100 9 | .Trashes 10 | Icon? 11 | ehthumbs.db 12 | Thumbs.db 13 | #################################### 14 | ############# packages ############# 15 | #################################### 16 | *.7z 17 | *.dmg 18 | *.gz 19 | *.iso 20 | *.jar 21 | *.rar 22 | *.tar 23 | *.zip 24 | *.xcuserstate 25 | project.xcworkspace 26 | xcuserdata 27 | UserInterfaceState.xcuserstate 28 | project.xcworkspace/ 29 | xcuserdata/ 30 | UserInterface.xcuserstate 31 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9FEC8AB821FF44C9007B623A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC8AB721FF44C9007B623A /* AppDelegate.m */; }; 11 | 9FEC8ABB21FF44C9007B623A /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC8ABA21FF44C9007B623A /* ViewController.m */; }; 12 | 9FEC8AC021FF44D2007B623A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9FEC8ABF21FF44D2007B623A /* Assets.xcassets */; }; 13 | 9FEC8AC321FF44D2007B623A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9FEC8AC121FF44D2007B623A /* LaunchScreen.storyboard */; }; 14 | 9FEC8AC621FF44D2007B623A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC8AC521FF44D2007B623A /* main.m */; }; 15 | 9FEC8ACE21FF456D007B623A /* RootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC8ACC21FF456D007B623A /* RootViewController.m */; }; 16 | 9FEC8AD721FF459A007B623A /* SKAutoScrollLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC8AD621FF459A007B623A /* SKAutoScrollLabel.m */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXFileReference section */ 20 | 9FEC8AB321FF44C9007B623A /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 21 | 9FEC8AB621FF44C9007B623A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 22 | 9FEC8AB721FF44C9007B623A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 23 | 9FEC8AB921FF44C9007B623A /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 24 | 9FEC8ABA21FF44C9007B623A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 25 | 9FEC8ABF21FF44D2007B623A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | 9FEC8AC221FF44D2007B623A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 27 | 9FEC8AC421FF44D2007B623A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 28 | 9FEC8AC521FF44D2007B623A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 29 | 9FEC8ACC21FF456D007B623A /* RootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RootViewController.m; sourceTree = ""; }; 30 | 9FEC8ACD21FF456D007B623A /* RootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RootViewController.h; sourceTree = ""; }; 31 | 9FEC8AD521FF459A007B623A /* SKAutoScrollLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SKAutoScrollLabel.h; sourceTree = ""; }; 32 | 9FEC8AD621FF459A007B623A /* SKAutoScrollLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SKAutoScrollLabel.m; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 9FEC8AB021FF44C9007B623A /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 9FEC8AAA21FF44C9007B623A = { 47 | isa = PBXGroup; 48 | children = ( 49 | 9FEC8AB521FF44C9007B623A /* Example */, 50 | 9FEC8AB421FF44C9007B623A /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | 9FEC8AB421FF44C9007B623A /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | 9FEC8AB321FF44C9007B623A /* Example.app */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | 9FEC8AB521FF44C9007B623A /* Example */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 9FEC8AD421FF459A007B623A /* SKAutoScrollLabel */, 66 | 9FEC8AB621FF44C9007B623A /* AppDelegate.h */, 67 | 9FEC8AB721FF44C9007B623A /* AppDelegate.m */, 68 | 9FEC8ACD21FF456D007B623A /* RootViewController.h */, 69 | 9FEC8ACC21FF456D007B623A /* RootViewController.m */, 70 | 9FEC8AB921FF44C9007B623A /* ViewController.h */, 71 | 9FEC8ABA21FF44C9007B623A /* ViewController.m */, 72 | 9FEC8ABF21FF44D2007B623A /* Assets.xcassets */, 73 | 9FEC8AC121FF44D2007B623A /* LaunchScreen.storyboard */, 74 | 9FEC8AC421FF44D2007B623A /* Info.plist */, 75 | 9FEC8AC521FF44D2007B623A /* main.m */, 76 | ); 77 | path = Example; 78 | sourceTree = ""; 79 | }; 80 | 9FEC8AD421FF459A007B623A /* SKAutoScrollLabel */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | 9FEC8AD521FF459A007B623A /* SKAutoScrollLabel.h */, 84 | 9FEC8AD621FF459A007B623A /* SKAutoScrollLabel.m */, 85 | ); 86 | name = SKAutoScrollLabel; 87 | path = ../../Source/SKAutoScrollLabel; 88 | sourceTree = ""; 89 | }; 90 | /* End PBXGroup section */ 91 | 92 | /* Begin PBXNativeTarget section */ 93 | 9FEC8AB221FF44C9007B623A /* Example */ = { 94 | isa = PBXNativeTarget; 95 | buildConfigurationList = 9FEC8AC921FF44D2007B623A /* Build configuration list for PBXNativeTarget "Example" */; 96 | buildPhases = ( 97 | 9FEC8AAF21FF44C9007B623A /* Sources */, 98 | 9FEC8AB021FF44C9007B623A /* Frameworks */, 99 | 9FEC8AB121FF44C9007B623A /* Resources */, 100 | ); 101 | buildRules = ( 102 | ); 103 | dependencies = ( 104 | ); 105 | name = Example; 106 | productName = Example; 107 | productReference = 9FEC8AB321FF44C9007B623A /* Example.app */; 108 | productType = "com.apple.product-type.application"; 109 | }; 110 | /* End PBXNativeTarget section */ 111 | 112 | /* Begin PBXProject section */ 113 | 9FEC8AAB21FF44C9007B623A /* Project object */ = { 114 | isa = PBXProject; 115 | attributes = { 116 | LastUpgradeCheck = 1010; 117 | ORGANIZATIONNAME = ShevaKuilin; 118 | TargetAttributes = { 119 | 9FEC8AB221FF44C9007B623A = { 120 | CreatedOnToolsVersion = 10.1; 121 | }; 122 | }; 123 | }; 124 | buildConfigurationList = 9FEC8AAE21FF44C9007B623A /* Build configuration list for PBXProject "Example" */; 125 | compatibilityVersion = "Xcode 9.3"; 126 | developmentRegion = en; 127 | hasScannedForEncodings = 0; 128 | knownRegions = ( 129 | en, 130 | Base, 131 | ); 132 | mainGroup = 9FEC8AAA21FF44C9007B623A; 133 | productRefGroup = 9FEC8AB421FF44C9007B623A /* Products */; 134 | projectDirPath = ""; 135 | projectRoot = ""; 136 | targets = ( 137 | 9FEC8AB221FF44C9007B623A /* Example */, 138 | ); 139 | }; 140 | /* End PBXProject section */ 141 | 142 | /* Begin PBXResourcesBuildPhase section */ 143 | 9FEC8AB121FF44C9007B623A /* Resources */ = { 144 | isa = PBXResourcesBuildPhase; 145 | buildActionMask = 2147483647; 146 | files = ( 147 | 9FEC8AC321FF44D2007B623A /* LaunchScreen.storyboard in Resources */, 148 | 9FEC8AC021FF44D2007B623A /* Assets.xcassets in Resources */, 149 | ); 150 | runOnlyForDeploymentPostprocessing = 0; 151 | }; 152 | /* End PBXResourcesBuildPhase section */ 153 | 154 | /* Begin PBXSourcesBuildPhase section */ 155 | 9FEC8AAF21FF44C9007B623A /* Sources */ = { 156 | isa = PBXSourcesBuildPhase; 157 | buildActionMask = 2147483647; 158 | files = ( 159 | 9FEC8ABB21FF44C9007B623A /* ViewController.m in Sources */, 160 | 9FEC8AC621FF44D2007B623A /* main.m in Sources */, 161 | 9FEC8ACE21FF456D007B623A /* RootViewController.m in Sources */, 162 | 9FEC8AD721FF459A007B623A /* SKAutoScrollLabel.m in Sources */, 163 | 9FEC8AB821FF44C9007B623A /* AppDelegate.m in Sources */, 164 | ); 165 | runOnlyForDeploymentPostprocessing = 0; 166 | }; 167 | /* End PBXSourcesBuildPhase section */ 168 | 169 | /* Begin PBXVariantGroup section */ 170 | 9FEC8AC121FF44D2007B623A /* LaunchScreen.storyboard */ = { 171 | isa = PBXVariantGroup; 172 | children = ( 173 | 9FEC8AC221FF44D2007B623A /* Base */, 174 | ); 175 | name = LaunchScreen.storyboard; 176 | sourceTree = ""; 177 | }; 178 | /* End PBXVariantGroup section */ 179 | 180 | /* Begin XCBuildConfiguration section */ 181 | 9FEC8AC721FF44D2007B623A /* Debug */ = { 182 | isa = XCBuildConfiguration; 183 | buildSettings = { 184 | ALWAYS_SEARCH_USER_PATHS = NO; 185 | CLANG_ANALYZER_NONNULL = YES; 186 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 187 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 188 | CLANG_CXX_LIBRARY = "libc++"; 189 | CLANG_ENABLE_MODULES = YES; 190 | CLANG_ENABLE_OBJC_ARC = YES; 191 | CLANG_ENABLE_OBJC_WEAK = YES; 192 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 193 | CLANG_WARN_BOOL_CONVERSION = YES; 194 | CLANG_WARN_COMMA = YES; 195 | CLANG_WARN_CONSTANT_CONVERSION = YES; 196 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 197 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 198 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 199 | CLANG_WARN_EMPTY_BODY = YES; 200 | CLANG_WARN_ENUM_CONVERSION = YES; 201 | CLANG_WARN_INFINITE_RECURSION = YES; 202 | CLANG_WARN_INT_CONVERSION = YES; 203 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 204 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 205 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 206 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 207 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 208 | CLANG_WARN_STRICT_PROTOTYPES = YES; 209 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 210 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 211 | CLANG_WARN_UNREACHABLE_CODE = YES; 212 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 213 | CODE_SIGN_IDENTITY = "iPhone Developer"; 214 | COPY_PHASE_STRIP = NO; 215 | DEBUG_INFORMATION_FORMAT = dwarf; 216 | ENABLE_STRICT_OBJC_MSGSEND = YES; 217 | ENABLE_TESTABILITY = YES; 218 | GCC_C_LANGUAGE_STANDARD = gnu11; 219 | GCC_DYNAMIC_NO_PIC = NO; 220 | GCC_NO_COMMON_BLOCKS = YES; 221 | GCC_OPTIMIZATION_LEVEL = 0; 222 | GCC_PREPROCESSOR_DEFINITIONS = ( 223 | "DEBUG=1", 224 | "$(inherited)", 225 | ); 226 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 227 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 228 | GCC_WARN_UNDECLARED_SELECTOR = YES; 229 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 230 | GCC_WARN_UNUSED_FUNCTION = YES; 231 | GCC_WARN_UNUSED_VARIABLE = YES; 232 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 233 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 234 | MTL_FAST_MATH = YES; 235 | ONLY_ACTIVE_ARCH = YES; 236 | SDKROOT = iphoneos; 237 | }; 238 | name = Debug; 239 | }; 240 | 9FEC8AC821FF44D2007B623A /* Release */ = { 241 | isa = XCBuildConfiguration; 242 | buildSettings = { 243 | ALWAYS_SEARCH_USER_PATHS = NO; 244 | CLANG_ANALYZER_NONNULL = YES; 245 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 246 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 247 | CLANG_CXX_LIBRARY = "libc++"; 248 | CLANG_ENABLE_MODULES = YES; 249 | CLANG_ENABLE_OBJC_ARC = YES; 250 | CLANG_ENABLE_OBJC_WEAK = YES; 251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 252 | CLANG_WARN_BOOL_CONVERSION = YES; 253 | CLANG_WARN_COMMA = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 267 | CLANG_WARN_STRICT_PROTOTYPES = YES; 268 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 269 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | CODE_SIGN_IDENTITY = "iPhone Developer"; 273 | COPY_PHASE_STRIP = NO; 274 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 275 | ENABLE_NS_ASSERTIONS = NO; 276 | ENABLE_STRICT_OBJC_MSGSEND = YES; 277 | GCC_C_LANGUAGE_STANDARD = gnu11; 278 | GCC_NO_COMMON_BLOCKS = YES; 279 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 280 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 281 | GCC_WARN_UNDECLARED_SELECTOR = YES; 282 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 283 | GCC_WARN_UNUSED_FUNCTION = YES; 284 | GCC_WARN_UNUSED_VARIABLE = YES; 285 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 286 | MTL_ENABLE_DEBUG_INFO = NO; 287 | MTL_FAST_MATH = YES; 288 | SDKROOT = iphoneos; 289 | VALIDATE_PRODUCT = YES; 290 | }; 291 | name = Release; 292 | }; 293 | 9FEC8ACA21FF44D2007B623A /* Debug */ = { 294 | isa = XCBuildConfiguration; 295 | buildSettings = { 296 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 297 | CODE_SIGN_STYLE = Automatic; 298 | INFOPLIST_FILE = Example/Info.plist; 299 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 300 | LD_RUNPATH_SEARCH_PATHS = ( 301 | "$(inherited)", 302 | "@executable_path/Frameworks", 303 | ); 304 | PRODUCT_BUNDLE_IDENTIFIER = SK.Example; 305 | PRODUCT_NAME = "$(TARGET_NAME)"; 306 | TARGETED_DEVICE_FAMILY = "1,2"; 307 | }; 308 | name = Debug; 309 | }; 310 | 9FEC8ACB21FF44D2007B623A /* Release */ = { 311 | isa = XCBuildConfiguration; 312 | buildSettings = { 313 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 314 | CODE_SIGN_STYLE = Automatic; 315 | INFOPLIST_FILE = Example/Info.plist; 316 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 317 | LD_RUNPATH_SEARCH_PATHS = ( 318 | "$(inherited)", 319 | "@executable_path/Frameworks", 320 | ); 321 | PRODUCT_BUNDLE_IDENTIFIER = SK.Example; 322 | PRODUCT_NAME = "$(TARGET_NAME)"; 323 | TARGETED_DEVICE_FAMILY = "1,2"; 324 | }; 325 | name = Release; 326 | }; 327 | /* End XCBuildConfiguration section */ 328 | 329 | /* Begin XCConfigurationList section */ 330 | 9FEC8AAE21FF44C9007B623A /* Build configuration list for PBXProject "Example" */ = { 331 | isa = XCConfigurationList; 332 | buildConfigurations = ( 333 | 9FEC8AC721FF44D2007B623A /* Debug */, 334 | 9FEC8AC821FF44D2007B623A /* Release */, 335 | ); 336 | defaultConfigurationIsVisible = 0; 337 | defaultConfigurationName = Release; 338 | }; 339 | 9FEC8AC921FF44D2007B623A /* Build configuration list for PBXNativeTarget "Example" */ = { 340 | isa = XCConfigurationList; 341 | buildConfigurations = ( 342 | 9FEC8ACA21FF44D2007B623A /* Debug */, 343 | 9FEC8ACB21FF44D2007B623A /* Release */, 344 | ); 345 | defaultConfigurationIsVisible = 0; 346 | defaultConfigurationName = Release; 347 | }; 348 | /* End XCConfigurationList section */ 349 | }; 350 | rootObject = 9FEC8AAB21FF44C9007B623A /* Project object */; 351 | } 352 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/xiangkuilin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcuserdata/xiangkuilin.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Example.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CADisplayLinkeDemo 4 | // 5 | // Created by ShevaKuilin on 2018/5/4. 6 | // Copyright © 2018年 ShevaKuilin. 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 | -------------------------------------------------------------------------------- /Example/Example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CADisplayLinkeDemo 4 | // 5 | // Created by ShevaKuilin on 2018/5/4. 6 | // Copyright © 2018年 ShevaKuilin. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "RootViewController.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; 21 | self.window.backgroundColor = [UIColor whiteColor]; 22 | self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController: [[RootViewController alloc] init]]; 23 | [self.window makeKeyAndVisible]; 24 | 25 | return YES; 26 | } 27 | 28 | 29 | - (void)applicationWillResignActive:(UIApplication *)application { 30 | // 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. 31 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 32 | } 33 | 34 | 35 | - (void)applicationDidEnterBackground:(UIApplication *)application { 36 | // 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. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | 41 | - (void)applicationWillEnterForeground:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationDidBecomeActive:(UIApplication *)application { 47 | // 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. 48 | } 49 | 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /Example/Example/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 | } -------------------------------------------------------------------------------- /Example/Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Example/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 | -------------------------------------------------------------------------------- /Example/Example/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 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Example/Example/RootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.h 3 | // CADisplayLinkeDemo 4 | // 5 | // Created by ShevaKuilin on 2018/5/8. 6 | // Copyright © 2018年 ShevaKuilin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RootViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Example/RootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.m 3 | // CADisplayLinkeDemo 4 | // 5 | // Created by ShevaKuilin on 2018/5/8. 6 | // Copyright © 2018年 ShevaKuilin. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "RootViewController.h" 11 | 12 | static NSString *const ReuseIdentifier = @"cell"; 13 | 14 | @interface RootViewController () 15 | 16 | @property (nonatomic, strong) UITableView *optionListView; 17 | @property (nonatomic, strong) NSArray *optionList; 18 | 19 | @end 20 | 21 | @implementation RootViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | [self initConfig]; 26 | [self initElement]; 27 | } 28 | 29 | - (void)viewDidLayoutSubviews { 30 | [super viewDidLayoutSubviews]; 31 | self.optionListView.frame = self.view.bounds; 32 | } 33 | 34 | - (void)initConfig { 35 | self.title = @"CHOOSE SCROLL TYPE"; 36 | self.optionList = @[@"RIGHT", @"LEFT", @"TOP", @"BOTTOM"]; 37 | } 38 | 39 | - (void)initElement { 40 | self.optionListView = [[UITableView alloc] init]; 41 | self.optionListView.delegate = self; 42 | self.optionListView.dataSource = self; 43 | self.optionListView.showsVerticalScrollIndicator = false; 44 | self.optionListView.scrollEnabled = false; 45 | self.optionListView.tableFooterView = [UIView new]; 46 | [self.view addSubview:self.optionListView]; 47 | 48 | [self registerCell]; 49 | } 50 | 51 | - (void)registerCell { 52 | [self.optionListView registerClass:[UITableViewCell class] forCellReuseIdentifier:ReuseIdentifier]; 53 | } 54 | 55 | - (void)toNextVcWithIndex:(NSInteger)index { 56 | ViewController *nextVc = [[ViewController alloc] init]; 57 | nextVc.scrollType = index; 58 | [self.navigationController showViewController:nextVc sender:nil]; 59 | } 60 | 61 | #pragma mark - 62 | 63 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 64 | return self.optionList.count; 65 | } 66 | 67 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 68 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ReuseIdentifier]; 69 | cell.textLabel.text = self.optionList[indexPath.row]; 70 | cell.textLabel.textAlignment = NSTextAlignmentCenter; 71 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 72 | 73 | return cell; 74 | } 75 | 76 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 77 | [self toNextVcWithIndex:indexPath.row]; 78 | } 79 | 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Example/Example/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CADisplayLinkeDemo 4 | // 5 | // Created by ShevaKuilin on 2018/5/4. 6 | // Copyright © 2018年 ShevaKuilin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @property (nonatomic, assign) NSUInteger scrollType; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /Example/Example/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CADisplayLinkeDemo 4 | // 5 | // Created by ShevaKuilin on 2018/5/4. 6 | // Copyright © 2018年 ShevaKuilin. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "SKAutoScrollLabel.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) SKAutoScrollLabel *scrollLabel; 15 | @property (nonatomic, strong) UIButton *pauseBtn; 16 | 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | [self initElements]; 24 | } 25 | 26 | - (void)initElements { 27 | self.view.backgroundColor = [UIColor whiteColor]; 28 | 29 | self.scrollLabel = [[SKAutoScrollLabel alloc] initWithTextContent:@"你指尖跃动的电光, 是我此生不灭的信仰! 唯我超电磁炮永世长存!! 哔哩哔哩(゜-゜)つロ干杯~-bilibili" direction:self.scrollType]; 30 | if (self.scrollType == 0 || self.scrollType == 1) { 31 | self.scrollLabel.frame = CGRectMake(10, 32 | ([UIScreen mainScreen].bounds.size.height / 2) - 30, 33 | [UIScreen mainScreen].bounds.size.width - 20, 34 | 60); 35 | } else { 36 | self.scrollLabel.frame = CGRectMake(self.view.center.x - 30, 37 | 100, 38 | 60, 39 | self.view.frame.size.height / 2 - 10); 40 | } 41 | self.scrollLabel.textColor = [UIColor whiteColor]; 42 | self.scrollLabel.backgroundColor = [UIColor colorWithRed:253/255.0 43 | green:172/255.0 44 | blue:201/255.0 45 | alpha:1.0]; 46 | [self.view addSubview:self.scrollLabel]; 47 | 48 | self.pauseBtn = [[UIButton alloc] init]; 49 | [self.pauseBtn setTitle:@"PAUSE" forState:UIControlStateNormal]; 50 | [self.pauseBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 51 | [self.pauseBtn setTitle:@"CONTINUE" forState:UIControlStateSelected]; 52 | [self.pauseBtn setTitleColor:[UIColor brownColor] forState:UIControlStateSelected]; 53 | self.pauseBtn.titleLabel.font = [UIFont boldSystemFontOfSize:20]; 54 | self.pauseBtn.frame = CGRectMake(self.view.center.x - 100, 55 | self.view.center.y + 100, 56 | 200, 57 | 50); 58 | self.pauseBtn.selected = false; 59 | [self.pauseBtn addTarget:self action:@selector(clickActionWithBtn:) forControlEvents:UIControlEventTouchUpInside]; 60 | [self.view addSubview:self.pauseBtn]; 61 | } 62 | 63 | - (void)clickActionWithBtn:(UIButton *)btn { 64 | self.pauseBtn.selected = !btn.selected; 65 | if (self.pauseBtn.selected) { 66 | [self.scrollLabel pauseScroll]; 67 | } else { 68 | [self.scrollLabel continueScroll]; 69 | } 70 | } 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /Example/Example/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Example 4 | // 5 | // Created by ShevaKuilin on 2019/1/28. 6 | // Copyright © 2019 ShevaKuilin. 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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ShevaKuilin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SKAutoScrollLabel 2 | 3 | ![](https://img.shields.io/badge/platform-iOS-green.svg) 4 | ![](https://img.shields.io/badge/pod-v1.6.0.beta.1-blue.svg) 5 | ![](https://img.shields.io/badge/language-ObjectiveC-purple.svg) 6 | ![](https://img.shields.io/badge/version-v0.0.6-red.svg) 7 | 8 | Automatically scrolling UILabel with both horizontal/vertical MARQUEE effects and gradient gradients on the edges.Gradient fading is used on the edge of the scroll to solve the problem of the hard edges of the rolling edge. The overall effect is a natural and easy to use. 9 | 10 | 11 | # Feature 12 | 13 | - Supports automatic scrolling in four directions: up, down, left, and right. 14 | - Freely controllable scroll pause/continue. 15 | - Scrolling behavior is not interrupted when applying background switching. 16 | - Lightweight, at least one line of initialization code is required to create. 17 | - Control gradient effect is softer and more natural. 18 | - Fully comment and instructions. 19 | 20 | # Using effect 21 | 22 | 23 | 24 | # How to use 25 | 26 | Step 1: Execute `git clone git@github.com:shevakuilin/SKAutoScrollLabel.git`,then view `Example`. 27 | 28 | 29 | Step 2: Copy the `SKAutoScrollLabel` from the directory directly into your project, or add `pod 'SKAutoScrollLabel'` to the Podfile. 30 | 31 | Step 3: Reference header file `#import ` 32 | 33 | # Installation 34 | 35 | #### Created by code 36 | 37 | ```objectivec 38 | // Scroll from right to left 39 | SKAutoScrollLabel *scrollLabel = [[SKAutoScrollLabel alloc] initWithTextContent:@"Fly me to the moon.Let me play among the stars.Let me see what spring is like on Jupiter and Mars.In other words, hold my hand.In other words, baby, kiss me." direction: SK_AUTOSCROLL_DIRECTION_LEFT]; 40 | scrollLabel.backgroundColor = [UIColor orangeColor]; 41 | scrollLabel.textColor = [UIColor whiteColor]; 42 | // ... other settings 43 | ``` 44 | 45 | #### Created by storyboard 46 | 47 | ```objectivec 48 | // Set the object's class to SKAutoScrollLabel in the storyboard 49 | @property (weak, nonatomic) IBOutlet SKAutoScrollLabel *scrollLabel; 50 | 51 | // Scroll from right to left 52 | self.scrollLabel.textContent = @"Fly me to the moon.Let me play among the stars.Let me see what spring is like on Jupiter and Mars.In other words, hold my hand.In other words, baby, kiss me."; 53 | self.scrollLabel.direction = SK_AUTOSCROLL_DIRECTION_LEFT; 54 | self.scrollLabel.backgroundColor = [UIColor orangeColor]; 55 | self.scrollLabel.textColor = [UIColor whiteColor]; 56 | // ... other settings 57 | ``` 58 | 59 | # Parameter meaning 60 | 61 | - `direction` scrolling direction 62 | - SK_AUTOSCROLL_DIRECTION_RIGHT // Scroll from left to right 63 | - SK_AUTOSCROLL_DIRECTION_LEFT // Scroll from right to left 64 | - SK_AUTOSCROLL_DIRECTION_TOP // Scroll from bottom to top 65 | - SK_AUTOSCROLL_DIRECTION_BOTTOM // Scroll from top to bottom 66 | 67 | 68 | - `pointsPerFrame` The distance each frame moves.The default vaule is 1.0f. 69 | 70 | - `labelSpacing` The spacing of each scrolling label.The default vaule is 20. 71 | 72 | - `textContent` Plain text content. 73 | 74 | - `attributedTextContent` Rich text content. 75 | 76 | - `textColor` Plain text color. 77 | 78 | - `font` Plain text font. 79 | 80 | - `textAlignment` Plain text alignment. 81 | 82 | - `enableFade` Default YES. Enable gradients of lable boundaries to fade. 83 | 84 | # Control Method 85 | 86 | ```objc 87 | - (void)pauseScroll; // Pause scrolling animation being played. 88 | ``` 89 | 90 | ```objc 91 | - (void)continueScroll; // Make a paused scrolling animation continue playing. 92 | ``` 93 | 94 | 95 | ------ 96 | 97 | # 简述 98 | 99 | 100 | SKAutoScrollLabel是一个同时支持水平/垂直两种类型的“跑马灯”效果的自动滚动UILabel。在滚动的边缘使用了梯度褪色来解决滚动边缘生硬的效果问题,总体效果呈现出混然天成的感觉,并且使用简单方便。如果你觉得还不错,请star支持一下吧~ 101 | 102 | # 特性 103 | 104 | - 支持上、下、左、右四个方向的自动滚动 105 | - 随时可自由控制的滚动暂停 / 继续 106 | - 应用前后台切换时,滚动行为不会被中断 107 | - 轻量级,最少仅需一行初始化代码即可完成创建 108 | - 控件梯度渐变效果柔和,更自然 109 | - 完善的注释与说明 110 | 111 | 112 | ### 效果图 113 | 114 | 115 | 116 | # 如何开始 117 | 118 | 1.`git clone git@github.com:shevakuilin/SKAutoScrollLabel.git`,查看示例工程 Example 119 | 120 | 2.直接将目录下的 SKAutoScrollLabel 拷贝到你的工程中,或在Podfile文件中添加 ```pod 'SKAutoScrollLabel'``` 121 | 122 | 3.引用头文件 `#import ` 123 | 124 | # 初始化 125 | 126 | #### 通过代码创建 127 | 128 | ```objectivec 129 | // 创建一个从右向左滚动的,背景颜色为橙色,字体颜色为白色的滚动 Label 130 | SKAutoScrollLabel *scrollLabel = [[SKAutoScrollLabel alloc] initWithTextContent:@"你指尖跃动的电光, 是我此生不灭的信仰! 唯我超电磁炮永世长存!! 哔哩哔哩(゜-゜)つロ干杯~-bilibili" direction: SK_AUTOSCROLL_DIRECTION_LEFT]; 131 | scrollLabel.backgroundColor = [UIColor orangeColor]; 132 | scrollLabel.textColor = [UIColor whiteColor]; 133 | // ... 其他设置 134 | ``` 135 | 136 | #### 通过 storyboard 创建 137 | 138 | ```objectivec 139 | // 在 storyboard 中将对象的类设置为 SKAutoScrollLabel 140 | @property (weak, nonatomic) IBOutlet SKAutoScrollLabel *scrollLabel; 141 | 142 | // 创建一个从右向左滚动的,背景颜色为橙色,字体颜色为白色的滚动 Label 143 | self.scrollLabel.textContent = @"你指尖跃动的电光, 是我此生不灭的信仰! 唯我超电磁炮永世长存!! 哔哩哔哩(゜-゜)つロ干杯~-bilibili"; 144 | self.scrollLabel.direction = SK_AUTOSCROLL_DIRECTION_LEFT; 145 | self.scrollLabel.backgroundColor = [UIColor orangeColor]; 146 | self.scrollLabel.textColor = [UIColor whiteColor]; 147 | // ... 其他设置 148 | ``` 149 | 150 | # 基本参数 151 | 152 | - `direction` 滚动方向 153 | - SK_AUTOSCROLL_DIRECTION_RIGHT // 从左向右滚动, 默认选项 154 | - SK_AUTOSCROLL_DIRECTION_LEFT // 从右向左滚动 155 | - SK_AUTOSCROLL_DIRECTION_TOP // 从下向上滚动 156 | - SK_AUTOSCROLL_DIRECTION_BOTTOM // 从上向下滚动 157 | 158 | 159 | - `pointsPerFrame` 每帧移动的距离。默认值为1.0f。 160 | 161 | - `labelSpacing` 每个滚动标签的间距。默认值为20。 162 | 163 | - `textContent` 普通文本内容 164 | 165 | - `attributedTextContent` 富文本内容 166 | 167 | - `textColor` 文本颜色 168 | 169 | - `font` 字体 170 | 171 | - `textAlignment` 文字对齐 172 | 173 | - `enableFade` 开关梯度渐变,默认开启 174 | 175 | # 控制方法 176 | 177 | ```objc 178 | - (void)pauseScroll; // 暂停滚动 179 | ``` 180 | 181 | ```objc 182 | - (void)continueScroll; // 继续滚动 183 | ``` 184 | 185 | ### 感谢你花时间阅读以上内容, 如果这个项目能够帮助到你,记得告诉我 186 | 187 | 188 | Email: shevakuilin@gmail.com 189 | -------------------------------------------------------------------------------- /SKAutoScrollLabel.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SKAutoScrollLabel" 3 | s.version = "0.0.6" 4 | s.summary = "水平/垂直跑马灯" 5 | s.description = <<-DESC 6 | Automatically scrolling UILabel with both horizontal/vertical MARQUEE effects and gradient gradients on the edges 7 | DESC 8 | s.homepage = "https://github.com/shevakuilin/SKAutoScrollLabel" 9 | s.license = { :type => "MIT", :file => "LICENSE" } 10 | s.author = { "ShevaKuilin" => "shevakuilin@gmail.com" } 11 | s.platform = :ios, "8.0" 12 | s.source = { :git => "https://github.com/shevakuilin/SKAutoScrollLabel.git", :tag => s.version.to_s } 13 | s.source_files = "Source/SKAutoScrollLabel/*.{h,m}" 14 | end -------------------------------------------------------------------------------- /Source/SKAutoScrollLabel/SKAutoScrollLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // SKAutoScrollLabel.h 3 | // CADisplayLinkeDemo 4 | // 5 | // Created by ShevaKuilin on 2018/5/4. 6 | // Copyright © 2018年 ShevaKuilin. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** The following enumeration controls the scrolling direction of the text. 12 | */ 13 | typedef NS_ENUM(NSInteger, SK_AUTOSCROLL_DIRECTION){ 14 | /** 15 | * @abstract Scroll from left to right 16 | */ 17 | SK_AUTOSCROLL_DIRECTION_RIGHT, 18 | /** 19 | * @abstract Scroll from right to left 20 | */ 21 | SK_AUTOSCROLL_DIRECTION_LEFT, 22 | /** 23 | * @abstract Scroll from bottom to top 24 | */ 25 | SK_AUTOSCROLL_DIRECTION_TOP, 26 | /** 27 | * @abstract Scroll from top to bottom 28 | */ 29 | SK_AUTOSCROLL_DIRECTION_BOTTOM 30 | }; 31 | 32 | @interface SKAutoScrollLabel : UIView 33 | 34 | /** 35 | * Initializes an `SKAutoScrollLabel` object in current viewController. 36 | * 37 | * This is the designated initializer. 38 | * 39 | * @param textContent Fill in the text content that needs to be scrolled here. 40 | * @param direction Direction of text content scrolling. The default type is SK_AUTOSCROLL_DIRECTION_RIGHT. 41 | */ 42 | - (instancetype)initWithTextContent:(NSString * _Nonnull)textContent 43 | direction:(SK_AUTOSCROLL_DIRECTION)direction; 44 | 45 | /** 46 | * Initializes an `SKAutoScrollLabel` object of rich text type in current viewController. 47 | * 48 | * This is the designated initializer. 49 | * 50 | * @param attributedTextContent Fill in the rich text content that needs to be scrolled here. 51 | * @param direction Direction of text content scrolling. The default type is SK_AUTOSCROLL_DIRECTION_RIGHT. 52 | */ 53 | - (instancetype)initWithAttributedTextContent:(NSAttributedString * _Nonnull)attributedTextContent 54 | direction:(SK_AUTOSCROLL_DIRECTION)direction; 55 | 56 | /** 57 | * Direction of text content scrolling.The default type is SK_AUTOSCROLL_DIRECTION_RIGHT. 58 | */ 59 | @property (nonatomic, assign) SK_AUTOSCROLL_DIRECTION direction; 60 | 61 | /** 62 | * The distance each frame moves.The default vaule is 1.0f. 63 | */ 64 | @property (nonatomic, assign) CGFloat pointsPerFrame; 65 | 66 | /** 67 | * The spacing of each scrolling label.The default vaule is 20. 68 | * 69 | * @discussion When the text content is scrolling, it is actually the same label that does the repeated loop animation. This property 70 | * determines the spacing between the label to be displayed and the label that will disappear each time the animation is repeated, 71 | * that is, the spacing between the labels of the two looping animations. 72 | * 73 | */ 74 | @property (nonatomic, assign) NSUInteger labelSpacing; 75 | 76 | /** 77 | * Plain text content. 78 | */ 79 | @property (nonatomic, copy, nonnull) NSString *textContent; 80 | 81 | /** 82 | * Rich text content. 83 | */ 84 | @property (nonatomic, copy, nonnull) NSAttributedString *attributedTextContent; 85 | 86 | /** 87 | * Plain text color. 88 | */ 89 | @property (nonatomic, strong) UIColor *textColor; 90 | 91 | /** 92 | * Plain text font. 93 | */ 94 | @property (nonatomic, strong) UIFont *font; 95 | 96 | /** 97 | * Plain text alignment. 98 | */ 99 | @property (nonatomic) NSTextAlignment textAlignment; 100 | 101 | /** 102 | * Default YES. Enable gradients of lable boundaries to fade. 103 | */ 104 | @property (nonatomic, assign) BOOL enableFade; 105 | 106 | /** 107 | * Pause scrolling animation being played. 108 | */ 109 | - (void)pauseScroll; 110 | 111 | /** 112 | * Make a paused scrolling animation continue playing. 113 | */ 114 | - (void)continueScroll; 115 | 116 | @end 117 | -------------------------------------------------------------------------------- /Source/SKAutoScrollLabel/SKAutoScrollLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // SKAutoScrollLabel.m 3 | // CADisplayLinkeDemo 4 | // 5 | // Created by ShevaKuilin on 2018/5/4. 6 | // Copyright © 2018年 ShevaKuilin. All rights reserved. 7 | // 8 | 9 | #import "SKAutoScrollLabel.h" 10 | 11 | static const CGFloat kDefaultFadeLength = 7.f; 12 | 13 | @interface SKAutoScrollLabel() 14 | 15 | @property (nonatomic, strong) CADisplayLink *displayLinke; 16 | @property (nonatomic, strong) UIView *containerView; 17 | @property (nonatomic, strong) UILabel *animationLabel; 18 | @property (nonatomic, assign) NSUInteger layoutCount; 19 | @property (nonatomic, assign) BOOL enableScroll; 20 | 21 | @end 22 | 23 | @implementation SKAutoScrollLabel 24 | 25 | #pragma mark - Initializes 26 | 27 | - (instancetype)init { 28 | self = [super init]; 29 | if (self) { 30 | [self _init]; 31 | } 32 | return self; 33 | } 34 | 35 | - (instancetype)initWithFrame:(CGRect)frame { 36 | self = [super initWithFrame:frame]; 37 | if (self) { 38 | [self _init]; 39 | } 40 | return self; 41 | } 42 | 43 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 44 | self = [super initWithCoder:aDecoder]; 45 | if (self) { 46 | [self _init]; 47 | } 48 | return self; 49 | } 50 | 51 | - (instancetype)initWithTextContent:(NSString * _Nonnull)textContent 52 | direction:(SK_AUTOSCROLL_DIRECTION)direction { 53 | self = [super init]; 54 | if (self) { 55 | [self _init]; 56 | self.textContent = textContent; 57 | self.direction = direction; 58 | } 59 | return self; 60 | } 61 | 62 | - (instancetype)initWithAttributedTextContent:(NSAttributedString * _Nonnull)attributedTextContent 63 | direction:(SK_AUTOSCROLL_DIRECTION)direction { 64 | self = [super init]; 65 | if (self) { 66 | [self _init]; 67 | self.attributedTextContent = attributedTextContent; 68 | self.direction = direction; 69 | } 70 | return self; 71 | } 72 | 73 | - (void)_init { 74 | self.direction = SK_AUTOSCROLL_DIRECTION_RIGHT; 75 | self.pointsPerFrame = 1.0f; 76 | self.labelSpacing = 20; 77 | self.enableFade = YES; 78 | } 79 | 80 | - (void)initElements { 81 | self.clipsToBounds = true; // Make sure the label does not exceed the border of the container 82 | 83 | self.containerView = [[UIView alloc] init]; 84 | self.containerView.backgroundColor = self.backgroundColor; 85 | [self addSubview:self.containerView]; 86 | 87 | self.animationLabel = [[UILabel alloc] init]; 88 | [self updateAnimationLabel]; 89 | [self.containerView addSubview:self.animationLabel]; 90 | 91 | self.animationLabel.frame = [self fixLabelFrameWithDirection:self.direction]; 92 | if (self.enableScroll) { 93 | // Set frame 94 | [self initContainerFrame]; 95 | // If the text width exceeds the width of the container, copy the same view 96 | [self createRepeatLabel]; 97 | } else { 98 | self.animationLabel.frame = CGRectMake(self.bounds.size.width / 2 - self.animationLabel.frame.size.width / 2, self.bounds.size.height / 2 - self.animationLabel.frame.size.height / 2, self.animationLabel.frame.size.width, self.animationLabel.frame.size.height); 99 | [self applyGradientMask]; 100 | } 101 | } 102 | 103 | - (void)initContainerFrame { 104 | switch (self.direction) { 105 | case SK_AUTOSCROLL_DIRECTION_RIGHT: 106 | self.containerView.frame = CGRectMake(self.bounds.size.width - (self.animationLabel.bounds.size.width * 2 + self.labelSpacing), 107 | 0, 108 | self.animationLabel.bounds.size.width * 2 + self.labelSpacing, 109 | self.bounds.size.height); 110 | break; 111 | case SK_AUTOSCROLL_DIRECTION_LEFT: 112 | self.containerView.frame = CGRectMake(0, 113 | 0, 114 | self.animationLabel.bounds.size.width * 2 + self.labelSpacing, 115 | self.bounds.size.height); 116 | break; 117 | case SK_AUTOSCROLL_DIRECTION_TOP: 118 | self.containerView.frame = CGRectMake(self.bounds.origin.x, 119 | self.bounds.origin.y, 120 | self.bounds.size.width, 121 | self.animationLabel.frame.size.height * 2 + self.labelSpacing); 122 | break; 123 | case SK_AUTOSCROLL_DIRECTION_BOTTOM: 124 | self.containerView.frame = CGRectMake(self.bounds.origin.x, 125 | -(self.animationLabel.frame.size.height * 2 + self.labelSpacing - self.frame.size.height), 126 | self.bounds.size.width, 127 | self.animationLabel.bounds.size.height * 2 + self.labelSpacing); 128 | break; 129 | 130 | default: 131 | break; 132 | } 133 | } 134 | 135 | - (void)updateAnimationLabel { 136 | self.animationLabel.text = self.textContent; 137 | self.animationLabel.textColor = self.textColor; 138 | self.animationLabel.font = self.font; 139 | self.animationLabel.numberOfLines = 0; 140 | self.animationLabel.textAlignment = self.textAlignment; 141 | if (self.attributedTextContent) { 142 | self.animationLabel.attributedText = self.attributedTextContent; 143 | } 144 | [self.animationLabel sizeToFit]; 145 | } 146 | 147 | - (void)layoutSubviews { 148 | [super layoutSubviews]; 149 | if (self.layoutCount < 1) { 150 | [self initElements]; 151 | [self creatDisplayLink]; 152 | self.layoutCount++; 153 | } 154 | } 155 | 156 | - (void)willMoveToSuperview:(UIView *)newSuperview { 157 | [super willMoveToSuperview:newSuperview]; 158 | if (!newSuperview) { 159 | [self stopDisplayLinke]; 160 | } 161 | } 162 | 163 | #pragma mark - Create repeatLabel 164 | 165 | - (void)createRepeatLabel { 166 | NSData *repeatLabelData = [NSKeyedArchiver archivedDataWithRootObject:self.animationLabel];// copy the animationLabel's data 167 | UILabel *repeatLabel = [NSKeyedUnarchiver unarchiveObjectWithData:repeatLabelData]; 168 | 169 | if (self.direction == SK_AUTOSCROLL_DIRECTION_TOP || self.direction == SK_AUTOSCROLL_DIRECTION_BOTTOM) { 170 | repeatLabel.frame = CGRectMake(self.animationLabel.bounds.origin.x, 171 | self.animationLabel.frame.origin.y + self.animationLabel.bounds.size.height + self.labelSpacing, 172 | self.animationLabel.bounds.size.width, 173 | self.animationLabel.bounds.size.height); 174 | repeatLabel.center = CGPointMake(self.animationLabel.center.x, repeatLabel.center.y); 175 | } else { 176 | repeatLabel.frame = CGRectMake(self.animationLabel.frame.origin.x + self.animationLabel.bounds.size.width + self.labelSpacing, 177 | self.animationLabel.bounds.origin.y, 178 | self.animationLabel.bounds.size.width, 179 | self.animationLabel.bounds.size.height); 180 | repeatLabel.center = CGPointMake(repeatLabel.center.x, self.animationLabel.center.y); 181 | } 182 | 183 | [self.containerView addSubview:repeatLabel]; 184 | } 185 | 186 | #pragma mark - Fix the frame of the label based on the scroll direction 187 | 188 | - (CGRect)fixLabelFrameWithDirection:(SK_AUTOSCROLL_DIRECTION)direction { 189 | if (direction == SK_AUTOSCROLL_DIRECTION_LEFT || direction == SK_AUTOSCROLL_DIRECTION_RIGHT) { 190 | return CGRectMake(0, 191 | self.bounds.size.height / 2 - self.animationLabel.frame.size.height / 2, 192 | self.animationLabel.frame.size.width, 193 | self.animationLabel.frame.size.height); 194 | } else { 195 | CGRect frame = self.animationLabel.frame; 196 | NSDictionary *attributeDic = [NSDictionary dictionaryWithObjectsAndKeys:self.animationLabel.font, NSFontAttributeName, nil];; 197 | CGFloat wordWidth = self.animationLabel.font.pointSize; 198 | CGSize textContentSize = [self.animationLabel.text boundingRectWithSize:CGSizeMake(wordWidth, CGFLOAT_MAX) 199 | options:NSStringDrawingUsesFontLeading | NSStringDrawingUsesLineFragmentOrigin 200 | attributes:attributeDic 201 | context:nil].size; 202 | CGFloat wordHeight = textContentSize.height; 203 | if (direction == SK_AUTOSCROLL_DIRECTION_TOP) { 204 | frame.origin = CGPointMake(self.frame.size.width / 2 - wordWidth / 2, 0); 205 | } else { 206 | frame.origin = CGPointMake(self.frame.size.width / 2 - wordWidth / 2, 0); 207 | } 208 | frame.size = CGSizeMake(wordWidth, wordHeight); 209 | 210 | return frame; 211 | } 212 | } 213 | 214 | #pragma mark - DisplayLink control 215 | 216 | - (void)creatDisplayLink { 217 | if (self.enableScroll) { 218 | self.displayLinke = [CADisplayLink displayLinkWithTarget:self selector:@selector(processDisplayLink)]; 219 | [self.displayLinke addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; 220 | } 221 | } 222 | 223 | - (void)processDisplayLink { 224 | CGRect frame = self.containerView.frame; 225 | CGFloat offsetX = self.animationLabel.bounds.size.width + self.labelSpacing; 226 | CGFloat offsetY = self.animationLabel.bounds.size.height + self.labelSpacing; 227 | CGFloat bottomDirectionY = -(offsetY - self.frame.size.height - self.labelSpacing); 228 | switch (self.direction) { 229 | case SK_AUTOSCROLL_DIRECTION_RIGHT: 230 | if (frame.origin.x >= -(offsetX - self.frame.size.width - self.labelSpacing)) { 231 | frame.origin.x = -(self.animationLabel.frame.size.width * 2 + self.labelSpacing - self.frame.size.width); 232 | } else { 233 | frame.origin.x += self.pointsPerFrame; 234 | if (frame.origin.x > -(offsetX - self.frame.size.width - self.labelSpacing)) { 235 | frame.origin.x = -(offsetX - self.frame.size.width - self.labelSpacing); 236 | } 237 | } 238 | 239 | break; 240 | case SK_AUTOSCROLL_DIRECTION_LEFT: 241 | if (frame.origin.x <= -offsetX) { 242 | frame.origin.x = 0; 243 | } else { 244 | frame.origin.x -= self.pointsPerFrame; 245 | if (frame.origin.x < -offsetX) { 246 | frame.origin.x = -offsetX; 247 | } 248 | } 249 | 250 | break; 251 | case SK_AUTOSCROLL_DIRECTION_TOP: 252 | if (frame.origin.y <= -offsetY) { 253 | frame.origin.y = 0; 254 | } else { 255 | frame.origin.y -= self.pointsPerFrame; 256 | if (frame.origin.y < -offsetY) { 257 | frame.origin.y = -offsetY; 258 | } 259 | } 260 | 261 | break; 262 | case SK_AUTOSCROLL_DIRECTION_BOTTOM: 263 | if (frame.origin.y >= bottomDirectionY) { 264 | frame.origin.y = -(self.animationLabel.frame.size.height * 2 + self.labelSpacing - self.frame.size.height); 265 | } else { 266 | frame.origin.y += self.pointsPerFrame; 267 | if (frame.origin.y > bottomDirectionY) { 268 | frame.origin.y = bottomDirectionY; 269 | } 270 | } 271 | break; 272 | 273 | default: 274 | break; 275 | } 276 | 277 | self.containerView.frame = frame; 278 | [self applyGradientMask]; 279 | } 280 | 281 | - (void)stopDisplayLinke { 282 | [self.displayLinke invalidate]; 283 | self.displayLinke = nil; 284 | } 285 | 286 | #pragma mark - Setter 287 | 288 | - (void)setDirection:(SK_AUTOSCROLL_DIRECTION)direction { 289 | _direction = direction; 290 | } 291 | 292 | - (void)setPointsPerFrame:(CGFloat)pointsPerFrame { 293 | _pointsPerFrame = pointsPerFrame; 294 | } 295 | 296 | - (void)setLabelSpacing:(NSUInteger)labelSpacing { 297 | _labelSpacing = labelSpacing; 298 | } 299 | 300 | - (void)setTextContent:(NSString *)textContent { 301 | _textContent = textContent; 302 | [self updateAnimationLabel]; 303 | } 304 | 305 | - (void)setAttributedTextContent:(NSAttributedString *)attributedTextContent { 306 | _attributedTextContent = attributedTextContent; 307 | [self updateAnimationLabel]; 308 | } 309 | 310 | - (void)setTextColor:(UIColor *)textColor { 311 | _textColor = textColor; 312 | [self updateAnimationLabel]; 313 | } 314 | 315 | - (void)setFont:(UIFont *)font { 316 | _font = font; 317 | [self updateAnimationLabel]; 318 | } 319 | 320 | - (void)setTextAlignment:(NSTextAlignment)textAlignment { 321 | _textAlignment = textAlignment; 322 | [self updateAnimationLabel]; 323 | } 324 | 325 | - (void)setEnableFade:(BOOL)enableFade { 326 | _enableFade = enableFade; 327 | } 328 | 329 | #pragma mark - Getter 330 | 331 | - (BOOL)enableScroll { 332 | BOOL morethanWidth = self.animationLabel.bounds.size.width > self.bounds.size.width ? YES:NO; 333 | morethanWidth = self.direction == SK_AUTOSCROLL_DIRECTION_RIGHT || self.direction == SK_AUTOSCROLL_DIRECTION_LEFT ? morethanWidth:NO; 334 | BOOL morethanHeight = self.animationLabel.bounds.size.height > self.bounds.size.height ? YES:NO; 335 | morethanHeight = self.direction == SK_AUTOSCROLL_DIRECTION_TOP || self.direction == SK_AUTOSCROLL_DIRECTION_BOTTOM ? morethanHeight:NO; 336 | 337 | if (morethanWidth | morethanHeight) { 338 | return YES; 339 | } 340 | return NO; 341 | } 342 | 343 | #pragma mark - Scrolling animation control 344 | 345 | - (void)pauseScroll { 346 | [self.displayLinke setPaused:true]; 347 | } 348 | 349 | - (void)continueScroll { 350 | [self.displayLinke setPaused:false]; 351 | } 352 | 353 | #pragma mark - Gradient fading 354 | 355 | - (void)applyGradientMask { 356 | if (kDefaultFadeLength) { 357 | // Create gradient mask and disappear length 358 | CAGradientLayer *gradientMask = [CAGradientLayer layer]; 359 | gradientMask.bounds = self.layer.bounds; 360 | gradientMask.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 361 | gradientMask.shouldRasterize = YES; 362 | gradientMask.rasterizationScale = [UIScreen mainScreen].scale; 363 | 364 | CGFloat fadePoint = 0; 365 | if (self.direction == SK_AUTOSCROLL_DIRECTION_TOP || self.direction == SK_AUTOSCROLL_DIRECTION_BOTTOM) { 366 | gradientMask.startPoint = CGPointMake(CGRectGetMidX(self.frame), 0); 367 | gradientMask.endPoint = CGPointMake(CGRectGetMidX(self.frame), 1); 368 | fadePoint = kDefaultFadeLength / CGRectGetHeight(self.bounds); 369 | } else { 370 | gradientMask.startPoint = CGPointMake(0, CGRectGetMidY(self.frame)); 371 | gradientMask.endPoint = CGPointMake(1, CGRectGetMidY(self.frame)); 372 | fadePoint = kDefaultFadeLength / CGRectGetWidth(self.bounds); 373 | } 374 | 375 | // Set the gradient mask color and position 376 | id transparent = (id)[UIColor clearColor].CGColor; 377 | id opaque = (id)[UIColor blackColor].CGColor; 378 | gradientMask.colors = @[transparent, opaque, opaque, transparent]; 379 | 380 | // Calculate fade 381 | NSNumber *leftFadePoint = @(fadePoint); 382 | NSNumber *rightFadePoint = @(1 - fadePoint); 383 | NSNumber *bottomFadePoint = self.enableFade ? @1:@0; 384 | if (!self.enableFade) { 385 | switch (self.direction) { 386 | case SK_AUTOSCROLL_DIRECTION_LEFT: 387 | leftFadePoint = @0; 388 | break; 389 | case SK_AUTOSCROLL_DIRECTION_RIGHT: 390 | leftFadePoint = @0; 391 | rightFadePoint = @1; 392 | break; 393 | case SK_AUTOSCROLL_DIRECTION_TOP: 394 | leftFadePoint = @0; 395 | break; 396 | case SK_AUTOSCROLL_DIRECTION_BOTTOM: 397 | leftFadePoint = @0; 398 | rightFadePoint = @1; 399 | break; 400 | } 401 | } 402 | // Give the result of the calculation to the mask 403 | gradientMask.locations = @[@0, leftFadePoint, rightFadePoint, bottomFadePoint]; 404 | 405 | [CATransaction begin]; 406 | [CATransaction setDisableActions:YES]; 407 | self.layer.mask = gradientMask; 408 | [CATransaction commit]; 409 | } else { 410 | // Remove gradient mask and fading length 411 | self.layer.mask = nil; 412 | } 413 | } 414 | 415 | @end 416 | --------------------------------------------------------------------------------