Skip to content
Snippets Groups Projects
OnboardingContentViewController.m 19.9 KiB
Newer Older
jakobsbode's avatar
jakobsbode committed
//
//  OnboardingContentViewController.m
//  Onboard
//
//  Created by Mike on 8/17/14.
//  Copyright (c) 2014 Mike Amaral. All rights reserved.
//

#import "OnboardingContentViewController.h"
#import "OnboardingViewController.h"
@import AVFoundation;

static NSString * const kDefaultOnboardingFont = @"Helvetica-Light";

#define DEFAULT_TEXT_COLOR [UIColor whiteColor];

static CGFloat const kContentWidthMultiplier = 0.9;
CGFloat kDefaultImageViewSize = 100;
jakobsbode's avatar
jakobsbode committed
static CGFloat const kDefaultTopPadding = 60;
static CGFloat const kDefaultUnderIconPadding = 30;
static CGFloat const kDefaultUnderTitlePadding = 30;
static CGFloat const kDefaultBottomPadding = 0;
static CGFloat const kDefaultUnderPageControlPadding = 0;
static CGFloat const kDefaultTitleFontSize = 38;
static CGFloat const kDefaultBodyFontSize = 28;
static CGFloat const kDefaultButtonFontSize = 24;

static CGFloat const kActionButtonHeight = 50;
static CGFloat const kMainPageControlHeight = 35;

NSString * const kOnboardMainTextAccessibilityIdentifier = @"OnboardMainTextAccessibilityIdentifier";
NSString * const kOnboardSubTextAccessibilityIdentifier = @"OnboardSubTextAccessibilityIdentifier";
NSString * const kOnboardActionButtonAccessibilityIdentifier = @"OnboardActionButtonAccessibilityIdentifier";
NSString * const kOnboardInputViewAccessibilityIdentifier = @"OnboardInputViewAccessibilityIdentifier";

@interface OnboardingContentViewController ()

@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) NSURL *videoURL;

@property (nonatomic) BOOL wasPreviouslyVisible;

jakobsbode's avatar
jakobsbode committed
@property (nonatomic, nullable) BOOL* withPadding;

jakobsbode's avatar
jakobsbode committed
@end

@implementation OnboardingContentViewController

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}


#pragma mark - Initializers

