Permalink
Browse files

Implemented scrolling credits.

  • Loading branch information...
1 parent 60ae16c commit 5ce98478a58f9c5b21f7742429ca924cb535ed37 @tomaz committed Mar 8, 2012
View
11 ScrollingAboutWindow/AboutWindow.xib
@@ -217,14 +217,15 @@
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="787351964"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
- <string key="NSClassName">NSView</string>
+ <string key="NSClassName">BackgroundColorView</string>
</object>
<object class="NSButton" id="701293670">
<reference key="NSNextResponder" ref="564727303"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{385, 0}, {95, 28}}</string>
<reference key="NSSuperview" ref="564727303"/>
<reference key="NSWindow"/>
+ <reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="536915794">
@@ -1165,6 +1166,14 @@
</object>
</object>
<object class="IBPartialClassDescription">
+ <string key="className">BackgroundColorView</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/BackgroundColorView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
<string key="className">NSLayoutConstraint</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
View
6 ScrollingAboutWindow/AboutWindowController.h
@@ -19,3 +19,9 @@
@property (weak) IBOutlet NSView *creditsView;
@end
+
+#pragma mark -
+
+@interface BackgroundColorView : NSView
+@property (nonatomic, strong) NSColor *gb_backgroundColor;
+@end
View
177 ScrollingAboutWindow/AboutWindowController.m
@@ -9,17 +9,45 @@
#import <QuartzCore/QuartzCore.h>
#import "AboutWindowController.h"
+static CGFloat kAboutWindowCreditsAnimationDuration = 30.0;
+static CGFloat kAboutWindowCreditsFadeHeight = 6.0;
+static CGColorRef kAboutWindowCreditsFadeColor1 = NULL;
+static CGColorRef kAboutWindowCreditsFadeColor2 = NULL;
+
+#pragma mark -
+
@interface AboutWindowController ()
@property (nonatomic, readonly) NSString *applicationNameString;
@property (nonatomic, readonly) NSString *applicationVersionString;
@property (nonatomic, readonly) NSString *applicationBuildNumberString;
@property (nonatomic, readonly) NSString *applicationCopyrightString;
+@property (nonatomic, assign) BOOL isCreditsAnimationActive;
+@end
+
+#pragma mark -
+
+@interface AboutWindowController (ScrollingCredits)
+- (void)startCreditsScrollAnimation;
+- (void)stopCreditsScrollAnimation;
+- (void)resetCreditsScrollPosition;
+- (CGSize)sizeForAttributedString:(NSAttributedString *)string inWidth:(CGFloat)width;
+@property (nonatomic, readonly) CGFloat creditsFadeHeightCompensation;
+@property (nonatomic, readonly) CALayer *creditsRootLayer;
+@property (nonatomic, readonly) CAGradientLayer *creditsTopFadeLayer;
+@property (nonatomic, readonly) CAGradientLayer *creditsBottomFadeLayer;
+@property (nonatomic, readonly) CATextLayer *creditsTextLayer;
@end
#pragma mark -
-@implementation AboutWindowController
+@implementation AboutWindowController {
+ CALayer *_creditsRootLayer;
+ CAGradientLayer *_creditsTopFadeLayer;
+ CAGradientLayer *_creditsBottomFadeLayer;
+ CATextLayer *_creditsTextLayer;
+}
+@synthesize isCreditsAnimationActive;
@synthesize applicationNameLabel;
@synthesize applicationVersionLabel;
@synthesize punchLineLabel;
@@ -28,6 +56,11 @@ @implementation AboutWindowController
#pragma mark - Initialization & disposal
++ (void)initialize {
+ kAboutWindowCreditsFadeColor1 = CGColorCreateGenericGray(1.0, 1.0);
+ kAboutWindowCreditsFadeColor2 = CGColorCreateGenericGray(1.0, 0.0);
+}
+
- (id)init {
self = [super initWithWindowNibName:@"AboutWindow"];
return self;
@@ -40,10 +73,21 @@ - (void)awakeFromNib {
NSString *versionString = [NSString stringWithFormat:versionFormat, self.applicationVersionString, self.applicationBuildNumberString];
self.applicationNameLabel.stringValue = self.applicationNameString;
self.punchLineLabel.stringValue = NSLocalizedString(@"Scrolling about window demonstrator!", nil);
+ self.creditsView.layer = self.creditsRootLayer;
+ self.creditsView.wantsLayer = YES;
[self.applicationVersionLabel.cell setPlaceholderString:versionString];
[self.copyrightLabel.cell setPlaceholderString:self.applicationCopyrightString];
}
+- (void)showWindow:(id)sender {
+ [super showWindow:sender];
+ [self startCreditsScrollAnimation];
+}
+
+- (void)windowWillClose:(NSNotification *)note {
+ [self stopCreditsScrollAnimation];
+}
+
#pragma mark - User actions
- (IBAction)getInTouch:(id)sender {
@@ -70,3 +114,134 @@ - (NSString *)applicationCopyrightString {
}
@end
+
+#pragma mark -
+
+@implementation AboutWindowController (ScrollingCredits)
+
+#pragma mark - Higher level
+
+- (void)startCreditsScrollAnimation {
+ CATextLayer *creditsLayer = self.creditsTextLayer;
+ CGFloat viewHeight = self.creditsView.bounds.size.height;
+ CGFloat fadeCompensation = self.creditsFadeHeightCompensation;
+
+ // Enable animation and reset.
+ self.isCreditsAnimationActive = YES;
+ [self resetCreditsScrollPosition];
+
+ // Animate to top and execute animation again - resulting in endless loop.
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:kAboutWindowCreditsAnimationDuration];
+ [CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
+ [CATransaction setCompletionBlock:^{
+ if (!self.isCreditsAnimationActive) return;
+ [self startCreditsScrollAnimation];
+ }];
+ creditsLayer.position = CGPointMake(0.0, viewHeight - fadeCompensation);
+ [CATransaction commit];
+}
+
+- (void)stopCreditsScrollAnimation {
+ self.isCreditsAnimationActive = NO;
+ [self resetCreditsScrollPosition];
+}
+
+- (void)resetCreditsScrollPosition {
+ CATextLayer *creditsLayer = self.creditsTextLayer;
+ CGFloat textHeight = creditsLayer.frame.size.height;
+ CGFloat fadeCompensation = self.creditsFadeHeightCompensation;
+
+ [CATransaction begin];
+ [CATransaction setAnimationDuration:0.0];
+ creditsLayer.position = CGPointMake(0.0, -textHeight + fadeCompensation);
+ [CATransaction commit];
+}
+
+- (CGFloat)creditsFadeHeightCompensation {
+ return self.creditsTopFadeLayer.frame.size.height - 2.0;
+}
+
+#pragma mark - Core Animation layers
+
+- (CATextLayer *)creditsTextLayer {
+ if (_creditsTextLayer) return _creditsTextLayer;
+ NSString *path = [[NSBundle mainBundle] pathForResource:@"Credits" ofType:@"rtf"];
+ NSAttributedString *credits = [[NSAttributedString alloc] initWithPath:path documentAttributes:nil];
+ CGSize size = [self sizeForAttributedString:credits inWidth:self.creditsView.bounds.size.width];
+ _creditsTextLayer = [CATextLayer layer];
+ _creditsTextLayer.wrapped = YES;
+ _creditsTextLayer.string = credits;
+ _creditsTextLayer.anchorPoint = CGPointMake(0.0, 0.0);
+ _creditsTextLayer.frame = CGRectMake(0.0, 0.0, size.width, size.height);
+ return _creditsTextLayer;
+}
+
+- (CAGradientLayer *)creditsTopFadeLayer {
+ if (_creditsTopFadeLayer) return _creditsTopFadeLayer;
+ CGColorRef color1 = kAboutWindowCreditsFadeColor1;
+ CGColorRef color2 = kAboutWindowCreditsFadeColor2;
+ CGFloat height = kAboutWindowCreditsFadeHeight;
+ _creditsTopFadeLayer = [CAGradientLayer layer];
+ _creditsTopFadeLayer.colors = [NSArray arrayWithObjects:(__bridge id)color1, (__bridge id)color2, nil];
+ _creditsTopFadeLayer.frame = CGRectMake(0.0, 0.0, self.creditsView.bounds.size.width, height);
+ return _creditsTopFadeLayer;
+}
+
+- (CAGradientLayer *)creditsBottomFadeLayer {
+ if (_creditsBottomFadeLayer) return _creditsBottomFadeLayer;
+ CGColorRef color1 = kAboutWindowCreditsFadeColor1;
+ CGColorRef color2 = kAboutWindowCreditsFadeColor2;
+ CGFloat height = kAboutWindowCreditsFadeHeight;
+ _creditsBottomFadeLayer = [CAGradientLayer layer];
+ _creditsBottomFadeLayer.colors = [NSArray arrayWithObjects:(__bridge id)color2, (__bridge id)color1, nil];
+ _creditsBottomFadeLayer.frame = CGRectMake(0.0, self.creditsView.bounds.size.height - height, self.creditsView.bounds.size.width, height);
+ return _creditsBottomFadeLayer;
+}
+
+- (CALayer *)creditsRootLayer {
+ if (_creditsRootLayer) return _creditsRootLayer;
+ _creditsRootLayer = [CALayer layer];
+ [_creditsRootLayer addSublayer:self.creditsTextLayer];
+ [_creditsRootLayer addSublayer:self.creditsTopFadeLayer];
+ [_creditsRootLayer addSublayer:self.creditsBottomFadeLayer];
+ return _creditsRootLayer;
+}
+
+- (CGSize)sizeForAttributedString:(NSAttributedString *)string inWidth:(CGFloat)width {
+ CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((__bridge CFAttributedStringRef)string);
+ CFIndex offset = 0, length;
+ CGFloat height = 0;
+ do {
+ length = CTTypesetterSuggestLineBreak(typesetter, offset, width);
+ CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length));
+ CGFloat ascent, descent, leading;
+ CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
+ CFRelease(line);
+ offset += length;
+ height += ascent + descent + leading;
+ } while (offset < [string length]);
+ CFRelease(typesetter);
+ return CGSizeMake(width, ceil(height));
+}
+
+@end
+
+#pragma mark -
+
+@implementation BackgroundColorView
+
+@synthesize gb_backgroundColor = _gb_backgroundColor;
+
+- (void)drawRect:(NSRect)dirtyRect {
+ [self.gb_backgroundColor set];
+ NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
+}
+
+- (NSColor *)gb_backgroundColor {
+ if (_gb_backgroundColor) return _gb_backgroundColor;
+ _gb_backgroundColor = [NSColor whiteColor];
+ return _gb_backgroundColor;
+}
+
+@end

0 comments on commit 5ce9847

Please sign in to comment.