├── .DS_Store ├── JHTickerView ├── JHTickerView.h └── JHTickerView.m ├── README.md ├── TickerDemo ├── TickerDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ ├── xcshareddata │ │ │ └── TickerDemo.xccheckout │ │ └── xcuserdata │ │ │ └── jeff.xcuserdatad │ │ │ ├── UserInterfaceState.xcuserstate │ │ │ └── WorkspaceSettings.xcsettings │ └── xcuserdata │ │ └── jeff.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── TickerDemo.xcscheme │ │ └── xcschememanagement.plist ├── TickerDemo │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── JHAppDelegate.h │ ├── JHAppDelegate.m │ ├── JHTickerView │ │ ├── JHTickerView.h │ │ └── JHTickerView.m │ ├── JHViewController.h │ ├── JHViewController.m │ ├── TickerDemo-Info.plist │ ├── TickerDemo-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── TickerDemoTests │ ├── TickerDemoTests-Info.plist │ ├── TickerDemoTests.m │ └── en.lproj │ └── InfoPlist.strings └── ticker.gif /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffhodnett/JHTickerView/00f5359d15a5471b02e7083846e2456353621dd3/.DS_Store -------------------------------------------------------------------------------- /JHTickerView/JHTickerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JHTickerView.h 3 | // Ticker 4 | // 5 | // Created by Jeff Hodnett on 03/05/2011. 6 | // Copyright 2011 Applausible. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum 12 | { 13 | JHTickerDirectionLTR, 14 | JHTickerDirectionRTL, 15 | } JHTickerDirection; 16 | 17 | @interface JHTickerView : UIView { 18 | 19 | // The ticker strings 20 | NSArray *tickerStrings; 21 | 22 | // The current index for the string 23 | int currentIndex; 24 | 25 | // The ticker speed 26 | float tickerSpeed; 27 | 28 | // Should the ticker loop 29 | BOOL loops; 30 | 31 | // The current state of the ticker 32 | BOOL running; 33 | 34 | // The ticker label 35 | UILabel *tickerLabel; 36 | 37 | // The ticker font 38 | UIFont *tickerFont; 39 | } 40 | 41 | @property(nonatomic, retain) NSArray *tickerStrings; 42 | @property(nonatomic) float tickerSpeed; 43 | @property(nonatomic) BOOL loops; 44 | @property(nonatomic) JHTickerDirection direction; 45 | 46 | -(void)start; 47 | //-(void)stop; 48 | -(void)pause; 49 | -(void)resume; 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /JHTickerView/JHTickerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JHTickerView.m 3 | // Ticker 4 | // 5 | // Created by Jeff Hodnett on 03/05/2011. 6 | // Copyright 2011 Applausible. All rights reserved. 7 | // 8 | 9 | #import "JHTickerView.h" 10 | #import 11 | 12 | @interface JHTickerView(Private) 13 | -(void)setupView; 14 | -(void)animateCurrentTickerString; 15 | -(void)pauseLayer:(CALayer *)layer; 16 | -(void)resumeLayer:(CALayer *)layer; 17 | @end 18 | 19 | @implementation JHTickerView 20 | 21 | @synthesize tickerStrings; 22 | @synthesize tickerSpeed; 23 | @synthesize loops; 24 | @synthesize direction; 25 | 26 | - (id)initWithFrame:(CGRect)frame 27 | { 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | // Initialization code 31 | [self setupView]; 32 | } 33 | return self; 34 | } 35 | 36 | -(id)initWithCoder:(NSCoder *)aDecoder { 37 | if( (self = [super initWithCoder:aDecoder]) ) { 38 | // Initialization code 39 | [self setupView]; 40 | } 41 | return self; 42 | } 43 | 44 | /* 45 | // Only override drawRect: if you perform custom drawing. 46 | // An empty implementation adversely affects performance during animation. 47 | - (void)drawRect:(CGRect)rect 48 | { 49 | // Drawing code 50 | } 51 | */ 52 | 53 | - (void)dealloc 54 | { 55 | [tickerLabel release]; 56 | [tickerStrings release]; 57 | 58 | [super dealloc]; 59 | } 60 | 61 | -(void)setupView { 62 | // Set background color to white 63 | [self setBackgroundColor:[UIColor whiteColor]]; 64 | 65 | // Set a corner radius 66 | [self.layer setCornerRadius:5.0f]; 67 | [self.layer setBorderWidth:2.0f]; 68 | [self.layer setBorderColor:[UIColor blackColor].CGColor]; 69 | [self setClipsToBounds:YES]; 70 | 71 | // Set the font 72 | tickerFont = [UIFont fontWithName:@"Marker Felt" size:22.0]; 73 | 74 | // Add the label (i'm gonna center it on the view - please feel free to do your own thing) 75 | tickerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, self.frame.size.height / 4, self.frame.size.width, self.frame.size.height)]; 76 | [tickerLabel setBackgroundColor:[UIColor clearColor]]; 77 | [tickerLabel setNumberOfLines:1]; 78 | [tickerLabel setFont:tickerFont]; 79 | [self addSubview:tickerLabel]; 80 | 81 | // Set that it loops by default 82 | loops = YES; 83 | 84 | // Set the default direction 85 | direction = JHTickerDirectionLTR; 86 | } 87 | 88 | -(void)animateCurrentTickerString 89 | { 90 | NSString *currentString = [tickerStrings objectAtIndex:currentIndex]; 91 | 92 | // Calculate the size of the text and update the frame size of the ticker label 93 | CGSize textSize = [currentString sizeWithFont:tickerFont constrainedToSize:CGSizeMake(9999, self.frame.size.height) lineBreakMode:UILineBreakModeWordWrap]; 94 | 95 | // Setup some starting and end points 96 | float startingX = 0.0f; 97 | float endX = 0.0f; 98 | switch (direction) { 99 | case JHTickerDirectionRTL: 100 | startingX = -textSize.width; 101 | endX = self.frame.size.width; 102 | break; 103 | case JHTickerDirectionLTR: 104 | default: 105 | startingX = self.frame.size.width; 106 | endX = -textSize.width; 107 | break; 108 | } 109 | 110 | // Set starting position 111 | [tickerLabel setFrame:CGRectMake(startingX, tickerLabel.frame.origin.y, textSize.width, textSize.height)]; 112 | 113 | // Set the string 114 | [tickerLabel setText:currentString]; 115 | 116 | // Calculate a uniform duration for the item 117 | float duration = (textSize.width + self.frame.size.width) / tickerSpeed; 118 | 119 | // Create a UIView animation 120 | [UIView beginAnimations:@"" context:nil]; 121 | [UIView setAnimationCurve:UIViewAnimationCurveLinear]; 122 | [UIView setAnimationDuration:duration]; 123 | [UIView setAnimationDelegate:self]; 124 | [UIView setAnimationDidStopSelector:@selector(tickerMoveAnimationDidStop:finished:context:)]; 125 | 126 | // Update end position 127 | CGRect tickerFrame = tickerLabel.frame; 128 | tickerFrame.origin.x = endX; 129 | [tickerLabel setFrame:tickerFrame]; 130 | 131 | [UIView commitAnimations]; 132 | } 133 | 134 | -(void)tickerMoveAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context 135 | { 136 | // Update the index 137 | currentIndex++; 138 | 139 | // Check the index count 140 | if(currentIndex >= [tickerStrings count]) { 141 | currentIndex = 0; 142 | 143 | // Check if we should loop 144 | if(!loops) { 145 | // Set not running 146 | running = NO; 147 | 148 | return; 149 | } 150 | } 151 | 152 | // Animate 153 | [self animateCurrentTickerString]; 154 | } 155 | 156 | #pragma mark - Ticker Animation Handling 157 | -(void)start { 158 | 159 | // Set the index to 0 on starting 160 | currentIndex = 0; 161 | 162 | // Set running 163 | running = YES; 164 | 165 | // Start the animation 166 | [self animateCurrentTickerString]; 167 | } 168 | 169 | -(void)pause { 170 | 171 | // Check if running 172 | if(running) { 173 | // Pause the layer 174 | [self pauseLayer:self.layer]; 175 | 176 | running = NO; 177 | } 178 | } 179 | 180 | -(void)resume { 181 | 182 | // Check not running 183 | if(!running) { 184 | // Resume the layer 185 | [self resumeLayer:self.layer]; 186 | 187 | running = YES; 188 | } 189 | } 190 | 191 | #pragma mark - UIView layer animations utilities 192 | -(void)pauseLayer:(CALayer *)layer 193 | { 194 | CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; 195 | layer.speed = 0.0; 196 | layer.timeOffset = pausedTime; 197 | } 198 | 199 | -(void)resumeLayer:(CALayer *)layer 200 | { 201 | CFTimeInterval pausedTime = [layer timeOffset]; 202 | layer.speed = 1.0; 203 | layer.timeOffset = 0.0; 204 | layer.beginTime = 0.0; 205 | CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; 206 | layer.beginTime = timeSincePause; 207 | } 208 | 209 | @end 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JHTickerView 2 | ============ 3 | 4 | ## About 5 | 6 | A custom ticker view for iOS 7 | 8 | ![Screenshot](https://github.com/jeffhodnett/JHTickerView/blob/master/ticker.gif) 9 | 10 | ## Credits 11 | 12 | Author: Jeff Hodnett 13 | 14 | Twitter: [@jeffhodnett](http://www.twitter.com/jeffhodnett) 15 | 16 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1FD47FB618798F0600DB5EE8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FD47FB518798F0600DB5EE8 /* Foundation.framework */; }; 11 | 1FD47FB818798F0600DB5EE8 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FD47FB718798F0600DB5EE8 /* CoreGraphics.framework */; }; 12 | 1FD47FBA18798F0600DB5EE8 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FD47FB918798F0600DB5EE8 /* UIKit.framework */; }; 13 | 1FD47FC018798F0600DB5EE8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1FD47FBE18798F0600DB5EE8 /* InfoPlist.strings */; }; 14 | 1FD47FC218798F0600DB5EE8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FD47FC118798F0600DB5EE8 /* main.m */; }; 15 | 1FD47FC618798F0600DB5EE8 /* JHAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FD47FC518798F0600DB5EE8 /* JHAppDelegate.m */; }; 16 | 1FD47FC918798F0600DB5EE8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1FD47FC718798F0600DB5EE8 /* Main.storyboard */; }; 17 | 1FD47FCC18798F0600DB5EE8 /* JHViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FD47FCB18798F0600DB5EE8 /* JHViewController.m */; }; 18 | 1FD47FCE18798F0600DB5EE8 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1FD47FCD18798F0600DB5EE8 /* Images.xcassets */; }; 19 | 1FD47FD518798F0600DB5EE8 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FD47FD418798F0600DB5EE8 /* XCTest.framework */; }; 20 | 1FD47FD618798F0600DB5EE8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FD47FB518798F0600DB5EE8 /* Foundation.framework */; }; 21 | 1FD47FD718798F0600DB5EE8 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FD47FB918798F0600DB5EE8 /* UIKit.framework */; }; 22 | 1FD47FDF18798F0600DB5EE8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1FD47FDD18798F0600DB5EE8 /* InfoPlist.strings */; }; 23 | 1FD47FE118798F0600DB5EE8 /* TickerDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FD47FE018798F0600DB5EE8 /* TickerDemoTests.m */; }; 24 | 1FD47FED18798FD300DB5EE8 /* JHTickerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FD47FEC18798FD300DB5EE8 /* JHTickerView.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 1FD47FD818798F0600DB5EE8 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 1FD47FAA18798F0600DB5EE8 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 1FD47FB118798F0600DB5EE8; 33 | remoteInfo = TickerDemo; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 1FD47FB218798F0600DB5EE8 /* TickerDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TickerDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 1FD47FB518798F0600DB5EE8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 40 | 1FD47FB718798F0600DB5EE8 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 41 | 1FD47FB918798F0600DB5EE8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 42 | 1FD47FBD18798F0600DB5EE8 /* TickerDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TickerDemo-Info.plist"; sourceTree = ""; }; 43 | 1FD47FBF18798F0600DB5EE8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 44 | 1FD47FC118798F0600DB5EE8 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 45 | 1FD47FC318798F0600DB5EE8 /* TickerDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TickerDemo-Prefix.pch"; sourceTree = ""; }; 46 | 1FD47FC418798F0600DB5EE8 /* JHAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JHAppDelegate.h; sourceTree = ""; }; 47 | 1FD47FC518798F0600DB5EE8 /* JHAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JHAppDelegate.m; sourceTree = ""; }; 48 | 1FD47FC818798F0600DB5EE8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | 1FD47FCA18798F0600DB5EE8 /* JHViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JHViewController.h; sourceTree = ""; }; 50 | 1FD47FCB18798F0600DB5EE8 /* JHViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JHViewController.m; sourceTree = ""; }; 51 | 1FD47FCD18798F0600DB5EE8 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 52 | 1FD47FD318798F0600DB5EE8 /* TickerDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TickerDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 1FD47FD418798F0600DB5EE8 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 54 | 1FD47FDC18798F0600DB5EE8 /* TickerDemoTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TickerDemoTests-Info.plist"; sourceTree = ""; }; 55 | 1FD47FDE18798F0600DB5EE8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 56 | 1FD47FE018798F0600DB5EE8 /* TickerDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TickerDemoTests.m; sourceTree = ""; }; 57 | 1FD47FEB18798FD300DB5EE8 /* JHTickerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JHTickerView.h; sourceTree = ""; }; 58 | 1FD47FEC18798FD300DB5EE8 /* JHTickerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JHTickerView.m; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 1FD47FAF18798F0600DB5EE8 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 1FD47FB818798F0600DB5EE8 /* CoreGraphics.framework in Frameworks */, 67 | 1FD47FBA18798F0600DB5EE8 /* UIKit.framework in Frameworks */, 68 | 1FD47FB618798F0600DB5EE8 /* Foundation.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | 1FD47FD018798F0600DB5EE8 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | 1FD47FD518798F0600DB5EE8 /* XCTest.framework in Frameworks */, 77 | 1FD47FD718798F0600DB5EE8 /* UIKit.framework in Frameworks */, 78 | 1FD47FD618798F0600DB5EE8 /* Foundation.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 1FD47FA918798F0600DB5EE8 = { 86 | isa = PBXGroup; 87 | children = ( 88 | 1FD47FBB18798F0600DB5EE8 /* TickerDemo */, 89 | 1FD47FDA18798F0600DB5EE8 /* TickerDemoTests */, 90 | 1FD47FB418798F0600DB5EE8 /* Frameworks */, 91 | 1FD47FB318798F0600DB5EE8 /* Products */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | 1FD47FB318798F0600DB5EE8 /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 1FD47FB218798F0600DB5EE8 /* TickerDemo.app */, 99 | 1FD47FD318798F0600DB5EE8 /* TickerDemoTests.xctest */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 1FD47FB418798F0600DB5EE8 /* Frameworks */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 1FD47FB518798F0600DB5EE8 /* Foundation.framework */, 108 | 1FD47FB718798F0600DB5EE8 /* CoreGraphics.framework */, 109 | 1FD47FB918798F0600DB5EE8 /* UIKit.framework */, 110 | 1FD47FD418798F0600DB5EE8 /* XCTest.framework */, 111 | ); 112 | name = Frameworks; 113 | sourceTree = ""; 114 | }; 115 | 1FD47FBB18798F0600DB5EE8 /* TickerDemo */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 1FD47FEA18798FD300DB5EE8 /* JHTickerView */, 119 | 1FD47FC418798F0600DB5EE8 /* JHAppDelegate.h */, 120 | 1FD47FC518798F0600DB5EE8 /* JHAppDelegate.m */, 121 | 1FD47FC718798F0600DB5EE8 /* Main.storyboard */, 122 | 1FD47FCA18798F0600DB5EE8 /* JHViewController.h */, 123 | 1FD47FCB18798F0600DB5EE8 /* JHViewController.m */, 124 | 1FD47FCD18798F0600DB5EE8 /* Images.xcassets */, 125 | 1FD47FBC18798F0600DB5EE8 /* Supporting Files */, 126 | ); 127 | path = TickerDemo; 128 | sourceTree = ""; 129 | }; 130 | 1FD47FBC18798F0600DB5EE8 /* Supporting Files */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | 1FD47FBD18798F0600DB5EE8 /* TickerDemo-Info.plist */, 134 | 1FD47FBE18798F0600DB5EE8 /* InfoPlist.strings */, 135 | 1FD47FC118798F0600DB5EE8 /* main.m */, 136 | 1FD47FC318798F0600DB5EE8 /* TickerDemo-Prefix.pch */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 1FD47FDA18798F0600DB5EE8 /* TickerDemoTests */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 1FD47FE018798F0600DB5EE8 /* TickerDemoTests.m */, 145 | 1FD47FDB18798F0600DB5EE8 /* Supporting Files */, 146 | ); 147 | path = TickerDemoTests; 148 | sourceTree = ""; 149 | }; 150 | 1FD47FDB18798F0600DB5EE8 /* Supporting Files */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | 1FD47FDC18798F0600DB5EE8 /* TickerDemoTests-Info.plist */, 154 | 1FD47FDD18798F0600DB5EE8 /* InfoPlist.strings */, 155 | ); 156 | name = "Supporting Files"; 157 | sourceTree = ""; 158 | }; 159 | 1FD47FEA18798FD300DB5EE8 /* JHTickerView */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 1FD47FEB18798FD300DB5EE8 /* JHTickerView.h */, 163 | 1FD47FEC18798FD300DB5EE8 /* JHTickerView.m */, 164 | ); 165 | path = JHTickerView; 166 | sourceTree = ""; 167 | }; 168 | /* End PBXGroup section */ 169 | 170 | /* Begin PBXNativeTarget section */ 171 | 1FD47FB118798F0600DB5EE8 /* TickerDemo */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = 1FD47FE418798F0600DB5EE8 /* Build configuration list for PBXNativeTarget "TickerDemo" */; 174 | buildPhases = ( 175 | 1FD47FAE18798F0600DB5EE8 /* Sources */, 176 | 1FD47FAF18798F0600DB5EE8 /* Frameworks */, 177 | 1FD47FB018798F0600DB5EE8 /* Resources */, 178 | ); 179 | buildRules = ( 180 | ); 181 | dependencies = ( 182 | ); 183 | name = TickerDemo; 184 | productName = TickerDemo; 185 | productReference = 1FD47FB218798F0600DB5EE8 /* TickerDemo.app */; 186 | productType = "com.apple.product-type.application"; 187 | }; 188 | 1FD47FD218798F0600DB5EE8 /* TickerDemoTests */ = { 189 | isa = PBXNativeTarget; 190 | buildConfigurationList = 1FD47FE718798F0600DB5EE8 /* Build configuration list for PBXNativeTarget "TickerDemoTests" */; 191 | buildPhases = ( 192 | 1FD47FCF18798F0600DB5EE8 /* Sources */, 193 | 1FD47FD018798F0600DB5EE8 /* Frameworks */, 194 | 1FD47FD118798F0600DB5EE8 /* Resources */, 195 | ); 196 | buildRules = ( 197 | ); 198 | dependencies = ( 199 | 1FD47FD918798F0600DB5EE8 /* PBXTargetDependency */, 200 | ); 201 | name = TickerDemoTests; 202 | productName = TickerDemoTests; 203 | productReference = 1FD47FD318798F0600DB5EE8 /* TickerDemoTests.xctest */; 204 | productType = "com.apple.product-type.bundle.unit-test"; 205 | }; 206 | /* End PBXNativeTarget section */ 207 | 208 | /* Begin PBXProject section */ 209 | 1FD47FAA18798F0600DB5EE8 /* Project object */ = { 210 | isa = PBXProject; 211 | attributes = { 212 | CLASSPREFIX = JH; 213 | LastUpgradeCheck = 0500; 214 | ORGANIZATIONNAME = "Jeff Hodnett"; 215 | TargetAttributes = { 216 | 1FD47FD218798F0600DB5EE8 = { 217 | TestTargetID = 1FD47FB118798F0600DB5EE8; 218 | }; 219 | }; 220 | }; 221 | buildConfigurationList = 1FD47FAD18798F0600DB5EE8 /* Build configuration list for PBXProject "TickerDemo" */; 222 | compatibilityVersion = "Xcode 3.2"; 223 | developmentRegion = English; 224 | hasScannedForEncodings = 0; 225 | knownRegions = ( 226 | en, 227 | Base, 228 | ); 229 | mainGroup = 1FD47FA918798F0600DB5EE8; 230 | productRefGroup = 1FD47FB318798F0600DB5EE8 /* Products */; 231 | projectDirPath = ""; 232 | projectRoot = ""; 233 | targets = ( 234 | 1FD47FB118798F0600DB5EE8 /* TickerDemo */, 235 | 1FD47FD218798F0600DB5EE8 /* TickerDemoTests */, 236 | ); 237 | }; 238 | /* End PBXProject section */ 239 | 240 | /* Begin PBXResourcesBuildPhase section */ 241 | 1FD47FB018798F0600DB5EE8 /* Resources */ = { 242 | isa = PBXResourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | 1FD47FCE18798F0600DB5EE8 /* Images.xcassets in Resources */, 246 | 1FD47FC018798F0600DB5EE8 /* InfoPlist.strings in Resources */, 247 | 1FD47FC918798F0600DB5EE8 /* Main.storyboard in Resources */, 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | }; 251 | 1FD47FD118798F0600DB5EE8 /* Resources */ = { 252 | isa = PBXResourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | 1FD47FDF18798F0600DB5EE8 /* InfoPlist.strings in Resources */, 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | /* End PBXResourcesBuildPhase section */ 260 | 261 | /* Begin PBXSourcesBuildPhase section */ 262 | 1FD47FAE18798F0600DB5EE8 /* Sources */ = { 263 | isa = PBXSourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | 1FD47FED18798FD300DB5EE8 /* JHTickerView.m in Sources */, 267 | 1FD47FC618798F0600DB5EE8 /* JHAppDelegate.m in Sources */, 268 | 1FD47FCC18798F0600DB5EE8 /* JHViewController.m in Sources */, 269 | 1FD47FC218798F0600DB5EE8 /* main.m in Sources */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | 1FD47FCF18798F0600DB5EE8 /* Sources */ = { 274 | isa = PBXSourcesBuildPhase; 275 | buildActionMask = 2147483647; 276 | files = ( 277 | 1FD47FE118798F0600DB5EE8 /* TickerDemoTests.m in Sources */, 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | /* End PBXSourcesBuildPhase section */ 282 | 283 | /* Begin PBXTargetDependency section */ 284 | 1FD47FD918798F0600DB5EE8 /* PBXTargetDependency */ = { 285 | isa = PBXTargetDependency; 286 | target = 1FD47FB118798F0600DB5EE8 /* TickerDemo */; 287 | targetProxy = 1FD47FD818798F0600DB5EE8 /* PBXContainerItemProxy */; 288 | }; 289 | /* End PBXTargetDependency section */ 290 | 291 | /* Begin PBXVariantGroup section */ 292 | 1FD47FBE18798F0600DB5EE8 /* InfoPlist.strings */ = { 293 | isa = PBXVariantGroup; 294 | children = ( 295 | 1FD47FBF18798F0600DB5EE8 /* en */, 296 | ); 297 | name = InfoPlist.strings; 298 | sourceTree = ""; 299 | }; 300 | 1FD47FC718798F0600DB5EE8 /* Main.storyboard */ = { 301 | isa = PBXVariantGroup; 302 | children = ( 303 | 1FD47FC818798F0600DB5EE8 /* Base */, 304 | ); 305 | name = Main.storyboard; 306 | sourceTree = ""; 307 | }; 308 | 1FD47FDD18798F0600DB5EE8 /* InfoPlist.strings */ = { 309 | isa = PBXVariantGroup; 310 | children = ( 311 | 1FD47FDE18798F0600DB5EE8 /* en */, 312 | ); 313 | name = InfoPlist.strings; 314 | sourceTree = ""; 315 | }; 316 | /* End PBXVariantGroup section */ 317 | 318 | /* Begin XCBuildConfiguration section */ 319 | 1FD47FE218798F0600DB5EE8 /* Debug */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BOOL_CONVERSION = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 331 | CLANG_WARN_EMPTY_BODY = YES; 332 | CLANG_WARN_ENUM_CONVERSION = YES; 333 | CLANG_WARN_INT_CONVERSION = YES; 334 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 335 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 336 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 337 | COPY_PHASE_STRIP = NO; 338 | GCC_C_LANGUAGE_STANDARD = gnu99; 339 | GCC_DYNAMIC_NO_PIC = NO; 340 | GCC_OPTIMIZATION_LEVEL = 0; 341 | GCC_PREPROCESSOR_DEFINITIONS = ( 342 | "DEBUG=1", 343 | "$(inherited)", 344 | ); 345 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 353 | ONLY_ACTIVE_ARCH = YES; 354 | SDKROOT = iphoneos; 355 | }; 356 | name = Debug; 357 | }; 358 | 1FD47FE318798F0600DB5EE8 /* Release */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | ALWAYS_SEARCH_USER_PATHS = NO; 362 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 363 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 364 | CLANG_CXX_LIBRARY = "libc++"; 365 | CLANG_ENABLE_MODULES = YES; 366 | CLANG_ENABLE_OBJC_ARC = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_CONSTANT_CONVERSION = YES; 369 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 370 | CLANG_WARN_EMPTY_BODY = YES; 371 | CLANG_WARN_ENUM_CONVERSION = YES; 372 | CLANG_WARN_INT_CONVERSION = YES; 373 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 374 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 375 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 376 | COPY_PHASE_STRIP = YES; 377 | ENABLE_NS_ASSERTIONS = NO; 378 | GCC_C_LANGUAGE_STANDARD = gnu99; 379 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 380 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 381 | GCC_WARN_UNDECLARED_SELECTOR = YES; 382 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 383 | GCC_WARN_UNUSED_FUNCTION = YES; 384 | GCC_WARN_UNUSED_VARIABLE = YES; 385 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 386 | SDKROOT = iphoneos; 387 | VALIDATE_PRODUCT = YES; 388 | }; 389 | name = Release; 390 | }; 391 | 1FD47FE518798F0600DB5EE8 /* Debug */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 395 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 396 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 397 | GCC_PREFIX_HEADER = "TickerDemo/TickerDemo-Prefix.pch"; 398 | INFOPLIST_FILE = "TickerDemo/TickerDemo-Info.plist"; 399 | PRODUCT_NAME = "$(TARGET_NAME)"; 400 | WRAPPER_EXTENSION = app; 401 | }; 402 | name = Debug; 403 | }; 404 | 1FD47FE618798F0600DB5EE8 /* Release */ = { 405 | isa = XCBuildConfiguration; 406 | buildSettings = { 407 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 408 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 409 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 410 | GCC_PREFIX_HEADER = "TickerDemo/TickerDemo-Prefix.pch"; 411 | INFOPLIST_FILE = "TickerDemo/TickerDemo-Info.plist"; 412 | PRODUCT_NAME = "$(TARGET_NAME)"; 413 | WRAPPER_EXTENSION = app; 414 | }; 415 | name = Release; 416 | }; 417 | 1FD47FE818798F0600DB5EE8 /* Debug */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 421 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TickerDemo.app/TickerDemo"; 422 | FRAMEWORK_SEARCH_PATHS = ( 423 | "$(SDKROOT)/Developer/Library/Frameworks", 424 | "$(inherited)", 425 | "$(DEVELOPER_FRAMEWORKS_DIR)", 426 | ); 427 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 428 | GCC_PREFIX_HEADER = "TickerDemo/TickerDemo-Prefix.pch"; 429 | GCC_PREPROCESSOR_DEFINITIONS = ( 430 | "DEBUG=1", 431 | "$(inherited)", 432 | ); 433 | INFOPLIST_FILE = "TickerDemoTests/TickerDemoTests-Info.plist"; 434 | PRODUCT_NAME = "$(TARGET_NAME)"; 435 | TEST_HOST = "$(BUNDLE_LOADER)"; 436 | WRAPPER_EXTENSION = xctest; 437 | }; 438 | name = Debug; 439 | }; 440 | 1FD47FE918798F0600DB5EE8 /* Release */ = { 441 | isa = XCBuildConfiguration; 442 | buildSettings = { 443 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 444 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/TickerDemo.app/TickerDemo"; 445 | FRAMEWORK_SEARCH_PATHS = ( 446 | "$(SDKROOT)/Developer/Library/Frameworks", 447 | "$(inherited)", 448 | "$(DEVELOPER_FRAMEWORKS_DIR)", 449 | ); 450 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 451 | GCC_PREFIX_HEADER = "TickerDemo/TickerDemo-Prefix.pch"; 452 | INFOPLIST_FILE = "TickerDemoTests/TickerDemoTests-Info.plist"; 453 | PRODUCT_NAME = "$(TARGET_NAME)"; 454 | TEST_HOST = "$(BUNDLE_LOADER)"; 455 | WRAPPER_EXTENSION = xctest; 456 | }; 457 | name = Release; 458 | }; 459 | /* End XCBuildConfiguration section */ 460 | 461 | /* Begin XCConfigurationList section */ 462 | 1FD47FAD18798F0600DB5EE8 /* Build configuration list for PBXProject "TickerDemo" */ = { 463 | isa = XCConfigurationList; 464 | buildConfigurations = ( 465 | 1FD47FE218798F0600DB5EE8 /* Debug */, 466 | 1FD47FE318798F0600DB5EE8 /* Release */, 467 | ); 468 | defaultConfigurationIsVisible = 0; 469 | defaultConfigurationName = Release; 470 | }; 471 | 1FD47FE418798F0600DB5EE8 /* Build configuration list for PBXNativeTarget "TickerDemo" */ = { 472 | isa = XCConfigurationList; 473 | buildConfigurations = ( 474 | 1FD47FE518798F0600DB5EE8 /* Debug */, 475 | 1FD47FE618798F0600DB5EE8 /* Release */, 476 | ); 477 | defaultConfigurationIsVisible = 0; 478 | }; 479 | 1FD47FE718798F0600DB5EE8 /* Build configuration list for PBXNativeTarget "TickerDemoTests" */ = { 480 | isa = XCConfigurationList; 481 | buildConfigurations = ( 482 | 1FD47FE818798F0600DB5EE8 /* Debug */, 483 | 1FD47FE918798F0600DB5EE8 /* Release */, 484 | ); 485 | defaultConfigurationIsVisible = 0; 486 | }; 487 | /* End XCConfigurationList section */ 488 | }; 489 | rootObject = 1FD47FAA18798F0600DB5EE8 /* Project object */; 490 | } 491 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo.xcodeproj/project.xcworkspace/xcshareddata/TickerDemo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 1EEFEB1B-D1F3-4EDB-A958-FCAF484DB62A 9 | IDESourceControlProjectName 10 | TickerDemo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | BAF6EE36-0CF5-49E9-AD1B-0E53B21AACC8 14 | ssh://github.com/jeffhodnett/JHTickerView.git 15 | 16 | IDESourceControlProjectPath 17 | TickerDemo/TickerDemo.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | BAF6EE36-0CF5-49E9-AD1B-0E53B21AACC8 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | ssh://github.com/jeffhodnett/JHTickerView.git 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | BAF6EE36-0CF5-49E9-AD1B-0E53B21AACC8 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | BAF6EE36-0CF5-49E9-AD1B-0E53B21AACC8 36 | IDESourceControlWCCName 37 | JHTickerView 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo.xcodeproj/project.xcworkspace/xcuserdata/jeff.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffhodnett/JHTickerView/00f5359d15a5471b02e7083846e2456353621dd3/TickerDemo/TickerDemo.xcodeproj/project.xcworkspace/xcuserdata/jeff.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TickerDemo/TickerDemo.xcodeproj/project.xcworkspace/xcuserdata/jeff.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo.xcodeproj/xcuserdata/jeff.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo.xcodeproj/xcuserdata/jeff.xcuserdatad/xcschemes/TickerDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo.xcodeproj/xcuserdata/jeff.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TickerDemo.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 1FD47FB118798F0600DB5EE8 16 | 17 | primary 18 | 19 | 20 | 1FD47FD218798F0600DB5EE8 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/JHAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // JHAppDelegate.h 3 | // TickerDemo 4 | // 5 | // Created by Jeff Hodnett on 1/5/14. 6 | // Copyright (c) 2014 Jeff Hodnett. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JHAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/JHAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // JHAppDelegate.m 3 | // TickerDemo 4 | // 5 | // Created by Jeff Hodnett on 1/5/14. 6 | // Copyright (c) 2014 Jeff Hodnett. All rights reserved. 7 | // 8 | 9 | #import "JHAppDelegate.h" 10 | 11 | @implementation JHAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | return YES; 17 | } 18 | 19 | - (void)applicationWillResignActive:(UIApplication *)application 20 | { 21 | // 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. 22 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 23 | } 24 | 25 | - (void)applicationDidEnterBackground:(UIApplication *)application 26 | { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | - (void)applicationWillEnterForeground:(UIApplication *)application 32 | { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | - (void)applicationDidBecomeActive:(UIApplication *)application 37 | { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application 42 | { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/JHTickerView/JHTickerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // JHTickerView.h 3 | // Ticker 4 | // 5 | // Created by Jeff Hodnett on 03/05/2011. 6 | // Copyright 2011 Applausible. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum 12 | { 13 | JHTickerDirectionLTR, 14 | JHTickerDirectionRTL, 15 | } JHTickerDirection; 16 | 17 | @interface JHTickerView : UIView 18 | 19 | // The ticker speed 20 | @property(nonatomic) CGFloat tickerSpeed; 21 | 22 | // Should the ticker loop 23 | @property(nonatomic) BOOL loops; 24 | 25 | // The ticker animation direction 26 | @property(nonatomic) JHTickerDirection direction; 27 | 28 | // The ticker font 29 | -(void)setTickerFont:(UIFont *)font; 30 | 31 | // Set text either normal NSString or NSAttributedString 32 | -(void)setTickerText:(NSArray *)text; 33 | -(void)addTickerText:(id)text; 34 | -(void)removeAllTickerText; 35 | 36 | // Start the ticker 37 | -(void)start; 38 | 39 | // Pause the ticker 40 | -(void)pause; 41 | 42 | // Resume the ticker 43 | -(void)resume; 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/JHTickerView/JHTickerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // JHTickerView.m 3 | // Ticker 4 | // 5 | // Created by Jeff Hodnett on 03/05/2011. 6 | // Copyright 2011 Applausible. All rights reserved. 7 | // 8 | 9 | #import "JHTickerView.h" 10 | #import 11 | 12 | // Defaults 13 | static NSString *kDefaultTickerFontName = @"Marker Felt"; 14 | static const BOOL kDefaultTickerDoesLoop = YES; 15 | static const JHTickerDirection kDefaultTickerDirection = JHTickerDirectionLTR; 16 | 17 | @interface JHTickerView() 18 | { 19 | // The current index for the string 20 | int _currentIndex; 21 | 22 | // The current state of the ticker 23 | BOOL _isRunning; 24 | 25 | // The ticker label 26 | UILabel *_tickerLabel; 27 | } 28 | 29 | @property(nonatomic, strong) UIFont *font; 30 | @property(nonatomic, strong) NSMutableArray *tickerStrings; 31 | 32 | -(void)setupView; 33 | -(void)animateCurrentTickerString; 34 | -(void)pauseLayer:(CALayer *)layer; 35 | -(void)resumeLayer:(CALayer *)layer; 36 | @end 37 | 38 | @implementation JHTickerView 39 | 40 | - (id)initWithFrame:(CGRect)frame 41 | { 42 | self = [super initWithFrame:frame]; 43 | if (self) { 44 | // Initialization code 45 | [self setupView]; 46 | } 47 | return self; 48 | } 49 | 50 | -(id)initWithCoder:(NSCoder *)aDecoder { 51 | if( (self = [super initWithCoder:aDecoder]) ) { 52 | // Initialization code 53 | [self setupView]; 54 | } 55 | return self; 56 | } 57 | 58 | -(void)setTickerSpeed:(CGFloat)tickerSpeed 59 | { 60 | _tickerSpeed = tickerSpeed; 61 | 62 | // Disallow less than zero ticker speeds 63 | if(_tickerSpeed <= 0.0f) { 64 | _tickerSpeed = 0.1f; 65 | } 66 | } 67 | 68 | #if !__has_feature(objc_arc) 69 | - (void)dealloc 70 | { 71 | [_tickerLabel release]; 72 | [_font release]; 73 | [_tickerStrings release]; 74 | 75 | [super dealloc]; 76 | } 77 | #endif 78 | 79 | -(void)setupView 80 | { 81 | // Set background color to white 82 | [self setBackgroundColor:[UIColor whiteColor]]; 83 | 84 | // Set a corner radius 85 | [self.layer setCornerRadius:5.0f]; 86 | [self.layer setBorderWidth:2.0f]; 87 | [self.layer setBorderColor:[UIColor blackColor].CGColor]; 88 | [self setClipsToBounds:YES]; 89 | 90 | // Set the font 91 | self.font = [UIFont fontWithName:kDefaultTickerFontName size:22.0]; 92 | 93 | // Add the ticker label 94 | _tickerLabel = [[UILabel alloc] initWithFrame:self.bounds]; 95 | [_tickerLabel setBackgroundColor:[UIColor clearColor]]; 96 | [_tickerLabel setNumberOfLines:1]; 97 | [_tickerLabel setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 98 | [_tickerLabel setFont:self.font]; 99 | [_tickerLabel setAdjustsFontSizeToFitWidth:YES]; 100 | [self addSubview:_tickerLabel]; 101 | 102 | // Set that it loops by default 103 | self.loops = kDefaultTickerDoesLoop; 104 | 105 | // Set the default direction 106 | self.direction = kDefaultTickerDirection; 107 | } 108 | 109 | -(void)setTickerFont:(UIFont *)font 110 | { 111 | self.font = font; 112 | [_tickerLabel setFont:self.font]; 113 | } 114 | 115 | -(void)setTickerText:(NSArray *)text 116 | { 117 | // Error check 118 | if (text == nil || [text count] == 0) { 119 | return; 120 | } 121 | 122 | self.tickerStrings = [NSMutableArray arrayWithArray:text]; 123 | } 124 | 125 | -(void)addTickerText:(id)text 126 | { 127 | [self.tickerStrings addObject:text]; 128 | } 129 | 130 | -(void)removeAllTickerText 131 | { 132 | [self.tickerStrings removeAllObjects]; 133 | } 134 | 135 | -(void)animateCurrentTickerString 136 | { 137 | id currentTickerString = [_tickerStrings objectAtIndex:_currentIndex]; 138 | 139 | // Calculate the size of the text and update the frame size of the ticker label 140 | CGSize textSize = CGSizeZero; 141 | CGSize maxSize = CGSizeMake(MAXFLOAT, CGRectGetHeight(self.bounds)); 142 | NSString *currentString = nil; 143 | if([currentTickerString isKindOfClass:[NSAttributedString class]]) { 144 | currentString = [currentTickerString string]; 145 | } 146 | else { 147 | currentString = currentTickerString; 148 | } 149 | 150 | // Calculate size 151 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000 152 | NSDictionary *textAttributes = nil; 153 | if([currentTickerString isKindOfClass:[NSAttributedString class]]) { 154 | // Use the NSAttributedString attributes 155 | textAttributes = [currentTickerString attributesAtIndex:0 effectiveRange:NULL]; 156 | } 157 | else { 158 | // Use this labels attributes 159 | textAttributes = @{ 160 | NSFontAttributeName: _tickerLabel.font 161 | }; 162 | } 163 | CGRect textRect = [currentString boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:textAttributes context:nil]; 164 | textSize = textRect.size; 165 | #else 166 | textSize = [currentString sizeWithFont:_tickerLabel.font constrainedToSize:maxSize lineBreakMode:UILineBreakModeWordWrap]; 167 | #endif 168 | 169 | // Setup some starting and end points 170 | CGFloat startingX = 0.0f; 171 | CGFloat endX = 0.0f; 172 | switch (self.direction) { 173 | case JHTickerDirectionRTL: 174 | startingX = -textSize.width; 175 | endX = self.frame.size.width; 176 | break; 177 | case JHTickerDirectionLTR: 178 | default: 179 | startingX = self.frame.size.width; 180 | endX = -textSize.width; 181 | break; 182 | } 183 | 184 | // Set starting position 185 | [_tickerLabel setFrame:CGRectMake(startingX, _tickerLabel.frame.origin.y, textSize.width, maxSize.height)]; 186 | 187 | // Set the string 188 | if([currentTickerString isKindOfClass:[NSAttributedString class]]) { 189 | [_tickerLabel setAttributedText:currentTickerString]; 190 | } 191 | else { 192 | [_tickerLabel setText:currentString]; 193 | } 194 | 195 | // Calculate a uniform duration for the item 196 | float duration = (textSize.width + self.frame.size.width) / self.tickerSpeed; 197 | 198 | // Create animation 199 | [UIView animateWithDuration:duration delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^{ 200 | // Update end position 201 | CGRect tickerFrame = _tickerLabel.frame; 202 | tickerFrame.origin.x = endX; 203 | [_tickerLabel setFrame:tickerFrame]; 204 | } completion:^(BOOL finished) { 205 | [self tickerAnimationCompleted]; 206 | }]; 207 | } 208 | 209 | //-(void)tickerMoveAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context 210 | -(void)tickerAnimationCompleted 211 | { 212 | // Update the index 213 | _currentIndex++; 214 | 215 | // Check the index count 216 | if(_currentIndex >= [_tickerStrings count]) { 217 | _currentIndex = 0; 218 | 219 | // Check if we should loop 220 | if(!self.loops) { 221 | // Set not running 222 | _isRunning = NO; 223 | 224 | return; 225 | } 226 | } 227 | 228 | // Animate 229 | [self animateCurrentTickerString]; 230 | } 231 | 232 | #pragma mark - Ticker Animation Handling 233 | -(void)start 234 | { 235 | // Set the index to 0 on starting 236 | _currentIndex = 0; 237 | 238 | // Set running 239 | _isRunning = YES; 240 | 241 | // Start the animation 242 | [self animateCurrentTickerString]; 243 | } 244 | 245 | -(void)pause 246 | { 247 | // Check if running 248 | if(_isRunning) { 249 | // Pause the layer 250 | [self pauseLayer:self.layer]; 251 | 252 | _isRunning = NO; 253 | } 254 | } 255 | 256 | -(void)resume 257 | { 258 | // Check not running 259 | if(!_isRunning) { 260 | // Resume the layer 261 | [self resumeLayer:self.layer]; 262 | 263 | _isRunning = YES; 264 | } 265 | } 266 | 267 | #pragma mark - UIView layer animations utilities 268 | -(void)pauseLayer:(CALayer *)layer 269 | { 270 | CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; 271 | layer.speed = 0.0; 272 | layer.timeOffset = pausedTime; 273 | } 274 | 275 | -(void)resumeLayer:(CALayer *)layer 276 | { 277 | CFTimeInterval pausedTime = [layer timeOffset]; 278 | layer.speed = 1.0; 279 | layer.timeOffset = 0.0; 280 | layer.beginTime = 0.0; 281 | CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime; 282 | layer.beginTime = timeSincePause; 283 | } 284 | 285 | @end 286 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/JHViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // JHViewController.h 3 | // TickerDemo 4 | // 5 | // Created by Jeff Hodnett on 1/5/14. 6 | // Copyright (c) 2014 Jeff Hodnett. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JHViewController : UIViewController 12 | 13 | -(IBAction)addTickerText:(id)sender; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/JHViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // JHViewController.m 3 | // TickerDemo 4 | // 5 | // Created by Jeff Hodnett on 1/5/14. 6 | // Copyright (c) 2014 Jeff Hodnett. All rights reserved. 7 | // 8 | 9 | #import "JHViewController.h" 10 | #import "JHTickerView.h" 11 | 12 | @interface JHViewController () 13 | { 14 | JHTickerView *_tickerNormal; 15 | JHTickerView *_tickerAttributed; 16 | } 17 | 18 | @end 19 | 20 | @implementation JHViewController 21 | 22 | - (void)viewDidLoad 23 | { 24 | [super viewDidLoad]; 25 | 26 | CGFloat runningY = 100.0f; 27 | CGRect tickerFrame = CGRectMake(10.0f, runningY, 300.0f, 50.0f); 28 | CGFloat tickerSpeed = 100.0f; 29 | 30 | // Add a normal string based ticker 31 | _tickerNormal = [[JHTickerView alloc] initWithFrame:tickerFrame]; 32 | [_tickerNormal setDirection:JHTickerDirectionLTR]; 33 | [_tickerNormal setTickerText:@[@"JHTickerView - A custom ticker view for iOS!"]]; 34 | [_tickerNormal setTickerFont:[UIFont fontWithName:@"Arial" size:21.0f]]; 35 | [_tickerNormal setTickerSpeed:tickerSpeed]; 36 | [_tickerNormal start]; 37 | [self.view addSubview:_tickerNormal]; 38 | 39 | tickerFrame.origin.y += tickerFrame.size.height + 20.0f; 40 | 41 | NSArray *tickerStrings = @[ 42 | @"We're no strangers to love, You know the rules and so do I,", 43 | @"A full commitment's what I'm thinking of, You wouldn't get this from any other guy.....", 44 | @"I just wanna tell you how I'm feeling, Gotta make you understand.....", 45 | @"Never gonna give you up, Never gonna let you down, Never gonna run around and desert you.....", 46 | @"Never gonna make you cry, Never gonna say goodbye, Never gonna tell a lie and hurt you....."]; 47 | 48 | // Add attributed text 49 | NSMutableArray *attributedText = [NSMutableArray array]; 50 | NSInteger attributedIndex = 0; 51 | for (NSString *str in tickerStrings) { 52 | // Create some attributed strings 53 | NSAttributedString *text = [self createRandomAttributedStringWithText:str]; 54 | [attributedText addObject:text]; 55 | attributedIndex++; 56 | } 57 | 58 | _tickerAttributed = [[JHTickerView alloc] initWithFrame:tickerFrame]; 59 | [_tickerAttributed setDirection:JHTickerDirectionLTR]; 60 | [_tickerAttributed setTickerText:attributedText]; 61 | [_tickerAttributed setTickerSpeed:tickerSpeed]; 62 | [_tickerAttributed start]; 63 | [self.view addSubview:_tickerAttributed]; 64 | } 65 | 66 | -(NSAttributedString *)createRandomAttributedStringWithText:(NSString *)text 67 | { 68 | // Generate some random attributes 69 | NSArray *familyNames = [UIFont familyNames]; 70 | NSString *randomFontName = [familyNames objectAtIndex:[self randomFrom:0 to:[familyNames count]]]; 71 | NSInteger randomFontPointSize = [self randomFrom:12.0f to:32.0f]; 72 | UIFont *randomFont = [UIFont fontWithName:randomFontName size:randomFontPointSize]; 73 | 74 | UIColor *randomForegroundColor = [UIColor blackColor]; 75 | 76 | NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text 77 | attributes:@{ 78 | NSFontAttributeName: randomFont, 79 | NSForegroundColorAttributeName: randomForegroundColor, 80 | }]; 81 | return attributedText; 82 | } 83 | 84 | -(NSInteger)randomFrom:(NSInteger)fromNumber to:(NSInteger)toNumber 85 | { 86 | return (arc4random()%(toNumber-fromNumber))+fromNumber; 87 | } 88 | 89 | -(IBAction)addTickerText:(id)sender 90 | { 91 | static int index = 0; 92 | [_tickerNormal addTickerText:[NSString stringWithFormat:@"Hello Ticker World %d!!", ++index]]; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/TickerDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.something.${PRODUCT_NAME:rfc1034identifier} 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 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/TickerDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TickerDemo 4 | // 5 | // Created by Jeff Hodnett on 1/5/14. 6 | // Copyright (c) 2014 Jeff Hodnett. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JHAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([JHAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemoTests/TickerDemoTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.something.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemoTests/TickerDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TickerDemoTests.m 3 | // TickerDemoTests 4 | // 5 | // Created by Jeff Hodnett on 1/5/14. 6 | // Copyright (c) 2014 Jeff Hodnett. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TickerDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation TickerDemoTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /TickerDemo/TickerDemoTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /ticker.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeffhodnett/JHTickerView/00f5359d15a5471b02e7083846e2456353621dd3/ticker.gif --------------------------------------------------------------------------------