+ (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText action:(dispatch_block_t)action {
    return [[self alloc] initWithTitle:title body:body image:image buttonText:buttonText action:action];
}

- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText action:(dispatch_block_t)action {
    return [self initWithTitle:title body:body image:image buttonText:buttonText actionBlock:^(OnboardingViewController *onboardController) {
        if (action) {
            action();
        }
    }];
}

+ (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock {
    return [[self alloc] initWithTitle:title body:body image:image buttonText:buttonText actionBlock:actionBlock];
}

+ (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText action:(dispatch_block_t)action {
    return [[self alloc] initWithTitle:title body:body videoURL:videoURL buttonText:buttonText action:action];
}

jakobsbode's avatar
jakobsbode committed
+ (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL inputView:(UIView *)inputView buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock {
    return [[self alloc] initWithTitle:title body:body image:nil videoURL:videoURL inputView:inputView buttonText:buttonText actionBlock:actionBlock];
jakobsbode's avatar
jakobsbode committed
}
Joscha's avatar
Joscha committed

jakobsbode's avatar
jakobsbode committed
+ (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL inputView:(UIView *)inputView buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock withPadding:(BOOL*)withPadding {
    return [[self alloc] initWithTitle:title body:body image:nil videoURL:videoURL inputView:inputView buttonText:buttonText actionBlock:actionBlock withPadding:withPadding];
jakobsbode's avatar
jakobsbode committed
}

- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL  buttonText:(NSString *)buttonText action:(dispatch_block_t)action {
    return [self initWithTitle:title body:body image:nil videoURL:videoURL buttonText:buttonText actionBlock:^(OnboardingViewController *onboardController) {
        if (action) {
            action();
        }
    }];
}

+ (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL  buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock {
    return [[self alloc] initWithTitle:title body:body image:nil videoURL:videoURL buttonText:buttonText actionBlock:actionBlock];
}

- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock {
    return [self initWithTitle:title body:body image:image videoURL:nil buttonText:buttonText actionBlock:actionBlock];
}

- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock {
    self = [super init];

    if (self == nil) {
        return nil;
    }
jakobsbode's avatar
jakobsbode committed
    
    self.onlyInputView = false;
jakobsbode's avatar
jakobsbode committed

    // Icon image view
    self.iconImageView = [[UIImageView alloc] initWithImage:image];
    self.iconWidth = image ? image.size.width : kDefaultImageViewSize;
    self.iconHeight = image ? image.size.height : kDefaultImageViewSize;

    // Title label
    self.titleLabel = [UILabel new];
    self.titleLabel.accessibilityIdentifier = kOnboardMainTextAccessibilityIdentifier;
    self.titleLabel.text = title;
    self.titleLabel.textColor = DEFAULT_TEXT_COLOR;
    self.titleLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultTitleFontSize];
    self.titleLabel.numberOfLines = 0;
    self.titleLabel.textAlignment = NSTextAlignmentCenter;

    // Body label
    self.bodyLabel = [UILabel new];
    self.bodyLabel.accessibilityIdentifier = kOnboardSubTextAccessibilityIdentifier;
    self.bodyLabel.text = body;
    self.bodyLabel.textColor = DEFAULT_TEXT_COLOR;
    self.bodyLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultBodyFontSize];
    self.bodyLabel.numberOfLines = 0;
    self.bodyLabel.textAlignment = NSTextAlignmentCenter;

    // Action button
    self.actionButton = [UIButton new];
    self.actionButton.accessibilityIdentifier = kOnboardActionButtonAccessibilityIdentifier;
    self.actionButton.titleLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultButtonFontSize];
    [self.actionButton setTitle:buttonText forState:UIControlStateNormal];
    [self.actionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [self.actionButton addTarget:self action:@selector(handleButtonPressed) forControlEvents:UIControlEventTouchUpInside];

    self.buttonActionHandler = actionBlock ?: ^(OnboardingViewController *controller){};

    // Movie player
    self.videoURL = videoURL;

    // Auto-navigation
    self.movesToNextViewController = NO;
    
    // Default padding values
    self.topPadding = kDefaultTopPadding;
    self.underIconPadding = kDefaultUnderIconPadding;
    self.underTitlePadding = kDefaultUnderTitlePadding;
    self.bottomPadding = kDefaultBottomPadding;
    self.underPageControlPadding = kDefaultUnderPageControlPadding;
    
    // Default blocks
    self.viewWillAppearBlock = ^{};
    self.viewDidAppearBlock = ^{};
    self.viewWillDisappearBlock = ^{};
    self.viewDidDisappearBlock = ^{};
    
    return self;
}

jakobsbode's avatar
jakobsbode committed
- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image videoURL:(NSURL *)videoURL inputView:(UIView *)inputView buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock withPadding:(BOOL*)withPadding{
    self = [super init];
    
    self.withPadding = withPadding;
    
    if (self == nil) {
        return nil;
    }
    
jakobsbode's avatar
jakobsbode committed
    self.onlyInputView = false;
    
jakobsbode's avatar
jakobsbode committed
    // Icon image view
    self.iconImageView = [[UIImageView alloc] initWithImage:image];
    self.iconWidth = image ? image.size.width : kDefaultImageViewSize;
    self.iconHeight = image ? image.size.height : kDefaultImageViewSize;
    
    // Title label
    self.titleLabel = [UILabel new];
    self.titleLabel.accessibilityIdentifier = kOnboardMainTextAccessibilityIdentifier;
    self.titleLabel.text = title;
    self.titleLabel.textColor = DEFAULT_TEXT_COLOR;
    self.titleLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultTitleFontSize];
    self.titleLabel.numberOfLines = 0;
    self.titleLabel.textAlignment = NSTextAlignmentCenter;
    
    // Body label
    self.bodyLabel = [UILabel new];
    self.bodyLabel.accessibilityIdentifier = kOnboardSubTextAccessibilityIdentifier;
    self.bodyLabel.text = body;
    self.bodyLabel.textColor = DEFAULT_TEXT_COLOR;
    self.bodyLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultBodyFontSize];
    self.bodyLabel.numberOfLines = 0;
    self.bodyLabel.textAlignment = NSTextAlignmentCenter;
    
    // Input view
    self.inputView = inputView;
    if (self.inputView) {
        self.inputView.accessibilityIdentifier = kOnboardInputViewAccessibilityIdentifier;
    }
    
    // Action button
    self.actionButton = [UIButton new];
    self.actionButton.accessibilityIdentifier = kOnboardActionButtonAccessibilityIdentifier;
    self.actionButton.titleLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultButtonFontSize];
    [self.actionButton setTitle:buttonText forState:UIControlStateNormal];
    [self.actionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [self.actionButton addTarget:self action:@selector(handleButtonPressed) forControlEvents:UIControlEventTouchUpInside];
    
    self.buttonActionHandler = actionBlock ?: ^(OnboardingViewController *controller){};
    
    // Movie player
    self.videoURL = videoURL;
    
    // Auto-navigation
    self.movesToNextViewController = NO;
    
    // Default padding values
    self.topPadding = kDefaultTopPadding;
    self.underIconPadding = kDefaultUnderIconPadding;
    self.underTitlePadding = kDefaultUnderTitlePadding;
    self.bottomPadding = kDefaultBottomPadding;
    self.underPageControlPadding = kDefaultUnderPageControlPadding;
    
    // Default blocks
    self.viewWillAppearBlock = ^{};
    self.viewDidAppearBlock = ^{};
    self.viewWillDisappearBlock = ^{};
    self.viewDidDisappearBlock = ^{};
    
    return self;
}
Oliver Wiese's avatar
Oliver Wiese committed

jakobsbode's avatar
jakobsbode committed
- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image videoURL:(NSURL *)videoURL inputView:(UIView *)inputView buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock {
    self = [super init];
    
    if (self == nil) {
        return nil;
    }
    
jakobsbode's avatar
jakobsbode committed
    self.onlyInputView = false;
    
jakobsbode's avatar
jakobsbode committed
    // Icon image view
    self.iconImageView = [[UIImageView alloc] initWithImage:image];
    self.iconWidth = image ? image.size.width : kDefaultImageViewSize;
    self.iconHeight = image ? image.size.height : kDefaultImageViewSize;
    
    // Title label
    self.titleLabel = [UILabel new];
    self.titleLabel.accessibilityIdentifier = kOnboardMainTextAccessibilityIdentifier;
    self.titleLabel.text = title;
    self.titleLabel.textColor = DEFAULT_TEXT_COLOR;
    self.titleLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultTitleFontSize];
    self.titleLabel.numberOfLines = 0;
    self.titleLabel.textAlignment = NSTextAlignmentCenter;
    
    // Body label
    self.bodyLabel = [UILabel new];
    self.bodyLabel.accessibilityIdentifier = kOnboardSubTextAccessibilityIdentifier;
    self.bodyLabel.text = body;
    self.bodyLabel.textColor = DEFAULT_TEXT_COLOR;
    self.bodyLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultBodyFontSize];
    self.bodyLabel.numberOfLines = 0;
    self.bodyLabel.textAlignment = NSTextAlignmentCenter;
    
    // Input view
    self.inputView = inputView;
    if (self.inputView) {
        self.inputView.accessibilityIdentifier = kOnboardInputViewAccessibilityIdentifier;
    }
    
    // Action button
    self.actionButton = [UIButton new];
    self.actionButton.accessibilityIdentifier = kOnboardActionButtonAccessibilityIdentifier;
    self.actionButton.titleLabel.font = [UIFont fontWithName:kDefaultOnboardingFont size:kDefaultButtonFontSize];
    [self.actionButton setTitle:buttonText forState:UIControlStateNormal];
    [self.actionButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    [self.actionButton addTarget:self action:@selector(handleButtonPressed) forControlEvents:UIControlEventTouchUpInside];
    
    self.buttonActionHandler = actionBlock ?: ^(OnboardingViewController *controller){};
    
    // Movie player
    self.videoURL = videoURL;
    
    // Auto-navigation
    self.movesToNextViewController = NO;
    
    // Default padding values
    self.topPadding = kDefaultTopPadding;
    self.underIconPadding = kDefaultUnderIconPadding;
    self.underTitlePadding = kDefaultUnderTitlePadding;
    self.bottomPadding = kDefaultBottomPadding;
    self.underPageControlPadding = kDefaultUnderPageControlPadding;
    
    // Default blocks
    self.viewWillAppearBlock = ^{};
    self.viewDidAppearBlock = ^{};
    self.viewWillDisappearBlock = ^{};
    self.viewDidDisappearBlock = ^{};
    
    return self;
}


#pragma mark - View life cycle

- (void)viewDidLoad {
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppEnteredForeground) name:UIApplicationDidBecomeActiveNotification object:nil];

    self.view.backgroundColor = [UIColor clearColor];

    // Add all our subviews
    if (self.videoURL) {
        self.player = [[AVPlayer alloc] initWithURL:self.videoURL];

        self.moviePlayerController = [AVPlayerViewController new];
        self.moviePlayerController.player = self.player;
        self.moviePlayerController.showsPlaybackControls = NO;
jakobsbode's avatar
jakobsbode committed
        
        self.moviePlayerController.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
        
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(playerItemDidReachEnd:)
                                                     name:AVPlayerItemDidPlayToEndTimeNotification
                                                   object:[self.moviePlayerController.player currentItem]];
