├── .gitignore ├── AutoScrollLabel.podspec ├── AutoScrollLabelDemo ├── AutoScrollLabelDemo.xcodeproj │ └── project.pbxproj ├── AutoScrollLabelDemo │ ├── ASLAppDelegate.h │ ├── ASLAppDelegate.m │ ├── ASLViewController.h │ ├── ASLViewController.m │ ├── AutoScrollLabelDemo-Info.plist │ ├── AutoScrollLabelDemo-Prefix.pch │ ├── Base.lproj │ │ ├── Launch Screen.storyboard │ │ └── Main.storyboard │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m ├── screenshot.png └── screenshot2.png ├── CBAutoScrollLabel ├── CBAutoScrollLabel.h └── CBAutoScrollLabel.m ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | AutoScrollLabelDemo/AutoScrollLabelDemo.xcodeproj/project.xcworkspace 2 | -------------------------------------------------------------------------------- /AutoScrollLabel.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'AutoScrollLabel' 3 | s.version = '0.4.5' 4 | s.summary = 'A marquee like scrolling UILabel, think iPod track title scrolling. Provides edge fading, speed adjustment, scroll direction, etc.' 5 | s.homepage = 'https://github.com/cbess/AutoScrollLabel' 6 | 7 | s.license = { :type => 'MIT', :file => 'LICENSE.txt' } 8 | s.author = { 'Christopher Bess' => 'cbess@quantumquinn.com' } 9 | s.source = { :git => 'https://github.com/cbess/AutoScrollLabel.git', :tag => 'v0.4.5' } 10 | 11 | s.ios.deployment_target = '10.0' 12 | s.tvos.deployment_target = '10.0' 13 | 14 | s.requires_arc = true 15 | s.source_files = 'CBAutoScrollLabel/*.{h,m}' 16 | s.frameworks = 'QuartzCore', 'CoreGraphics' 17 | s.screenshots = 'https://github.com/cbess/AutoScrollLabel/raw/master/AutoScrollLabelDemo/screenshot.png' 18 | end 19 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1A21FA6D1E58DC0F00782FA8 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1A21FA6B1E58DC0F00782FA8 /* Launch Screen.storyboard */; }; 11 | 1E6EE5BD15991EDE00F94499 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E6EE5BC15991EDE00F94499 /* UIKit.framework */; }; 12 | 1E6EE5BF15991EDE00F94499 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E6EE5BE15991EDE00F94499 /* Foundation.framework */; }; 13 | 1E6EE5C115991EDE00F94499 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E6EE5C015991EDE00F94499 /* CoreGraphics.framework */; }; 14 | 1E6EE5C715991EDE00F94499 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1E6EE5C515991EDE00F94499 /* InfoPlist.strings */; }; 15 | 1E6EE5C915991EDE00F94499 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E6EE5C815991EDE00F94499 /* main.m */; }; 16 | 1E6EE5CD15991EDE00F94499 /* ASLAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E6EE5CC15991EDE00F94499 /* ASLAppDelegate.m */; }; 17 | 1E6EE5D015991EDE00F94499 /* ASLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E6EE5CF15991EDE00F94499 /* ASLViewController.m */; }; 18 | 921F8F0519E11A6D003E9CDE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 921F8F0319E11A6D003E9CDE /* Main.storyboard */; }; 19 | 92BA4B2916E1CCBF00770787 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92BA4B2816E1CCBF00770787 /* QuartzCore.framework */; }; 20 | 92BA4B3216E4636F00770787 /* CBAutoScrollLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 92BA4B3116E4636F00770787 /* CBAutoScrollLabel.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 1A7764852335346300BDB8A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = "Base.lproj/Launch Screen.storyboard"; sourceTree = ""; }; 25 | 1A7764862335346300BDB8A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 26 | 1A77648723354B3D00BDB8A0 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 27 | 1E6EE5B815991EDE00F94499 /* AutoScrollLabelDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoScrollLabelDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 1E6EE5BC15991EDE00F94499 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 29 | 1E6EE5BE15991EDE00F94499 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 30 | 1E6EE5C015991EDE00F94499 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 31 | 1E6EE5C415991EDE00F94499 /* AutoScrollLabelDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "AutoScrollLabelDemo-Info.plist"; sourceTree = ""; }; 32 | 1E6EE5C615991EDE00F94499 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 33 | 1E6EE5C815991EDE00F94499 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 34 | 1E6EE5CA15991EDE00F94499 /* AutoScrollLabelDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AutoScrollLabelDemo-Prefix.pch"; sourceTree = ""; }; 35 | 1E6EE5CB15991EDE00F94499 /* ASLAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASLAppDelegate.h; sourceTree = ""; }; 36 | 1E6EE5CC15991EDE00F94499 /* ASLAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASLAppDelegate.m; sourceTree = ""; }; 37 | 1E6EE5CE15991EDE00F94499 /* ASLViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ASLViewController.h; sourceTree = ""; }; 38 | 1E6EE5CF15991EDE00F94499 /* ASLViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASLViewController.m; sourceTree = ""; }; 39 | 92BA4B2816E1CCBF00770787 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 40 | 92BA4B3016E4636F00770787 /* CBAutoScrollLabel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CBAutoScrollLabel.h; sourceTree = ""; }; 41 | 92BA4B3116E4636F00770787 /* CBAutoScrollLabel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CBAutoScrollLabel.m; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 1E6EE5B515991EDE00F94499 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | 92BA4B2916E1CCBF00770787 /* QuartzCore.framework in Frameworks */, 50 | 1E6EE5BD15991EDE00F94499 /* UIKit.framework in Frameworks */, 51 | 1E6EE5BF15991EDE00F94499 /* Foundation.framework in Frameworks */, 52 | 1E6EE5C115991EDE00F94499 /* CoreGraphics.framework in Frameworks */, 53 | ); 54 | runOnlyForDeploymentPostprocessing = 0; 55 | }; 56 | /* End PBXFrameworksBuildPhase section */ 57 | 58 | /* Begin PBXGroup section */ 59 | 1E6EE5AD15991EDD00F94499 = { 60 | isa = PBXGroup; 61 | children = ( 62 | 1A77648723354B3D00BDB8A0 /* README.md */, 63 | 1E6EE5C215991EDE00F94499 /* AutoScrollLabelDemo */, 64 | 1E6EE5BB15991EDE00F94499 /* Frameworks */, 65 | 1E6EE5B915991EDE00F94499 /* Products */, 66 | ); 67 | sourceTree = ""; 68 | }; 69 | 1E6EE5B915991EDE00F94499 /* Products */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 1E6EE5B815991EDE00F94499 /* AutoScrollLabelDemo.app */, 73 | ); 74 | name = Products; 75 | sourceTree = ""; 76 | }; 77 | 1E6EE5BB15991EDE00F94499 /* Frameworks */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | 92BA4B2816E1CCBF00770787 /* QuartzCore.framework */, 81 | 1E6EE5BC15991EDE00F94499 /* UIKit.framework */, 82 | 1E6EE5BE15991EDE00F94499 /* Foundation.framework */, 83 | 1E6EE5C015991EDE00F94499 /* CoreGraphics.framework */, 84 | ); 85 | name = Frameworks; 86 | sourceTree = ""; 87 | }; 88 | 1E6EE5C215991EDE00F94499 /* AutoScrollLabelDemo */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 92BA4B2F16E4636F00770787 /* CBAutoScrollLabel */, 92 | 1E6EE5CB15991EDE00F94499 /* ASLAppDelegate.h */, 93 | 1E6EE5CC15991EDE00F94499 /* ASLAppDelegate.m */, 94 | 1E6EE5CE15991EDE00F94499 /* ASLViewController.h */, 95 | 1E6EE5CF15991EDE00F94499 /* ASLViewController.m */, 96 | 1A21FA6B1E58DC0F00782FA8 /* Launch Screen.storyboard */, 97 | 921F8F0319E11A6D003E9CDE /* Main.storyboard */, 98 | 1E6EE5C315991EDE00F94499 /* Supporting Files */, 99 | ); 100 | path = AutoScrollLabelDemo; 101 | sourceTree = ""; 102 | }; 103 | 1E6EE5C315991EDE00F94499 /* Supporting Files */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 1E6EE5C415991EDE00F94499 /* AutoScrollLabelDemo-Info.plist */, 107 | 1E6EE5C515991EDE00F94499 /* InfoPlist.strings */, 108 | 1E6EE5C815991EDE00F94499 /* main.m */, 109 | 1E6EE5CA15991EDE00F94499 /* AutoScrollLabelDemo-Prefix.pch */, 110 | ); 111 | name = "Supporting Files"; 112 | sourceTree = ""; 113 | }; 114 | 92BA4B2F16E4636F00770787 /* CBAutoScrollLabel */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 92BA4B3016E4636F00770787 /* CBAutoScrollLabel.h */, 118 | 92BA4B3116E4636F00770787 /* CBAutoScrollLabel.m */, 119 | ); 120 | name = CBAutoScrollLabel; 121 | path = ../../CBAutoScrollLabel; 122 | sourceTree = ""; 123 | }; 124 | /* End PBXGroup section */ 125 | 126 | /* Begin PBXNativeTarget section */ 127 | 1E6EE5B715991EDE00F94499 /* AutoScrollLabelDemo */ = { 128 | isa = PBXNativeTarget; 129 | buildConfigurationList = 1E6EE5D615991EDE00F94499 /* Build configuration list for PBXNativeTarget "AutoScrollLabelDemo" */; 130 | buildPhases = ( 131 | 1E6EE5B415991EDE00F94499 /* Sources */, 132 | 1E6EE5B515991EDE00F94499 /* Frameworks */, 133 | 1E6EE5B615991EDE00F94499 /* Resources */, 134 | ); 135 | buildRules = ( 136 | ); 137 | dependencies = ( 138 | ); 139 | name = AutoScrollLabelDemo; 140 | productName = AutoScrollLabelDemo; 141 | productReference = 1E6EE5B815991EDE00F94499 /* AutoScrollLabelDemo.app */; 142 | productType = "com.apple.product-type.application"; 143 | }; 144 | /* End PBXNativeTarget section */ 145 | 146 | /* Begin PBXProject section */ 147 | 1E6EE5AF15991EDE00F94499 /* Project object */ = { 148 | isa = PBXProject; 149 | attributes = { 150 | CLASSPREFIX = ASL; 151 | LastUpgradeCheck = 1100; 152 | ORGANIZATIONNAME = "C. Bess"; 153 | TargetAttributes = { 154 | 1E6EE5B715991EDE00F94499 = { 155 | ProvisioningStyle = Manual; 156 | }; 157 | }; 158 | }; 159 | buildConfigurationList = 1E6EE5B215991EDE00F94499 /* Build configuration list for PBXProject "AutoScrollLabelDemo" */; 160 | compatibilityVersion = "Xcode 3.2"; 161 | developmentRegion = en; 162 | hasScannedForEncodings = 0; 163 | knownRegions = ( 164 | en, 165 | Base, 166 | ); 167 | mainGroup = 1E6EE5AD15991EDD00F94499; 168 | productRefGroup = 1E6EE5B915991EDE00F94499 /* Products */; 169 | projectDirPath = ""; 170 | projectRoot = ""; 171 | targets = ( 172 | 1E6EE5B715991EDE00F94499 /* AutoScrollLabelDemo */, 173 | ); 174 | }; 175 | /* End PBXProject section */ 176 | 177 | /* Begin PBXResourcesBuildPhase section */ 178 | 1E6EE5B615991EDE00F94499 /* Resources */ = { 179 | isa = PBXResourcesBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | 1E6EE5C715991EDE00F94499 /* InfoPlist.strings in Resources */, 183 | 1A21FA6D1E58DC0F00782FA8 /* Launch Screen.storyboard in Resources */, 184 | 921F8F0519E11A6D003E9CDE /* Main.storyboard in Resources */, 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | }; 188 | /* End PBXResourcesBuildPhase section */ 189 | 190 | /* Begin PBXSourcesBuildPhase section */ 191 | 1E6EE5B415991EDE00F94499 /* Sources */ = { 192 | isa = PBXSourcesBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | 1E6EE5C915991EDE00F94499 /* main.m in Sources */, 196 | 1E6EE5CD15991EDE00F94499 /* ASLAppDelegate.m in Sources */, 197 | 1E6EE5D015991EDE00F94499 /* ASLViewController.m in Sources */, 198 | 92BA4B3216E4636F00770787 /* CBAutoScrollLabel.m in Sources */, 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | /* End PBXSourcesBuildPhase section */ 203 | 204 | /* Begin PBXVariantGroup section */ 205 | 1A21FA6B1E58DC0F00782FA8 /* Launch Screen.storyboard */ = { 206 | isa = PBXVariantGroup; 207 | children = ( 208 | 1A7764852335346300BDB8A0 /* Base */, 209 | ); 210 | name = "Launch Screen.storyboard"; 211 | sourceTree = ""; 212 | }; 213 | 1E6EE5C515991EDE00F94499 /* InfoPlist.strings */ = { 214 | isa = PBXVariantGroup; 215 | children = ( 216 | 1E6EE5C615991EDE00F94499 /* en */, 217 | ); 218 | name = InfoPlist.strings; 219 | sourceTree = ""; 220 | }; 221 | 921F8F0319E11A6D003E9CDE /* Main.storyboard */ = { 222 | isa = PBXVariantGroup; 223 | children = ( 224 | 1A7764862335346300BDB8A0 /* Base */, 225 | ); 226 | name = Main.storyboard; 227 | sourceTree = ""; 228 | }; 229 | /* End PBXVariantGroup section */ 230 | 231 | /* Begin XCBuildConfiguration section */ 232 | 1E6EE5D415991EDE00F94499 /* Debug */ = { 233 | isa = XCBuildConfiguration; 234 | buildSettings = { 235 | ALWAYS_SEARCH_USER_PATHS = NO; 236 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 237 | CLANG_ENABLE_OBJC_ARC = YES; 238 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 239 | CLANG_WARN_BOOL_CONVERSION = YES; 240 | CLANG_WARN_COMMA = YES; 241 | CLANG_WARN_CONSTANT_CONVERSION = YES; 242 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 243 | CLANG_WARN_EMPTY_BODY = YES; 244 | CLANG_WARN_ENUM_CONVERSION = YES; 245 | CLANG_WARN_INFINITE_RECURSION = YES; 246 | CLANG_WARN_INT_CONVERSION = YES; 247 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 248 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 249 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 250 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 251 | CLANG_WARN_STRICT_PROTOTYPES = YES; 252 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 253 | CLANG_WARN_UNREACHABLE_CODE = YES; 254 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 255 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 256 | COPY_PHASE_STRIP = NO; 257 | ENABLE_STRICT_OBJC_MSGSEND = YES; 258 | ENABLE_TESTABILITY = YES; 259 | GCC_C_LANGUAGE_STANDARD = gnu99; 260 | GCC_DYNAMIC_NO_PIC = NO; 261 | GCC_NO_COMMON_BLOCKS = YES; 262 | GCC_OPTIMIZATION_LEVEL = 0; 263 | GCC_PREPROCESSOR_DEFINITIONS = ( 264 | "DEBUG=1", 265 | "$(inherited)", 266 | ); 267 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 268 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 276 | ONLY_ACTIVE_ARCH = YES; 277 | SDKROOT = iphoneos; 278 | }; 279 | name = Debug; 280 | }; 281 | 1E6EE5D515991EDE00F94499 /* Release */ = { 282 | isa = XCBuildConfiguration; 283 | buildSettings = { 284 | ALWAYS_SEARCH_USER_PATHS = NO; 285 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 286 | CLANG_ENABLE_OBJC_ARC = YES; 287 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 288 | CLANG_WARN_BOOL_CONVERSION = YES; 289 | CLANG_WARN_COMMA = YES; 290 | CLANG_WARN_CONSTANT_CONVERSION = YES; 291 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 292 | CLANG_WARN_EMPTY_BODY = YES; 293 | CLANG_WARN_ENUM_CONVERSION = YES; 294 | CLANG_WARN_INFINITE_RECURSION = YES; 295 | CLANG_WARN_INT_CONVERSION = YES; 296 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 297 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 298 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 299 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 300 | CLANG_WARN_STRICT_PROTOTYPES = YES; 301 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 302 | CLANG_WARN_UNREACHABLE_CODE = YES; 303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 304 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 305 | COPY_PHASE_STRIP = YES; 306 | ENABLE_STRICT_OBJC_MSGSEND = YES; 307 | GCC_C_LANGUAGE_STANDARD = gnu99; 308 | GCC_NO_COMMON_BLOCKS = YES; 309 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 312 | GCC_WARN_UNDECLARED_SELECTOR = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 317 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 318 | SDKROOT = iphoneos; 319 | VALIDATE_PRODUCT = YES; 320 | }; 321 | name = Release; 322 | }; 323 | 1E6EE5D715991EDE00F94499 /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | CURRENT_PROJECT_VERSION = 1; 327 | DEVELOPMENT_TEAM = ""; 328 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 329 | GCC_PREFIX_HEADER = "AutoScrollLabelDemo/AutoScrollLabelDemo-Prefix.pch"; 330 | INFOPLIST_FILE = "AutoScrollLabelDemo/AutoScrollLabelDemo-Info.plist"; 331 | PRODUCT_BUNDLE_IDENTIFIER = "cbess.${PRODUCT_NAME:rfc1034identifier}"; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | TARGETED_DEVICE_FAMILY = 1; 334 | WRAPPER_EXTENSION = app; 335 | }; 336 | name = Debug; 337 | }; 338 | 1E6EE5D815991EDE00F94499 /* Release */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | CURRENT_PROJECT_VERSION = 1; 342 | DEVELOPMENT_TEAM = ""; 343 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 344 | GCC_PREFIX_HEADER = "AutoScrollLabelDemo/AutoScrollLabelDemo-Prefix.pch"; 345 | INFOPLIST_FILE = "AutoScrollLabelDemo/AutoScrollLabelDemo-Info.plist"; 346 | PRODUCT_BUNDLE_IDENTIFIER = "cbess.${PRODUCT_NAME:rfc1034identifier}"; 347 | PRODUCT_NAME = "$(TARGET_NAME)"; 348 | TARGETED_DEVICE_FAMILY = 1; 349 | WRAPPER_EXTENSION = app; 350 | }; 351 | name = Release; 352 | }; 353 | /* End XCBuildConfiguration section */ 354 | 355 | /* Begin XCConfigurationList section */ 356 | 1E6EE5B215991EDE00F94499 /* Build configuration list for PBXProject "AutoScrollLabelDemo" */ = { 357 | isa = XCConfigurationList; 358 | buildConfigurations = ( 359 | 1E6EE5D415991EDE00F94499 /* Debug */, 360 | 1E6EE5D515991EDE00F94499 /* Release */, 361 | ); 362 | defaultConfigurationIsVisible = 0; 363 | defaultConfigurationName = Release; 364 | }; 365 | 1E6EE5D615991EDE00F94499 /* Build configuration list for PBXNativeTarget "AutoScrollLabelDemo" */ = { 366 | isa = XCConfigurationList; 367 | buildConfigurations = ( 368 | 1E6EE5D715991EDE00F94499 /* Debug */, 369 | 1E6EE5D815991EDE00F94499 /* Release */, 370 | ); 371 | defaultConfigurationIsVisible = 0; 372 | defaultConfigurationName = Release; 373 | }; 374 | /* End XCConfigurationList section */ 375 | }; 376 | rootObject = 1E6EE5AF15991EDE00F94499 /* Project object */; 377 | } 378 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/ASLAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASLAppDelegate.h 3 | // AutoScrollLabelDemo 4 | // 5 | // Created by Christopher Bess on 6/25/12. 6 | // Copyright (c) 2012 C. Bess. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ASLViewController; 12 | 13 | @interface ASLAppDelegate : UIResponder 14 | 15 | @property (strong, nonatomic) UIWindow *window; 16 | 17 | @property (strong, nonatomic) ASLViewController *viewController; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/ASLAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASLAppDelegate.m 3 | // AutoScrollLabelDemo 4 | // 5 | // Created by Christopher Bess on 6/25/12. 6 | // Copyright (c) 2012 C. Bess. All rights reserved. 7 | // 8 | 9 | #import "ASLAppDelegate.h" 10 | #import "ASLViewController.h" 11 | 12 | @implementation ASLAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | return YES; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/ASLViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ASLViewController.h 3 | // AutoScrollLabelDemo 4 | // 5 | // Created by Christopher Bess on 6/25/12. 6 | // Copyright (c) 2012 C. Bess. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ASLViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/ASLViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ASLViewController.m 3 | // AutoScrollLabelDemo 4 | // 5 | // Created by Christopher Bess on 6/25/12. 6 | // Copyright (c) 2012 C. Bess. All rights reserved. 7 | // 8 | 9 | #import "ASLViewController.h" 10 | #import "CBAutoScrollLabel.h" 11 | 12 | @interface ASLViewController () 13 | 14 | @property (weak, nonatomic) IBOutlet CBAutoScrollLabel *autoScrollLabel; 15 | @property (weak, nonatomic) IBOutlet CBAutoScrollLabel *navigationBarScrollLabel; 16 | 17 | @end 18 | 19 | @implementation ASLViewController 20 | 21 | - (void)viewDidLoad { 22 | [super viewDidLoad]; 23 | 24 | // setup the auto scroll label 25 | self.autoScrollLabel.text = @"This text may be clipped, but now it will be scrolled. This text will be scrolled even after device rotation."; 26 | self.autoScrollLabel.textColor = [UIColor systemBlueColor]; 27 | self.autoScrollLabel.backgroundColor = [UIColor clearColor]; 28 | self.autoScrollLabel.labelSpacing = 30; // distance between start and end labels 29 | self.autoScrollLabel.pauseInterval = 1.7; // seconds of pause before scrolling starts again 30 | self.autoScrollLabel.scrollSpeed = 30; // pixels per second 31 | self.autoScrollLabel.textAlignment = NSTextAlignmentCenter; // centers text when no auto-scrolling is applied 32 | self.autoScrollLabel.fadeLength = 12.f; 33 | self.autoScrollLabel.scrollDirection = CBAutoScrollDirectionLeft; 34 | [self.autoScrollLabel observeApplicationNotifications]; 35 | 36 | // navigation bar auto scroll label 37 | self.navigationBarScrollLabel.text = @"Navigation Bar Title... Scrolling... And scrolling."; 38 | self.navigationBarScrollLabel.pauseInterval = 3.f; 39 | self.navigationBarScrollLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; 40 | if (@available(iOS 13.0, *)) { 41 | self.navigationBarScrollLabel.textColor = [UIColor labelColor]; 42 | } else { 43 | self.navigationBarScrollLabel.textColor = [UIColor blackColor]; 44 | } 45 | [self.navigationBarScrollLabel observeApplicationNotifications]; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/AutoScrollLabelDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/AutoScrollLabelDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'AutoScrollLabelDemo' target in the 'AutoScrollLabelDemo' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/Base.lproj/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/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 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 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 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/AutoScrollLabelDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AutoScrollLabelDemo 4 | // 5 | // Created by Christopher Bess on 6/25/12. 6 | // Copyright (c) 2012 C. Bess. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "ASLAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([ASLAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /AutoScrollLabelDemo/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbess/AutoScrollLabel/a63bc83a0625240c9a1876e2c40851317d2b8c19/AutoScrollLabelDemo/screenshot.png -------------------------------------------------------------------------------- /AutoScrollLabelDemo/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cbess/AutoScrollLabel/a63bc83a0625240c9a1876e2c40851317d2b8c19/AutoScrollLabelDemo/screenshot2.png -------------------------------------------------------------------------------- /CBAutoScrollLabel/CBAutoScrollLabel.h: -------------------------------------------------------------------------------- 1 | // 2 | // CBAutoScrollLabel.h 3 | // CBAutoScrollLabel 4 | // 5 | // Created by Brian Stormont on 10/21/09. 6 | // Updated/Modernized by Christopher Bess on 2/5/12 7 | // 8 | // Copyright 2009 Stormy Productions. All rights reserved. 9 | // 10 | // Originally from: http://blog.stormyprods.com/2009/10/simple-scrolling-uilabel-for-iphone.html 11 | // 12 | // Permission is granted to use this code free of charge for any project. 13 | // 14 | 15 | #import 16 | 17 | /// Specifies the direction of the scroll 18 | typedef NS_ENUM(NSInteger, CBAutoScrollDirection) { 19 | CBAutoScrollDirectionRight, 20 | CBAutoScrollDirectionLeft 21 | }; 22 | 23 | @interface CBAutoScrollLabel : UIView 24 | 25 | @property (nonatomic) CBAutoScrollDirection scrollDirection; 26 | /// Scroll speed in pixels per second, defaults to 30 27 | @property (nonatomic) float scrollSpeed; 28 | @property (nonatomic) NSTimeInterval pauseInterval; // defaults to 1.5 29 | @property (nonatomic) NSInteger labelSpacing; // pixels, defaults to 20 30 | 31 | /** 32 | * The animation options used when scrolling the UILabels. 33 | * @discussion UIViewAnimationOptionAllowUserInteraction is always applied to the animations. 34 | */ 35 | @property (nonatomic) UIViewAnimationOptions animationOptions; 36 | 37 | /** 38 | * Returns YES, if it is actively scrolling, NO if it has paused or if text is within bounds (disables scrolling). 39 | */ 40 | @property (nonatomic, readonly) BOOL scrolling; 41 | @property (nonatomic) CGFloat fadeLength; // defaults to 7 42 | 43 | // UILabel properties 44 | @property (nonatomic, strong, nonnull) UIFont *font; 45 | @property (nonatomic, copy, nullable) NSString *text; 46 | @property (nonatomic, copy, nullable) NSAttributedString *attributedText; 47 | @property (nonatomic, strong, nonnull) UIColor *textColor; 48 | @property (nonatomic) NSTextAlignment textAlignment; // only applies when not auto-scrolling 49 | @property (nonatomic, strong, nullable) UIColor *shadowColor; 50 | @property (nonatomic) CGSize shadowOffset; 51 | 52 | /** 53 | * Lays out the scrollview contents, enabling text scrolling if the text will be clipped. 54 | * @discussion Uses [scrollLabelIfNeeded] internally. 55 | */ 56 | - (void)refreshLabels; 57 | 58 | /** 59 | * Set the text to the label and refresh labels, if needed. 60 | * @discussion Useful when you have a situation where you need to layout the scroll label after it's text is set. 61 | */ 62 | - (void)setText:(nullable NSString *)text refreshLabels:(BOOL)refresh; 63 | 64 | /** 65 | Set the attributed text and refresh labels, if needed. 66 | */ 67 | - (void)setAttributedText:(nullable NSAttributedString *)theText refreshLabels:(BOOL)refresh; 68 | 69 | /** 70 | * Initiates auto-scroll, if the label width exceeds the bounds of the scrollview. 71 | */ 72 | - (void)scrollLabelIfNeeded; 73 | 74 | /** 75 | * Observes UIApplication state notifications to auto-restart scrolling and watch for 76 | * orientation changes to refresh the labels. 77 | * @discussion Must be called to observe the notifications. Calling multiple times will still only 78 | * register the notifications once. 79 | */ 80 | - (void)observeApplicationNotifications; 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /CBAutoScrollLabel/CBAutoScrollLabel.m: -------------------------------------------------------------------------------- 1 | // 2 | // CBAutoScrollLabel.m 3 | // CBAutoScrollLabel 4 | // 5 | // Created by Brian Stormont on 10/21/09. 6 | // Updated by Christopher Bess on 2/5/12 7 | // 8 | // Copyright 2009 Stormy Productions. 9 | // 10 | // Permission is granted to use this code free of charge for any project. 11 | // 12 | 13 | #import "CBAutoScrollLabel.h" 14 | #import 15 | 16 | #define kLabelCount 2 17 | #define kDefaultFadeLength 7.f 18 | // pixel buffer space between scrolling label 19 | #define kDefaultLabelBufferSpace 20 20 | #define kDefaultPixelsPerSecond 30 21 | #define kDefaultPauseTime 1.5f 22 | 23 | // shortcut method for NSArray iterations 24 | static void each_object(NSArray *objects, void (^block)(id object)) { 25 | for (id obj in objects) { 26 | block(obj); 27 | } 28 | } 29 | 30 | // shortcut to change each label attribute value 31 | #define EACH_LABEL(ATTR, VALUE) each_object(self.labels, ^(UILabel *label) { label.ATTR = VALUE; }); 32 | 33 | @interface CBAutoScrollLabel () 34 | 35 | @property (nonatomic, strong) NSArray *labels; 36 | @property (nonatomic, strong, readonly) UILabel *mainLabel; 37 | @property (nonatomic, strong) UIScrollView *scrollView; 38 | 39 | @end 40 | 41 | @implementation CBAutoScrollLabel 42 | 43 | - (id)initWithCoder:(NSCoder *)aDecoder { 44 | if ((self = [super initWithCoder:aDecoder])) { 45 | [self commonInit]; 46 | } 47 | return self; 48 | } 49 | 50 | - (id)initWithFrame:(CGRect)frame { 51 | if ((self = [super initWithFrame:frame])) { 52 | [self commonInit]; 53 | } 54 | return self; 55 | } 56 | 57 | - (void)commonInit { 58 | // create the labels 59 | NSMutableSet *labelSet = [[NSMutableSet alloc] initWithCapacity:kLabelCount]; 60 | 61 | for (int index = 0; index < kLabelCount; ++index) { 62 | UILabel *label = [[UILabel alloc] init]; 63 | label.backgroundColor = [UIColor clearColor]; 64 | label.autoresizingMask = self.autoresizingMask; 65 | 66 | // store labels 67 | [self.scrollView addSubview:label]; 68 | [labelSet addObject:label]; 69 | } 70 | 71 | self.labels = [labelSet.allObjects copy]; 72 | 73 | // default values 74 | _scrollDirection = CBAutoScrollDirectionLeft; 75 | _scrollSpeed = kDefaultPixelsPerSecond; 76 | self.pauseInterval = kDefaultPauseTime; 77 | self.labelSpacing = kDefaultLabelBufferSpace; 78 | self.textAlignment = NSTextAlignmentLeft; 79 | self.animationOptions = UIViewAnimationOptionCurveLinear; 80 | self.scrollView.showsVerticalScrollIndicator = NO; 81 | self.scrollView.showsHorizontalScrollIndicator = NO; 82 | self.scrollView.scrollEnabled = NO; 83 | self.userInteractionEnabled = NO; 84 | self.backgroundColor = [UIColor clearColor]; 85 | self.clipsToBounds = YES; 86 | self.fadeLength = kDefaultFadeLength; 87 | } 88 | 89 | - (void)dealloc { 90 | [NSObject cancelPreviousPerformRequestsWithTarget:self]; 91 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 92 | } 93 | 94 | - (void)setFrame:(CGRect)frame { 95 | [super setFrame:frame]; 96 | 97 | [self didChangeFrame]; 98 | } 99 | 100 | // For autolayout 101 | - (void)setBounds:(CGRect)bounds { 102 | [super setBounds:bounds]; 103 | 104 | [self didChangeFrame]; 105 | } 106 | 107 | - (void)didMoveToWindow { 108 | [super didMoveToWindow]; 109 | 110 | if (self.window) { 111 | [self scrollLabelIfNeeded]; 112 | } 113 | } 114 | 115 | #pragma mark - Properties 116 | 117 | - (UIScrollView *)scrollView { 118 | if (_scrollView == nil) { 119 | _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds]; 120 | _scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); 121 | _scrollView.backgroundColor = [UIColor clearColor]; 122 | 123 | [self addSubview:_scrollView]; 124 | } 125 | return _scrollView; 126 | } 127 | 128 | - (void)setFadeLength:(CGFloat)fadeLength { 129 | if (_fadeLength != fadeLength) { 130 | _fadeLength = fadeLength; 131 | 132 | [self refreshLabels]; 133 | [self applyGradientMaskForFadeLength:fadeLength enableFade:NO]; 134 | } 135 | } 136 | 137 | - (UILabel *)mainLabel { 138 | return [self.labels firstObject]; 139 | } 140 | 141 | - (void)setText:(NSString *)theText { 142 | [self setText:theText refreshLabels:YES]; 143 | } 144 | 145 | - (void)setText:(NSString *)theText refreshLabels:(BOOL)refresh { 146 | // ignore identical text changes 147 | if ([theText isEqualToString:self.text]) 148 | return; 149 | 150 | EACH_LABEL(text, theText) 151 | 152 | if (refresh) 153 | [self refreshLabels]; 154 | } 155 | 156 | - (NSString *)text { 157 | return self.mainLabel.text; 158 | } 159 | 160 | - (void)setAttributedText:(NSAttributedString *)theText { 161 | [self setAttributedText:theText refreshLabels:YES]; 162 | } 163 | 164 | - (void)setAttributedText:(NSAttributedString *)theText refreshLabels:(BOOL)refresh { 165 | // ignore identical text changes 166 | if ([theText.string isEqualToString:self.attributedText.string]) 167 | return; 168 | 169 | EACH_LABEL(attributedText, theText) 170 | 171 | if (refresh) 172 | [self refreshLabels]; 173 | } 174 | 175 | - (NSAttributedString *)attributedText { 176 | return self.mainLabel.attributedText; 177 | } 178 | 179 | - (void)setTextColor:(UIColor *)color { 180 | EACH_LABEL(textColor, color) 181 | } 182 | 183 | - (UIColor *)textColor { 184 | return self.mainLabel.textColor; 185 | } 186 | 187 | - (void)setFont:(UIFont *)font { 188 | if (self.mainLabel.font == font) 189 | return; 190 | 191 | EACH_LABEL(font, font) 192 | 193 | [self refreshLabels]; 194 | [self invalidateIntrinsicContentSize]; 195 | } 196 | 197 | - (UIFont *)font { 198 | return self.mainLabel.font; 199 | } 200 | 201 | - (void)setScrollSpeed:(float)speed { 202 | _scrollSpeed = speed; 203 | 204 | [self scrollLabelIfNeeded]; 205 | } 206 | 207 | - (void)setScrollDirection:(CBAutoScrollDirection)direction { 208 | _scrollDirection = direction; 209 | 210 | [self scrollLabelIfNeeded]; 211 | } 212 | 213 | - (void)setShadowColor:(UIColor *)color { 214 | EACH_LABEL(shadowColor, color) 215 | } 216 | 217 | - (UIColor *)shadowColor { 218 | return self.mainLabel.shadowColor; 219 | } 220 | 221 | - (void)setShadowOffset:(CGSize)offset { 222 | EACH_LABEL(shadowOffset, offset) 223 | } 224 | 225 | - (CGSize)shadowOffset { 226 | return self.mainLabel.shadowOffset; 227 | } 228 | 229 | #pragma mark - Autolayout 230 | 231 | - (CGSize)intrinsicContentSize { 232 | return CGSizeMake(0, [self.mainLabel intrinsicContentSize].height); 233 | } 234 | 235 | #pragma mark - Misc 236 | 237 | - (void)observeApplicationNotifications { 238 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 239 | 240 | // restart scrolling when the app has been activated 241 | [[NSNotificationCenter defaultCenter] addObserver:self 242 | selector:@selector(scrollLabelIfNeeded) 243 | name:UIApplicationWillEnterForegroundNotification 244 | object:nil]; 245 | 246 | [[NSNotificationCenter defaultCenter] addObserver:self 247 | selector:@selector(scrollLabelIfNeeded) 248 | name:UIApplicationDidBecomeActiveNotification 249 | object:nil]; 250 | 251 | #ifndef TARGET_OS_TV 252 | // refresh labels when interface orientation is changed 253 | [[NSNotificationCenter defaultCenter] addObserver:self 254 | selector:@selector(onUIApplicationDidChangeStatusBarOrientationNotification:) 255 | name:UIApplicationDidChangeStatusBarOrientationNotification 256 | object:nil]; 257 | #endif 258 | 259 | } 260 | 261 | - (void)enableShadow { 262 | _scrolling = YES; 263 | [self applyGradientMaskForFadeLength:self.fadeLength enableFade:YES]; 264 | } 265 | 266 | - (void)scrollLabelIfNeeded { 267 | if (!self.text.length) 268 | return; 269 | 270 | CGFloat labelWidth = CGRectGetWidth(self.mainLabel.bounds); 271 | if (labelWidth <= CGRectGetWidth(self.bounds)) 272 | return; 273 | 274 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(scrollLabelIfNeeded) object:nil]; 275 | [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(enableShadow) object:nil]; 276 | 277 | [self.scrollView.layer removeAllAnimations]; 278 | 279 | BOOL doScrollLeft = (self.scrollDirection == CBAutoScrollDirectionLeft); 280 | self.scrollView.contentOffset = (doScrollLeft ? CGPointZero : CGPointMake(labelWidth + self.labelSpacing, 0)); 281 | 282 | // Add the left shadow after delay 283 | [self performSelector:@selector(enableShadow) withObject:nil afterDelay:self.pauseInterval]; 284 | 285 | // animate the scrolling 286 | NSTimeInterval duration = labelWidth / self.scrollSpeed; 287 | [UIView animateWithDuration:duration delay:self.pauseInterval options:self.animationOptions | UIViewAnimationOptionAllowUserInteraction animations:^{ 288 | // adjust offset 289 | self.scrollView.contentOffset = (doScrollLeft ? CGPointMake(labelWidth + self.labelSpacing, 0) : CGPointZero); 290 | } completion:^(BOOL finished) { 291 | self->_scrolling = NO; 292 | 293 | // remove the left shadow 294 | [self applyGradientMaskForFadeLength:self.fadeLength enableFade:NO]; 295 | 296 | // setup pause delay/loop 297 | if (finished) { 298 | [self performSelector:@selector(scrollLabelIfNeeded) withObject:nil]; 299 | } 300 | }]; 301 | } 302 | 303 | - (void)refreshLabels { 304 | __block float offset = 0; 305 | 306 | each_object(self.labels, ^(UILabel *label) { 307 | [label sizeToFit]; 308 | 309 | CGRect frame = label.frame; 310 | frame.origin = CGPointMake(offset, 0); 311 | frame.size.height = CGRectGetHeight(self.bounds); 312 | label.frame = frame; 313 | 314 | // Recenter label vertically within the scroll view 315 | label.center = CGPointMake(label.center.x, roundf(self.center.y - CGRectGetMinY(self.frame))); 316 | 317 | offset += CGRectGetWidth(label.bounds) + self.labelSpacing; 318 | }); 319 | 320 | self.scrollView.contentOffset = CGPointZero; 321 | [self.scrollView.layer removeAllAnimations]; 322 | 323 | // if the label is bigger than the space allocated, then it should scroll 324 | if (CGRectGetWidth(self.mainLabel.bounds) > CGRectGetWidth(self.bounds)) { 325 | CGSize size; 326 | size.width = CGRectGetWidth(self.mainLabel.bounds) + CGRectGetWidth(self.bounds) + self.labelSpacing; 327 | size.height = CGRectGetHeight(self.bounds); 328 | self.scrollView.contentSize = size; 329 | 330 | EACH_LABEL(hidden, NO) 331 | 332 | [self applyGradientMaskForFadeLength:self.fadeLength enableFade:self.scrolling]; 333 | 334 | [self scrollLabelIfNeeded]; 335 | } else { 336 | // Hide the other labels 337 | EACH_LABEL(hidden, (self.mainLabel != label)) 338 | 339 | // adjust the scroll view and main label 340 | self.scrollView.contentSize = self.bounds.size; 341 | self.mainLabel.frame = self.bounds; 342 | self.mainLabel.hidden = NO; 343 | self.mainLabel.textAlignment = self.textAlignment; 344 | 345 | // cleanup animation 346 | [self.scrollView.layer removeAllAnimations]; 347 | 348 | [self applyGradientMaskForFadeLength:0 enableFade:NO]; 349 | } 350 | } 351 | 352 | // bounds or frame has been changed 353 | - (void)didChangeFrame { 354 | [self refreshLabels]; 355 | [self applyGradientMaskForFadeLength:self.fadeLength enableFade:self.scrolling]; 356 | } 357 | 358 | #pragma mark - Gradient 359 | 360 | // ref: https://github.com/cbpowell/MarqueeLabel 361 | - (void)applyGradientMaskForFadeLength:(CGFloat)fadeLength enableFade:(BOOL)fade { 362 | CGFloat labelWidth = CGRectGetWidth(self.mainLabel.bounds); 363 | 364 | if (labelWidth <= CGRectGetWidth(self.bounds)) 365 | fadeLength = 0; 366 | 367 | if (fadeLength) { 368 | // Recreate gradient mask with new fade length 369 | CAGradientLayer *gradientMask = [CAGradientLayer layer]; 370 | 371 | gradientMask.bounds = self.layer.bounds; 372 | gradientMask.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); 373 | 374 | gradientMask.shouldRasterize = YES; 375 | gradientMask.rasterizationScale = [UIScreen mainScreen].scale; 376 | 377 | gradientMask.startPoint = CGPointMake(0, CGRectGetMidY(self.frame)); 378 | gradientMask.endPoint = CGPointMake(1, CGRectGetMidY(self.frame)); 379 | 380 | // setup fade mask colors and location 381 | id transparent = (id)[UIColor clearColor].CGColor; 382 | id opaque = (id)[UIColor blackColor].CGColor; 383 | gradientMask.colors = @[transparent, opaque, opaque, transparent]; 384 | 385 | // calcluate fade 386 | CGFloat fadePoint = fadeLength / CGRectGetWidth(self.bounds); 387 | NSNumber *leftFadePoint = @(fadePoint); 388 | NSNumber *rightFadePoint = @(1 - fadePoint); 389 | if (!fade) switch (self.scrollDirection) { 390 | case CBAutoScrollDirectionLeft: 391 | leftFadePoint = @0; 392 | break; 393 | 394 | case CBAutoScrollDirectionRight: 395 | leftFadePoint = @0; 396 | rightFadePoint = @1; 397 | break; 398 | } 399 | 400 | // apply calculations to mask 401 | gradientMask.locations = @[@0, leftFadePoint, rightFadePoint, @1]; 402 | 403 | // don't animate the mask change 404 | [CATransaction begin]; 405 | [CATransaction setDisableActions:YES]; 406 | self.layer.mask = gradientMask; 407 | [CATransaction commit]; 408 | } else { 409 | // Remove gradient mask for 0.0f length fade length 410 | self.layer.mask = nil; 411 | } 412 | } 413 | 414 | #pragma mark - Notifications 415 | 416 | - (void)onUIApplicationDidChangeStatusBarOrientationNotification:(NSNotification *)notification { 417 | // delay to have it re-calculate on next runloop 418 | [self performSelector:@selector(refreshLabels) withObject:nil afterDelay:.1f]; 419 | [self performSelector:@selector(scrollLabelIfNeeded) withObject:nil afterDelay:.1f]; 420 | } 421 | 422 | @end 423 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT 2 | 3 | Copyright (c) 2013 Christopher Bess 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AutoScrollLabel 2 | 3 | A `UILabel` with auto scrolling for text that may be clipped by the view. Provides edge fading, speed adjustment, scroll direction, etc. 4 | 5 | ![screenshot](https://github.com/cbess/AutoScrollLabel/raw/master/AutoScrollLabelDemo/screenshot.png) 6 | ![screenshot2](https://github.com/cbess/AutoScrollLabel/raw/master/AutoScrollLabelDemo/screenshot2.png) 7 | 8 | CocoaPod: `AutoScrollLabel` 9 | 10 | iOS 12, tvOS 12 and greater. For Non-ARC use v0.2.1. 11 | 12 | ```objc 13 | #import 14 | ``` 15 | 16 | ```swift 17 | import AutoScrollLabel 18 | ``` 19 | 20 | ```objc 21 | autoScrollLabel.text = @"This text may be clipped, but now it will be scrolled."; 22 | autoScrollLabel.textColor = [UIColor systemBlueColor]; 23 | autoScrollLabel.labelSpacing = 35; // distance between start and end labels 24 | autoScrollLabel.pauseInterval = 3.7; // seconds of pause before scrolling starts again 25 | autoScrollLabel.scrollSpeed = 30; // pixels per second 26 | autoScrollLabel.textAlignment = NSTextAlignmentCenter; // centers text when no auto-scrolling is applied 27 | autoScrollLabel.fadeLength = 12.f; // length of the left and right edge fade, 0 to disable 28 | ``` 29 | 30 | [Soli Deo gloria](https://perfectGod.com) 31 | 32 | #### Real world use: 33 | 34 | ![app](http://static.solatunes.com//images/app/app-stage.jpg) 35 | 36 | [Sola Tunes iOS App (Free)](https://itunes.apple.com/us/app/sola-tunes-free-music-for-all/id554537542?mt=8) 37 | 38 | #### License 39 | 40 | MIT - Copyright (c) 2019 Christopher Bess 41 | --------------------------------------------------------------------------------