jakobsbode's avatar
jakobsbode committed

        [self.view addSubview:self.moviePlayerController.view];
    }

    [self.view addSubview:self.iconImageView];
    [self.view addSubview:self.titleLabel];
    [self.view addSubview:self.bodyLabel];
    if (self.inputView) {
        [self.view addSubview:self.inputView];
    }
    [self.view addSubview:self.actionButton];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    // If we have a delegate set, mark ourselves as the next page now that we're
    // about to appear
    if (self.delegate) {
        [self.delegate setNextPage:self];
    }

    // If we have a video URL, start playing
    if (self.videoURL) {
        [self.player play];
    }
    
    // Call our view will appear block
    if (self.viewWillAppearBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.viewWillAppearBlock();
        });
    }

    self.wasPreviouslyVisible = YES;
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    // If we have a delegate set, mark ourselves as the current page now that
    // we've appeared
    if (self.delegate) {
        [self.delegate setCurrentPage:self];
    }
    
    // Call our view did appear block
    if (self.viewDidAppearBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.viewDidAppearBlock();
        });
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    // Call our view will disappear block
    if (self.viewWillDisappearBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.viewWillDisappearBlock();
        });
    }

    self.wasPreviouslyVisible = NO;
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    // Call our view did disappear block
    if (self.viewDidDisappearBlock) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.viewDidDisappearBlock();
        });
    }

    // Pause our video if we have one.
    if ((self.player.rate != 0.0) && !self.player.error) {
        [self.player pause];
    }
}


#pragma mark - App life cycle

- (void)handleAppEnteredForeground {
    // If we have a video URL and this view controller was previously on screen
    // restart it as it will be paused when the app enters the foreground.
    if (self.videoURL && self.wasPreviouslyVisible) {
        [self.player play];
    }
}


#pragma mark - Layout

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
jakobsbode's avatar
jakobsbode committed
    
    if (!self.onlyInputView) {
        if (self.videoURL) {
jakobsbode's avatar
jakobsbode committed
            self.moviePlayerController.view.frame = self.inputView.frame;//self.view.frame;
jakobsbode's avatar
jakobsbode committed
        }
jakobsbode's avatar
jakobsbode committed
        CGFloat viewWidth = CGRectGetWidth(self.view.frame);
        CGFloat contentWidth = viewWidth * kContentWidthMultiplier;
        CGFloat xPadding = (viewWidth - contentWidth) / 2.0;
jakobsbode's avatar
jakobsbode committed
        [self.iconImageView setFrame:CGRectMake((viewWidth / 2.0) - (self.iconWidth / 2.0), self.topPadding, self.iconWidth, self.iconHeight)];
jakobsbode's avatar
jakobsbode committed
        CGFloat titleYOrigin = CGRectGetMaxY(self.iconImageView.frame) + self.underIconPadding;
jakobsbode's avatar
jakobsbode committed
        self.titleLabel.frame = CGRectMake(xPadding, titleYOrigin, contentWidth, 0);
        [self.titleLabel sizeToFit];
        self.titleLabel.frame = CGRectMake(xPadding, titleYOrigin, contentWidth, CGRectGetHeight(self.titleLabel.frame));
jakobsbode's avatar
jakobsbode committed
        CGFloat bodyYOrigin = CGRectGetMaxY(self.titleLabel.frame) + self.underTitlePadding;
jakobsbode's avatar
jakobsbode committed
        self.bodyLabel.frame = CGRectMake(xPadding, bodyYOrigin, contentWidth, 0);
        [self.bodyLabel sizeToFit];
        self.bodyLabel.frame = CGRectMake(xPadding, bodyYOrigin, contentWidth, CGRectGetHeight(self.bodyLabel.frame));
Oliver Wiese's avatar
Oliver Wiese committed

jakobsbode's avatar
jakobsbode committed
        self.actionButton.frame = CGRectMake((CGRectGetMaxX(self.view.frame) / 2) - (contentWidth / 2), CGRectGetMaxY(self.view.frame) - self.underPageControlPadding - kMainPageControlHeight - kActionButtonHeight - self.bottomPadding, contentWidth, kActionButtonHeight);
        CGFloat inputYOrigin = CGRectGetMaxY(self.bodyLabel.frame) + self.underTitlePadding;
        if (self.withPadding != NULL) {
            if (*self.withPadding == NO) {
                inputYOrigin = CGRectGetMaxY(self.bodyLabel.frame);
            }
        }
        if (self.inputView) {
            self.inputView.frame = CGRectMake(xPadding, inputYOrigin, contentWidth, CGRectGetHeight(self.inputView.frame));
            for (UIView *view in self.inputView.subviews){
                view.frame = CGRectMake(CGRectGetMinX(view.frame), CGRectGetMinY(view.frame), contentWidth, CGRectGetHeight(view.frame));
            }
jakobsbode's avatar
jakobsbode committed
    else {
        CGFloat viewWidth = CGRectGetWidth(self.view.frame);
        CGFloat contentWidth = viewWidth * kContentWidthMultiplier;
        CGFloat xPadding = (viewWidth - contentWidth) / 2.0;
        
        if (self.inputView) {
            self.inputView.frame = CGRectMake(xPadding, self.topPadding, contentWidth, CGRectGetHeight(self.inputView.frame));
            for (UIView *view in self.inputView.subviews){
                view.frame = CGRectMake(CGRectGetMinX(view.frame), CGRectGetMinY(view.frame), contentWidth, CGRectGetHeight(view.frame));
            }
jakobsbode's avatar
jakobsbode committed
    }
}


#pragma mark - Transition alpha

- (void)updateAlphas:(CGFloat)newAlpha {
    self.iconImageView.alpha = newAlpha;
    self.titleLabel.alpha = newAlpha;
    self.bodyLabel.alpha = newAlpha;
    self.actionButton.alpha = newAlpha;
}


#pragma mark - Action button stuff

- (void)handleButtonPressed {
    // if we want to navigate to the next view controller, tell our delegate
    // to handle it
    if (self.movesToNextViewController) {
        [self.delegate moveNextPage];
    }
    
    // call the provided action handler
    if (self.buttonActionHandler) {
        self.buttonActionHandler(self.delegate);
    }
}

jakobsbode's avatar
jakobsbode committed
- (void)playerItemDidReachEnd:(NSNotification *)notification {
    AVPlayerItem *p = [notification object];
    [p seekToTime:kCMTimeZero];
}

jakobsbode's avatar
jakobsbode committed
@end