diff --git a/Demo/Demo Files/AppDelegate.m b/Demo/Demo Files/AppDelegate.m index 2389387..f5b76f7 100644 --- a/Demo/Demo Files/AppDelegate.m +++ b/Demo/Demo Files/AppDelegate.m @@ -29,12 +29,9 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // otherwise set the root view controller to the onboarding view controller else { - self.window.rootViewController = [self generateFirstDemoVC]; -// self.window.rootViewController = [self generateSecondDemoVC]; -// self.window.rootViewController = [self generateThirdDemoVC]; -// self.window.rootViewController = [self generateFourthDemoVC]; -// self.window.rootViewController = [self generateFifthDemoVC]; - + self.window.rootViewController = [self generateStandardOnboardingVC]; +// self.window.rootViewController = [self generateMovieOnboardingVC]; + // __weak typeof(self) weakSelf = self; // // self.window.rootViewController = [[MyOnboardingViewController alloc] initWithCompletionHandler:^{ @@ -68,7 +65,7 @@ - (void)handleOnboardingCompletion { [self setupNormalRootViewController]; } -- (OnboardingViewController *)generateFirstDemoVC { +- (OnboardingViewController *)generateStandardOnboardingVC { OnboardingContentViewController *firstPage = [OnboardingContentViewController contentWithTitle:@"What A Beautiful Photo" body:@"This city background image is so beautiful." image:[UIImage imageNamed:@"blue"] buttonText:@"Enable Location Services" action:^{ [[[UIAlertView alloc] initWithTitle:nil message:@"Here you can prompt users for various application permissions, providing them useful information about why you'd like those permissions to enhance their experience, increasing your chances they will grant those permissions." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; }]; @@ -100,24 +97,22 @@ - (OnboardingViewController *)generateFirstDemoVC { return onboardingVC; } -- (OnboardingViewController *)generateSecondDemoVC { +- (OnboardingViewController *)generateMovieOnboardingVC { OnboardingContentViewController *firstPage = [[OnboardingContentViewController alloc] initWithTitle:@"Everything Under The Sun" body:@"The temperature of the photosphere is over 10,000°F." image:nil buttonText:nil action:nil]; firstPage.topPadding = -15; firstPage.underTitlePadding = 160; - firstPage.titleTextColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; - firstPage.titleFontName = @"SFOuterLimitsUpright"; - firstPage.bodyTextColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; - firstPage.bodyFontName = @"NasalizationRg-Regular"; - firstPage.bodyFontSize = 18; + firstPage.titleLabel.textColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; + firstPage.titleLabel.font = [UIFont fontWithName:@"SFOuterLimitsUpright" size:38.0]; + firstPage.bodyLabel.textColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; + firstPage.bodyLabel.font = [UIFont fontWithName:@"NasalizationRg-Regular" size:18.0]; OnboardingContentViewController *secondPage = [[OnboardingContentViewController alloc] initWithTitle:@"Every Second" body:@"600 million tons of protons are converted into helium atoms." image:nil buttonText:nil action:nil]; - secondPage.titleFontName = @"SFOuterLimitsUpright"; + secondPage.titleLabel.font = [UIFont fontWithName:@"SFOuterLimitsUpright" size:38.0]; secondPage.underTitlePadding = 170; secondPage.topPadding = 0; - secondPage.titleTextColor = [UIColor colorWithRed:251/255.0 green:176/255.0 blue:59/255.0 alpha:1.0]; - secondPage.bodyTextColor = [UIColor colorWithRed:251/255.0 green:176/255.0 blue:59/255.0 alpha:1.0]; - secondPage.bodyFontName = @"NasalizationRg-Regular"; - secondPage.bodyFontSize = 18; + secondPage.titleLabel.textColor = [UIColor colorWithRed:251/255.0 green:176/255.0 blue:59/255.0 alpha:1.0]; + secondPage.bodyLabel.textColor = [UIColor colorWithRed:251/255.0 green:176/255.0 blue:59/255.0 alpha:1.0]; + secondPage.bodyLabel.font = [UIFont fontWithName:@"NasalizationRg-Regular" size:18.0]; OnboardingContentViewController *thirdPage = [[OnboardingContentViewController alloc] initWithTitle:@"We're All Star Stuff" body:@"Our very bodies consist of the same chemical elements found in the most distant nebulae, and our activities are guided by the same universal rules." image:nil buttonText:@"Explore the universe" action:^{ [self handleOnboardingCompletion]; @@ -125,14 +120,12 @@ - (OnboardingViewController *)generateSecondDemoVC { thirdPage.topPadding = 10; thirdPage.underTitlePadding = 160; thirdPage.bottomPadding = -10; - thirdPage.titleFontName = @"SFOuterLimitsUpright"; - thirdPage.titleTextColor = [UIColor colorWithRed:58/255.0 green:105/255.0 blue:136/255.0 alpha:1.0]; - thirdPage.bodyTextColor = [UIColor colorWithRed:58/255.0 green:105/255.0 blue:136/255.0 alpha:1.0]; - thirdPage.buttonTextColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; - thirdPage.bodyFontName = @"NasalizationRg-Regular"; - thirdPage.bodyFontSize = 15; - thirdPage.buttonFontName = @"SpaceAge"; - thirdPage.buttonFontSize = 17; + thirdPage.titleLabel.font = [UIFont fontWithName:@"SFOuterLimitsUpright" size:38.0]; + thirdPage.titleLabel.textColor = [UIColor colorWithRed:58/255.0 green:105/255.0 blue:136/255.0 alpha:1.0]; + thirdPage.bodyLabel.textColor = [UIColor colorWithRed:58/255.0 green:105/255.0 blue:136/255.0 alpha:1.0]; + thirdPage.bodyLabel.font = [UIFont fontWithName:@"NasalizationRg-Regular" size:15.0]; + [thirdPage.actionButton setTitleColor:[UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0] forState:UIControlStateNormal]; + thirdPage.actionButton.titleLabel.font = [UIFont fontWithName:@"SpaceAge" size:17.0]; NSBundle *bundle = [NSBundle mainBundle]; NSString *moviePath = [bundle pathForResource:@"sun" ofType:@"mp4"]; @@ -146,85 +139,4 @@ - (OnboardingViewController *)generateSecondDemoVC { return onboardingVC; } -- (OnboardingViewController *)generateThirdDemoVC { - OnboardingContentViewController *firstPage = [[OnboardingContentViewController alloc] initWithTitle:@"It's one small step for a man..." body:@"The first man on the moon, Buzz Aldrin, only had one photo taken of him while on the lunar surface due to an unexpected call from Dick Nixon." image:[UIImage imageNamed:@"space1"] buttonText:nil action:nil]; - firstPage.bodyFontSize = 25; - - OnboardingContentViewController *secondPage = [[OnboardingContentViewController alloc] initWithTitle:@"The Drake Equation" body:@"In 1961, Frank Drake proposed a probabilistic formula to help estimate the number of potential active and radio-capable extraterrestrial civilizations in the Milky Way Galaxy." image:[UIImage imageNamed:@"space2"] buttonText:nil action:nil]; - secondPage.bodyFontSize = 24; - - OnboardingContentViewController *thirdPage = [[OnboardingContentViewController alloc] initWithTitle:@"Cold Welding" body:@"Two pieces of metal without any coating on them will form into one piece in the vacuum of space." image:[UIImage imageNamed:@"space3"] buttonText:nil action:nil]; - - OnboardingContentViewController *fourthPage = [[OnboardingContentViewController alloc] initWithTitle:@"Goodnight Moon" body:@"Every year the moon moves about 3.8cm further away from the Earth." image:[UIImage imageNamed:@"space4"] buttonText:@"See Ya Later!" action:nil]; - - OnboardingViewController *onboardingVC = [[OnboardingViewController alloc] initWithBackgroundImage:[UIImage imageNamed:@"milky_way.jpg"] contents:@[firstPage, secondPage, thirdPage, fourthPage]]; - onboardingVC.shouldMaskBackground = NO; - onboardingVC.shouldBlurBackground = YES; - return onboardingVC; -} - -- (OnboardingViewController *)generateFourthDemoVC { - OnboardingContentViewController *firstPage = [[OnboardingContentViewController alloc] initWithTitle:@"\"If you can't explain it simply, you don't know it well enough.\"" body:@" - Einsten" image:[UIImage imageNamed:@""] buttonText:nil action:nil]; - - OnboardingContentViewController *secondPage = [[OnboardingContentViewController alloc] initWithTitle:@"\"If you wish to make an apple pie from scratch, you must first invent the universe.\"" body:@" - Sagan" image:nil buttonText:nil action:nil]; - secondPage.topPadding = 0; - - OnboardingContentViewController *thirdPage = [[OnboardingContentViewController alloc] initWithTitle:@"\"That which can be asserted without evidence, can be dismissed without evidence.\"" body:@" - Hitchens" image:nil buttonText:nil action:nil]; - thirdPage.titleFontSize = 33; - thirdPage.bodyFontSize = 25; - - OnboardingContentViewController *fourthPage = [[OnboardingContentViewController alloc] initWithTitle:@"\"Scientists have become the bearers of the torch of discovery in our quest for knowledge.\"" body:@" - Hawking" image:nil buttonText:nil action:nil]; - fourthPage.titleFontSize = 28; - fourthPage.bodyFontSize = 24; - - OnboardingViewController *onboardingVC = [[OnboardingViewController alloc] initWithBackgroundImage:[UIImage imageNamed:@"yellowbg"] contents:@[firstPage, secondPage, thirdPage, fourthPage]]; - onboardingVC.shouldMaskBackground = NO; - onboardingVC.titleTextColor = [UIColor colorWithRed:57/255.0 green:57/255.0 blue:57/255.0 alpha:1.0];; - onboardingVC.bodyTextColor = [UIColor colorWithRed:244/255.0 green:64/255.0 blue:40/255.0 alpha:1.0]; - onboardingVC.fontName = @"HelveticaNeue-Italic"; - return onboardingVC; -} - -- (OnboardingViewController *)generateFifthDemoVC { - OnboardingContentViewController *firstPage = [[OnboardingContentViewController alloc] initWithTitle:@"Tri-tip bacon shankle" body:@"Bacon ipsum dolor amet cow filet mignon porchetta ham hamburger pork chop venison landjaeger ribeye drumstick beef ribs tongue." videoURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"video1" ofType:@"mp4"]] buttonText:nil action:nil]; - firstPage.topPadding = -15; - firstPage.underTitlePadding = 160; - firstPage.titleTextColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; - firstPage.titleFontName = @"SFOuterLimitsUpright"; - firstPage.bodyTextColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; - firstPage.bodyFontName = @"NasalizationRg-Regular"; - firstPage.bodyFontSize = 18; - - OnboardingContentViewController *secondPage = [[OnboardingContentViewController alloc] initWithTitle:@"Ball tip hamburger" body:@"Bacon ipsum dolor amet kielbasa landjaeger ham fatback frankfurter pork beef pig strip steak pancetta tenderloin pork chop." videoURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"video2" ofType:@"mp4"]] buttonText:nil action:nil]; - secondPage.titleFontName = @"SFOuterLimitsUpright"; - secondPage.underTitlePadding = 170; - secondPage.topPadding = 0; - secondPage.titleTextColor = [UIColor colorWithRed:251/255.0 green:176/255.0 blue:59/255.0 alpha:1.0]; - secondPage.bodyTextColor = [UIColor colorWithRed:251/255.0 green:176/255.0 blue:59/255.0 alpha:1.0]; - secondPage.bodyFontName = @"NasalizationRg-Regular"; - secondPage.bodyFontSize = 18; - - OnboardingContentViewController *thirdPage = [[OnboardingContentViewController alloc] initWithTitle:@"Sausage prosciutto flank capicola" body:@"Bacon ipsum dolor amet tail sausage salami filet mignon spare ribs hamburger." videoURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"video3" ofType:@"mp4"]] buttonText:@"Tap me" action:^{ - [self handleOnboardingCompletion]; - }]; - thirdPage.topPadding = 10; - thirdPage.underTitlePadding = 160; - thirdPage.bottomPadding = -10; - thirdPage.titleFontName = @"SFOuterLimitsUpright"; - thirdPage.titleTextColor = [UIColor colorWithRed:58/255.0 green:105/255.0 blue:136/255.0 alpha:1.0]; - thirdPage.bodyTextColor = [UIColor colorWithRed:58/255.0 green:105/255.0 blue:136/255.0 alpha:1.0]; - thirdPage.buttonTextColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; - thirdPage.bodyFontName = @"NasalizationRg-Regular"; - thirdPage.bodyFontSize = 15; - thirdPage.buttonFontName = @"SpaceAge"; - thirdPage.buttonFontSize = 17; - - OnboardingViewController *onboardingVC = [[OnboardingViewController alloc] initWithBackgroundImage:[UIImage imageNamed:@"milky_way.jpg"] contents:@[firstPage, secondPage, thirdPage]]; - onboardingVC.shouldFadeTransitions = YES; - onboardingVC.shouldMaskBackground = NO; - onboardingVC.pageControl.currentPageIndicatorTintColor = [UIColor colorWithRed:239/255.0 green:88/255.0 blue:35/255.0 alpha:1.0]; - onboardingVC.pageControl.pageIndicatorTintColor = [UIColor whiteColor]; - return onboardingVC; -} - -@end \ No newline at end of file +@end diff --git a/Demo/Demo Files/OnboardTests/OnboardTests.m b/Demo/Demo Files/OnboardTests/OnboardTests.m index 70a8f41..78ef634 100644 --- a/Demo/Demo Files/OnboardTests/OnboardTests.m +++ b/Demo/Demo Files/OnboardTests/OnboardTests.m @@ -76,47 +76,6 @@ - (void)testDefaultValues { XCTAssertFalse(onboardingVC.shouldBlurBackground, @"The background should not be blurred by default."); } -- (void)testConvenienceSetters { - // This tests that when we use the convenience setter methods on the master onboaring view controller, - // the properties correctly trickle down to each of the child content view controllers. - OnboardingViewController *onboardingVC = [[OnboardingViewController alloc] initWithBackgroundImage:nil contents:[self generateStockContentVCS]]; - - CGFloat testIconSize = 80; - onboardingVC.iconSize = testIconSize; - - UIColor *testColor = [UIColor purpleColor]; - onboardingVC.titleTextColor = testColor; - onboardingVC.bodyTextColor = testColor; - onboardingVC.buttonTextColor = testColor; - - NSString *testFontName = @"Helvetica-LightOblique"; - onboardingVC.fontName = testFontName; - - CGFloat testFontSize = 12; - onboardingVC.titleFontSize = testFontSize; - onboardingVC.bodyFontSize = testFontSize; - - CGFloat testPadding = 22; - onboardingVC.topPadding = testPadding; - onboardingVC.underIconPadding = testPadding; - onboardingVC.underTitlePadding = testPadding; - onboardingVC.bottomPadding = testPadding; - - NSArray *contentsFromController = onboardingVC.viewControllers; - - for (OnboardingContentViewController *contentVC in contentsFromController) { - XCTAssert(contentVC.titleTextColor == testColor, @"The content view controller's title text color is invalid."); - XCTAssert(contentVC.bodyTextColor == testColor, @"The content view controller's body text color is invalid."); - XCTAssert(contentVC.buttonTextColor == testColor, @"The content view controller's button test color is invalid."); - XCTAssert(contentVC.titleFontSize == testFontSize, @"The content view controller's title fotn size is invalid."); - XCTAssert(contentVC.bodyFontSize == testFontSize, @"The content view controller's body font size is invalid."); - XCTAssert(contentVC.topPadding == testPadding, @"The content view controller's top padding is invalid."); - XCTAssert(contentVC.underIconPadding == testPadding, @"The content view controller's under icon padding is invalid."); - XCTAssert(contentVC.underTitlePadding == testPadding, @"The content view controller's under title padding is invalid."); - XCTAssert(contentVC.bottomPadding == testPadding, @"The content view controller's bottom padding is invalid."); - } -} - - (void)testActionHandler { XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; OnboardingContentViewController *contentVC = [[OnboardingContentViewController alloc] initWithTitle:@"T1" body:@"B1" image:nil buttonText:nil action:^{ diff --git a/Onboard.podspec b/Onboard.podspec index 9c03c16..6418e8b 100644 --- a/Onboard.podspec +++ b/Onboard.podspec @@ -1,14 +1,14 @@ Pod::Spec.new do |s| s.name = "Onboard" - s.version = "2.1.9" + s.version = "2.2.0" s.summary = "Create a beautiful and engaging onboarding experience with only a few lines of code." s.homepage = "https://github.com/mamaral/Onboard" s.license = "MIT" s.author = { "Mike Amaral" => "mike.amaral36@gmail.com" } s.social_media_url = "http://twitter.com/MikeAmaral" s.platform = :ios - s.source = { :git => "https://github.com/mamaral/Onboard.git", :tag => "v2.1.9" } + s.source = { :git => "https://github.com/mamaral/Onboard.git", :tag => "v2.2.0" } s.source_files = "Source/OnboardingViewController.{h,m}", "Source/OnboardingContentViewController.{h,m}" s.requires_arc = true diff --git a/README.md b/README.md index e1d0822..64617b1 100644 --- a/README.md +++ b/README.md @@ -75,13 +75,9 @@ With only a few lines of code you have a beautiful, end-to-end onboarding proces Customization ============= -The content pages can be customized by setting the provided padding, font, and size properties on either the pages individually (if you want something different on each) or on the OnboardingViewController itself, which will pass those properties to all of the content view controllers. +The `iconImageView`, `titleLabel`, `bodyLabel`, and `actionButton` properties are exposed for customizing fonts, sizing, etc., and the spacing between elements on the content pages can be customized as well: ```objective-c -OnboardingViewController *onboardingVC = [OnboardingViewController onboardWithBackgroundImage contents:yourContentsArray]; -onboardingVC.fontName = @"Helvetica-Light"; -onboardingVC.titleFontSize = 28; -onboardingVC.bodyFontSize = 22; onboardingVC.topPadding = 20; onboardingVC.underIconPadding = 10; onboardingVC.underTitlePadding = 15; diff --git a/Source/OnboardingContentViewController.h b/Source/OnboardingContentViewController.h index 5649958..6f60d84 100644 --- a/Source/OnboardingContentViewController.h +++ b/Source/OnboardingContentViewController.h @@ -10,6 +10,15 @@ @import MediaPlayer; @class OnboardingViewController; +@class OnboardingContentViewController; + +@protocol OnboardingContentViewControllerDelegate + +@required +- (void)setNextPage:(OnboardingContentViewController *)contentVC; +- (void)setCurrentPage:(OnboardingContentViewController *)contentVC; + +@end extern NSString * const kOnboardMainTextAccessibilityIdentifier; extern NSString * const kOnboardSubTextAccessibilityIdentifier; @@ -17,63 +26,188 @@ extern NSString * const kOnboardActionButtonAccessibilityIdentifier; typedef void (^action_callback)(OnboardingViewController *onboardController); -@interface OnboardingContentViewController : UIViewController { - NSString *_titleText; - NSString *_body; - UIImage *_image; - NSString *_buttonText; - - UIImageView *_imageView; - UILabel *_mainTextLabel; - UILabel *_subTextLabel; - UIButton *_actionButton; -} -@property (nonatomic) OnboardingViewController *delegate; +@interface OnboardingContentViewController : UIViewController +@property (nonatomic, weak) OnboardingViewController *delegate; + +/** + * @brief Determines if the next page is automatically shown when the action button is pressed. + */ @property (nonatomic) BOOL movesToNextViewController; -@property (nonatomic) CGFloat iconHeight; -@property (nonatomic) CGFloat iconWidth; -@property (nonatomic, strong) UIColor *titleTextColor; -@property (nonatomic, strong) UIColor *bodyTextColor; -@property (nonatomic, strong) UIColor *buttonTextColor; -@property (nonatomic, strong) UIColor *backgroundColor; +/** + * @brief The image view used to show the top icon. + */ +@property (nonatomic, strong) UIImageView *iconImageView; -@property (nonatomic, strong) NSString *titleFontName; -@property (nonatomic) CGFloat titleFontSize; -@property (nonatomic, strong) NSString *bodyFontName; -@property (nonatomic) CGFloat bodyFontSize; +/** + * @brief The title label. + */ +@property (nonatomic, strong) UILabel *titleLabel; + + +/** + * @brief The body label. + */ +@property (nonatomic, strong) UILabel *bodyLabel; + + +/** + * @brief The button used to call the action handler if one was provided. + */ +@property (nonatomic, strong) UIButton *actionButton; + + +/** + * @brief The width of the icon image view. + */ +@property (nonatomic) CGFloat iconWidth; -@property (nonatomic, strong) NSString *buttonFontName; -@property (nonatomic) CGFloat buttonFontSize; +/** + * @brief The height of the icon image view. + */ +@property (nonatomic) CGFloat iconHeight; + + +/** + * @brief The padding between the top of the screen and the top of the icon image view. + */ @property (nonatomic) CGFloat topPadding; + + +/** + * @brief The padding between the icon image view and the title label. + */ @property (nonatomic) CGFloat underIconPadding; + + +/** + * @brief The padding between the title label and the body label; + */ @property (nonatomic) CGFloat underTitlePadding; + + +/** + * @brief The padding between the bottom of the action button and the page control. + */ @property (nonatomic) CGFloat bottomPadding; + + +/** + * @brief The padding between the bottom of the screen and the page control. + */ @property (nonatomic) CGFloat underPageControlPadding; + +/** + * @brief The block executed when the action button is pressed. + */ @property (nonatomic, copy) action_callback buttonActionHandler; + +/** + * @brief The block executed when the content view controller's viewWillAppear method is called. + */ @property (nonatomic, copy) dispatch_block_t viewWillAppearBlock; + + +/** + * @brief The block executed when the content view controller's viewDidAppear method is called. + */ @property (nonatomic, copy) dispatch_block_t viewDidAppearBlock; + + +/** + * @brief The block executed when the content view controller's viewWillDisappear method is called. + */ @property (nonatomic, copy) dispatch_block_t viewWillDisappearBlock; + + +/** + * @brief The block executed when the content view controller's viewDidDisappear method is called. + */ @property (nonatomic, copy) dispatch_block_t viewDidDisappearBlock; + +/** + * @brief Convenience class initializer for creating an onboarding content view controller. + * @return An instance of OnboardingViewController with the provided information. + */ + (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText action:(dispatch_block_t)action; + + +/** + * @brief Initializer for creating an onboarding content view controller. + * @return An instance of OnboardingViewController with the provided information. + */ - (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText action:(dispatch_block_t)action; + + +/** + * @brief Convenience class initializer for creating an onboarding content view controller with an action_callback block. + * @return An instance of OnboardingViewController with the provided information. + */ + (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock; + +/** + * @brief Convenience class initializer for creating an onboarding content view controller with a video. + * @return An instance of OnboardingViewController with the provided information. + */ + (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText action:(dispatch_block_t)action; -- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText action:(dispatch_block_t)action; -+ (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock; + +/** + * @brief Initializer for creating an onboarding content view controller with a video. + * @return An instance of OnboardingViewController with the provided information. + */ +- (instancetype)initWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText action:(dispatch_block_t)action; + + +/** + * @brief Convenience class initializer for creating an onboarding content view controller with a video and an action_callback block. + * @return An instance of OnboardingViewController with the provided information. + */ ++ (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock; + + +/** + * @brief Initializer for creating an onboarding content view controller with a video and an action_callback block. + * @return An instance of OnboardingViewController with the provided information. + */ - (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock; + + +/** + * @brief Initializer for creating an onboarding content view controller with a video and an action_callback block. + * @return An instance of OnboardingViewController with the provided information. + */ - (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image videoURL:videoURL buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock; + +/** + * @brief Method used to update the alpha value for all floating subviews (image, title, body, etc.) + */ - (void)updateAlphas:(CGFloat)newAlpha; + +// The following properties are all deprecated, and will be removed in the next release of Onboard. +// +// +@property (nonatomic, strong) UIColor *titleTextColor __attribute__((deprecated("Set titleLabel.textColor instead. This property will be removed in the next update."))); +@property (nonatomic, strong) NSString *titleFontName __attribute__((deprecated("Set titleLabel.font instead. This property will be removed in the next update."))); +@property (nonatomic) CGFloat titleFontSize __attribute__((deprecated("Set titleLabel.font instead. This property will be removed in the next update."))); + +@property (nonatomic, strong) UIColor *bodyTextColor __attribute__((deprecated("Set bodyLabel.textColor instead. This property will be removed in the next update."))); +@property (nonatomic, strong) NSString *bodyFontName __attribute__((deprecated("Set bodyLabel.font instead. This property will be removed in the next update."))); +@property (nonatomic) CGFloat bodyFontSize __attribute__((deprecated("Set bodyLabel.font instead. This property will be removed in the next update."))); + +@property (nonatomic, strong) UIColor *buttonTextColor __attribute__((deprecated("Modify the actionButton property directly. This property will be removed in the next update."))); +@property (nonatomic, strong) NSString *buttonFontName __attribute__((deprecated("Modify the actionButton property directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat buttonFontSize __attribute__((deprecated("Modify the actionButton property directly. This property will be removed in the next update."))); + @end diff --git a/Source/OnboardingContentViewController.m b/Source/OnboardingContentViewController.m index 1e122c9..2a47ca1 100644 --- a/Source/OnboardingContentViewController.m +++ b/Source/OnboardingContentViewController.m @@ -34,6 +34,7 @@ @interface OnboardingContentViewController () +@property (nonatomic, strong) UIImageView *thumbnailImageView; @property (nonatomic, strong) MPMoviePlayerController *moviePlayerController; @property (nonatomic, strong) NSURL *videoURL; @@ -42,39 +43,39 @@ @interface OnboardingContentViewController () @implementation OnboardingContentViewController - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:UIApplicationWillEnterForegroundNotification]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } + (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText action:(dispatch_block_t)action { - OnboardingContentViewController *contentVC = [[self alloc] initWithTitle:title body:body image:image buttonText:buttonText action:action]; - return contentVC; + 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(); + if (action) { + action(); + } }]; } + (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock { - OnboardingContentViewController *contentVC = [[self alloc] initWithTitle:title body:body image:image buttonText:buttonText actionBlock:actionBlock]; - return contentVC; + 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 { - OnboardingContentViewController *contentVC = [[self alloc] initWithTitle:title body:body videoURL:videoURL buttonText:buttonText action:action]; - return contentVC; + return [[self alloc] initWithTitle:title body:body videoURL:videoURL buttonText:buttonText action:action]; } - (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(); + if (action) { + action(); + } }]; } + (instancetype)contentWithTitle:(NSString *)title body:(NSString *)body videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock { - OnboardingContentViewController *contentVC = [[self alloc] initWithTitle:title body:body image:nil videoURL:videoURL buttonText:buttonText actionBlock:actionBlock]; - return contentVC; + 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 { @@ -84,84 +85,108 @@ - (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UII - (instancetype)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image videoURL:(NSURL *)videoURL buttonText:(NSString *)buttonText actionBlock:(action_callback)actionBlock { self = [super init]; - // hold onto the passed in parameters, and set the action block to an empty block - // in case we were passed nil, so we don't have to nil-check the block later before - // calling - _titleText = title; - _body = body; - _image = image; - _buttonText = buttonText; - self.videoURL = videoURL; + if (self == nil) { + return nil; + } - self.buttonActionHandler = actionBlock; + // 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 + if (videoURL) { + self.videoURL = videoURL; + + self.moviePlayerController = [MPMoviePlayerController new]; + self.moviePlayerController.contentURL = self.videoURL; + self.moviePlayerController.repeatMode = MPMovieRepeatModeOne; + self.moviePlayerController.controlStyle = MPMovieControlStyleNone; + + self.thumbnailImageView = [[UIImageView alloc] initWithImage:[self thumbnailImageForVideo:self.videoURL]]; + self.thumbnailImageView.contentMode = UIViewContentModeScaleAspectFit; + } - // default auto-navigation + // Auto-navigation self.movesToNextViewController = NO; - // default icon properties - if(_image) { - self.iconHeight = _image.size.height; - self.iconWidth = _image.size.width; - } - - else { - self.iconHeight = kDefaultImageViewSize; - self.iconWidth = kDefaultImageViewSize; - } - - // default title properties - self.titleFontName = kDefaultOnboardingFont; - self.titleFontSize = kDefaultTitleFontSize; - - // default body properties - self.bodyFontName = kDefaultOnboardingFont; - self.bodyFontSize = kDefaultBodyFontSize; - - // default button properties - self.buttonFontName = kDefaultOnboardingFont; - self.buttonFontSize = kDefaultButtonFontSize; - - // default padding values + // Default padding values self.topPadding = kDefaultTopPadding; self.underIconPadding = kDefaultUnderIconPadding; self.underTitlePadding = kDefaultUnderTitlePadding; self.bottomPadding = kDefaultBottomPadding; self.underPageControlPadding = kDefaultUnderPageControlPadding; - // default colors - self.titleTextColor = DEFAULT_TEXT_COLOR; - self.bodyTextColor = DEFAULT_TEXT_COLOR; - self.buttonTextColor = DEFAULT_TEXT_COLOR; - - // default blocks + // Default blocks self.viewWillAppearBlock = ^{}; self.viewDidAppearBlock = ^{}; self.viewWillDisappearBlock = ^{}; self.viewDidDisappearBlock = ^{}; - // Handle when the app enters the foreground. - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppEnteredForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; - return self; } + +#pragma mark - View life cycle + - (void)viewDidLoad { [super viewDidLoad]; - - // now that the view has loaded we can generate the content - [self generateView]; + + // Handle when the app enters the foreground. + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppEnteredForeground) name:UIApplicationWillEnterForegroundNotification object:nil]; + + self.view.backgroundColor = [UIColor clearColor]; + + // Add all our subviews + if (self.videoURL != nil) { + [self.moviePlayerController.backgroundView addSubview:self.thumbnailImageView]; + [self.view addSubview:self.moviePlayerController.view]; + } + + [self.view addSubview:self.iconImageView]; + [self.view addSubview:self.titleLabel]; + [self.view addSubview:self.bodyLabel]; + [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 + // 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]; } - // call our view will appear block + // Call our view will appear block if (self.viewWillAppearBlock) { dispatch_async(dispatch_get_main_queue(), ^{ self.viewWillAppearBlock(); @@ -172,20 +197,20 @@ - (void)viewWillAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - // if we have a delegate set, mark ourselves as the current page now that + // 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 + // Call our view did appear block if (self.viewDidAppearBlock) { dispatch_async(dispatch_get_main_queue(), ^{ self.viewDidAppearBlock(); }); } - // if we have a video, start playing + // If we have a video, start playing if (self.moviePlayerController.playbackState != MPMoviePlaybackStatePlaying) { self.moviePlayerController.currentPlaybackTime = 0.0; [self.moviePlayerController play]; @@ -195,7 +220,7 @@ - (void)viewDidAppear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; - // call our view will disappear block + // Call our view will disappear block if (self.viewWillDisappearBlock) { dispatch_async(dispatch_get_main_queue(), ^{ self.viewWillDisappearBlock(); @@ -206,116 +231,57 @@ - (void)viewWillDisappear:(BOOL)animated { - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; - // call our view did disappear block + // Call our view did disappear block if (self.viewDidDisappearBlock) { dispatch_async(dispatch_get_main_queue(), ^{ self.viewDidDisappearBlock(); }); } - // if we have a video, stop playing + // If we have a video, stop playing if (self.moviePlayerController.playbackState != MPMoviePlaybackStateStopped) { [self.moviePlayerController stop]; } } -- (void)setButtonActionHandler:(action_callback)actionBlock { - _buttonActionHandler = actionBlock ?: ^(OnboardingViewController *controller){}; -} -- (void)generateView { - // If no background color specified, - // we want our background to be clear so we can see through it to the image provided - self.view.backgroundColor = self.backgroundColor ? - self.backgroundColor : [UIColor clearColor]; - - // do some calculation for some common values we'll need, namely the width of the view, - // the center of the width, and the content width we want to fill up, which is some - // fraction of the view width we set in the multipler constant - CGFloat viewWidth = CGRectGetWidth(self.view.frame); - CGFloat horizontalCenter = viewWidth / 2; - CGFloat contentWidth = viewWidth * kContentWidthMultiplier; - - if (_image) { - - // create the image view with the appropriate image, size, and center in on screen - _imageView = [[UIImageView alloc] initWithImage:_image]; - [_imageView setFrame:CGRectMake(horizontalCenter - (self.iconWidth / 2), self.topPadding, self.iconWidth, self.iconHeight)]; - [self.view addSubview:_imageView]; - - } else if (self.videoURL) { - - // or if we have a video create and configure the video player controller - self.moviePlayerController = [MPMoviePlayerController new]; - self.moviePlayerController.contentURL = self.videoURL; +#pragma mark - Layout + +- (void)viewWillLayoutSubviews { + [super viewWillLayoutSubviews]; + + if (self.videoURL) { self.moviePlayerController.view.frame = self.view.frame; - self.moviePlayerController.repeatMode = MPMovieRepeatModeOne; - self.moviePlayerController.controlStyle = MPMovieControlStyleNone; - - UIImageView *thumbnailImageView = [[UIImageView alloc] initWithImage:[self thumbnailImageForVideo:self.videoURL]]; - thumbnailImageView.frame = self.view.frame; - thumbnailImageView.contentMode = UIViewContentModeScaleAspectFit; - - [self.moviePlayerController.backgroundView addSubview:thumbnailImageView]; - - [self.view addSubview:self.moviePlayerController.view]; - } - - // create and configure the main text label sitting underneath the icon with the provided padding - _mainTextLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_imageView.frame) + self.underIconPadding, contentWidth, 0)]; - _mainTextLabel.accessibilityIdentifier = kOnboardMainTextAccessibilityIdentifier; - _mainTextLabel.text = _titleText; - _mainTextLabel.textColor = self.titleTextColor; - _mainTextLabel.font = [UIFont fontWithName:self.titleFontName size:self.titleFontSize]; - _mainTextLabel.numberOfLines = 0; - _mainTextLabel.textAlignment = NSTextAlignmentCenter; - [_mainTextLabel sizeToFit]; - _mainTextLabel.center = CGPointMake(horizontalCenter, _mainTextLabel.center.y); - [self.view addSubview:_mainTextLabel]; - - // create and configure the sub text label - _subTextLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_mainTextLabel.frame) + self.underTitlePadding, contentWidth, 0)]; - _subTextLabel.accessibilityIdentifier = kOnboardSubTextAccessibilityIdentifier; - _subTextLabel.text = _body; - _subTextLabel.textColor = self.bodyTextColor; - _subTextLabel.font = [UIFont fontWithName:self.bodyFontName size:self.bodyFontSize]; - _subTextLabel.numberOfLines = 0; - _subTextLabel.textAlignment = NSTextAlignmentCenter; - [_subTextLabel sizeToFit]; - _subTextLabel.center = CGPointMake(horizontalCenter, _subTextLabel.center.y); - [self.view addSubview:_subTextLabel]; - - // create the action button if we were given button text - if (_buttonText) { - _actionButton = [[UIButton alloc] initWithFrame:CGRectMake((CGRectGetMaxX(self.view.frame) / 2) - (contentWidth / 2), CGRectGetMaxY(self.view.frame) - self.underPageControlPadding - kMainPageControlHeight - kActionButtonHeight - self.bottomPadding, contentWidth, kActionButtonHeight)]; - _actionButton.accessibilityIdentifier = kOnboardActionButtonAccessibilityIdentifier; - _actionButton.titleLabel.font = [UIFont fontWithName:self.buttonFontName size:self.buttonFontSize]; - [_actionButton setTitle:_buttonText forState:UIControlStateNormal]; - [_actionButton setTitleColor:self.buttonTextColor forState:UIControlStateNormal]; - [_actionButton addTarget:self action:@selector(handleButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - [self.view addSubview:_actionButton]; } -} -- (UIImage *)thumbnailImageForVideo:(NSURL *)videoURL { - - AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil]; - AVAssetImageGenerator *assetIG = [[AVAssetImageGenerator alloc] initWithAsset:asset]; - assetIG.appliesPreferredTrackTransform = YES; - assetIG.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels; - - NSError *error = nil; - CGImageRef thumbnailImageRef = [assetIG copyCGImageAtTime:CMTimeMake(0, 60) actualTime:NULL error:&error]; - - if (!error) { - UIImage *thumbnailImage = [[UIImage alloc] initWithCGImage:thumbnailImageRef]; - return thumbnailImage; - } else { - NSLog(@"thumbnailImageGenerationError %@", error); - return nil; + if (self.thumbnailImageView) { + self.thumbnailImageView.frame = self.view.frame; } + + CGFloat viewWidth = CGRectGetWidth(self.view.frame); + CGFloat contentWidth = viewWidth * kContentWidthMultiplier; + CGFloat xPadding = (viewWidth - contentWidth) / 2.0; + + [self.iconImageView setFrame:CGRectMake((viewWidth / 2.0) - (self.iconWidth / 2.0), self.topPadding, self.iconWidth, self.iconHeight)]; + + CGFloat titleYOrigin = CGRectGetMaxY(self.iconImageView.frame) + self.underIconPadding; + + self.titleLabel.frame = CGRectMake(xPadding, titleYOrigin, contentWidth, 0); + [self.titleLabel sizeToFit]; + self.titleLabel.frame = CGRectMake(xPadding, titleYOrigin, contentWidth, CGRectGetHeight(self.titleLabel.frame)); + + CGFloat bodyYOrigin = CGRectGetMaxY(self.titleLabel.frame) + self.underTitlePadding; + + self.bodyLabel.frame = CGRectMake(xPadding, bodyYOrigin, contentWidth, 0); + [self.bodyLabel sizeToFit]; + self.bodyLabel.frame = CGRectMake(xPadding, bodyYOrigin, contentWidth, CGRectGetHeight(self.bodyLabel.frame)); + + self.actionButton.frame = CGRectMake((CGRectGetMaxX(self.view.frame) / 2) - (contentWidth / 2), CGRectGetMaxY(self.view.frame) - self.underPageControlPadding - kMainPageControlHeight - kActionButtonHeight - self.bottomPadding, contentWidth, kActionButtonHeight); } + +#pragma mark - App state + - (void)handleAppEnteredForeground { //If the movie player is paused, as it does by default when backgrounded, start playing again. if (self.moviePlayerController.playbackState == MPMoviePlaybackStatePaused) { @@ -323,16 +289,18 @@ - (void)handleAppEnteredForeground { } } + #pragma mark - Transition alpha - (void)updateAlphas:(CGFloat)newAlpha { - _imageView.alpha = newAlpha; - _mainTextLabel.alpha = newAlpha; - _subTextLabel.alpha = newAlpha; - _actionButton.alpha = newAlpha; + self.iconImageView.alpha = newAlpha; + self.titleLabel.alpha = newAlpha; + self.bodyLabel.alpha = newAlpha; + self.actionButton.alpha = newAlpha; } -#pragma mark - action button callback + +#pragma mark - Action button stuff - (void)handleButtonPressed { // if we want to navigate to the next view controller, tell our delegate @@ -342,8 +310,30 @@ - (void)handleButtonPressed { } // call the provided action handler - if (_buttonActionHandler) { - _buttonActionHandler(self.delegate); + if (self.buttonActionHandler) { + self.buttonActionHandler(self.delegate); + } +} + + +#pragma mark - Utils + +- (UIImage *)thumbnailImageForVideo:(NSURL *)videoURL { + + AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil]; + AVAssetImageGenerator *assetIG = [[AVAssetImageGenerator alloc] initWithAsset:asset]; + assetIG.appliesPreferredTrackTransform = YES; + assetIG.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels; + + NSError *error = nil; + CGImageRef thumbnailImageRef = [assetIG copyCGImageAtTime:CMTimeMake(0, 60) actualTime:NULL error:&error]; + + if (!error) { + UIImage *thumbnailImage = [[UIImage alloc] initWithCGImage:thumbnailImageRef]; + return thumbnailImage; + } else { + NSLog(@"thumbnailImageGenerationError %@", error); + return nil; } } diff --git a/Source/OnboardingViewController.h b/Source/OnboardingViewController.h index 6e0bfe2..bf63200 100644 --- a/Source/OnboardingViewController.h +++ b/Source/OnboardingViewController.h @@ -10,86 +10,158 @@ #import "OnboardingContentViewController.h" @import MediaPlayer; -@interface OnboardingViewController : UIViewController +@interface OnboardingViewController : UIViewController -// View controllers and background image +/** + * @brief The onboarding content view controllers. + */ @property (nonatomic, strong) NSArray *viewControllers; + + +/** + * @brief The background image that will be visible through the content view controllers. + */ @property (nonatomic, strong) UIImage *backgroundImage; -// Masking, blurring, fading, etc. + +/** + * @brief Determines whether or not the background will be masked. The default value of this property is YES. + */ @property (nonatomic) BOOL shouldMaskBackground; + + +/** + * @brief Determines whether or not the background will be blurred. The default value of this property is NO; + */ @property (nonatomic) BOOL shouldBlurBackground; + + +/** + * @brief Determines whether or not the contents on screen will fade as the user swipes between pages. The default value of this property is NO. + */ @property (nonatomic) BOOL shouldFadeTransitions; + + +/** + * @brief Determines whether or not the background will be masked. The default value of this property is NO. + */ @property (nonatomic) BOOL fadePageControlOnLastPage; + + +/** + * @brief Determines whether or not the skip button will fade away on the last page. The default value of this property is NO. + */ @property (nonatomic) BOOL fadeSkipButtonOnLastPage; -// Skipping + +/** + * @brief Determines whether or not the ship button will be shown. The default value of this property is NO. + */ @property (nonatomic) BOOL allowSkipping; + + +/** + * @brief A block that will be executed when the skip button is pressed. + */ @property (nonatomic, strong) dispatch_block_t skipHandler; -// Swiping + +/** + * @brief Determines whether or not swiping is enabled between pages. The default value of this property is YES. + */ @property (nonatomic) BOOL swipingEnabled; -// Page Control -@property (nonatomic) BOOL hidePageControl; + +/** + * @brief Determines whether or not the page cotrol will be visible. + */ @property (nonatomic, strong) UIPageControl *pageControl; -// Skip Button + +/** + * @brief The skip button that allows users to skip onboarding anytime. + */ @property (nonatomic, strong) UIButton *skipButton; -// Movie player + +/** + * @brief Determines whether or not the movie player stops playing when the view disappears. + */ @property (nonatomic) BOOL stopMoviePlayerWhenDisappear; + + +/** + * @brief The movie player controller used to play background movies. + */ @property (nonatomic) MPMoviePlayerController *moviePlayerController; -// Initializers + +/** + * @brief The padding between the bottom of the screen and the bottom of the page control. + */ +@property (nonatomic) CGFloat underPageControlPadding; + + +/** + * @brief Convenience class initializer for onboarding with a backround image. + * @return An instance of OnboardingViewController with the provided background image and content view controllers. + */ + (instancetype)onboardWithBackgroundImage:(UIImage *)backgroundImage contents:(NSArray *)contents; + + +/** + * @brief Initializer for onboarding with a backround video. + * @return An instance of OnboardingViewController with the provided background video and content view controllers. + */ - (instancetype)initWithBackgroundImage:(UIImage *)backgroundImage contents:(NSArray *)contents; -+ (instancetype)onboardWithBackgroundVideoURL:(NSURL *)backgroundVideoURL contents:(NSArray *)contents; -- (instancetype)initWithBackgroundVideoURL:(NSURL *)backgroundVideoURL contents:(NSArray *)contents; -// Manually moving to next page -- (void)moveNextPage; +/** + * @brief Convenience class initializer for onboarding with a backround video. + * @return An instance of OnboardingViewController with the provided background video and content view controllers. + */ ++ (instancetype)onboardWithBackgroundVideoURL:(NSURL *)backgroundVideoURL contents:(NSArray *)contents; -//////////////////////////////////////////////////////////////////// -// These are convenience properties for content view customization, so you -// can set these properties on the master onboarding view controller and -// it will make sure they trickle down to each content view controller, -// rather than having to individually set the same values on each -@property (nonatomic) CGFloat iconSize; // set this if you want the icon to have the same width and height for all contents -@property (nonatomic) CGFloat iconHeight; -@property (nonatomic) CGFloat iconWidth; +/** + * @brief Initializer for onboarding with a backround video. + * @return An instance of OnboardingViewController with the provided background video and content view controllers. + */ +- (instancetype)initWithBackgroundVideoURL:(NSURL *)backgroundVideoURL contents:(NSArray *)contents; -@property (nonatomic, strong) UIColor *titleTextColor; -@property (nonatomic, strong) UIColor *bodyTextColor; -@property (nonatomic, strong) UIColor *buttonTextColor; -@property (nonatomic, strong) NSString *fontName; // to set the same font for everything +/** + * @brief Method to tell the onboarding view controller to automatically move to the next page. + */ +- (void)moveNextPage; -@property (nonatomic, strong) NSString *titleFontName; -@property (nonatomic) CGFloat titleFontSize; -@property (nonatomic, strong) NSString *bodyFontName; -@property (nonatomic) CGFloat bodyFontSize; +// The following properties are all deprecated, and will be removed in the next release of Onboard. +// +// +@property (nonatomic) BOOL hidePageControl __attribute__((deprecated("Modify the pageControl property directly. This property will be removed in the next update."))); -@property (nonatomic, strong) NSString *buttonFontName; -@property (nonatomic) CGFloat buttonFontSize; +@property (nonatomic) CGFloat iconSize __attribute__((deprecated("Modify the content view controller's iconSize directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat iconHeight __attribute__((deprecated("Modify the content view controller's iconHeight directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat iconWidth __attribute__((deprecated("Modify the content view controller's iconWidth directly. This property will be removed in the next update."))); -@property (nonatomic) CGFloat topPadding; -@property (nonatomic) CGFloat underIconPadding; -@property (nonatomic) CGFloat underTitlePadding; -@property (nonatomic) CGFloat bottomPadding; -@property (nonatomic) CGFloat underPageControlPadding; +@property (nonatomic, strong) UIColor *titleTextColor __attribute__((deprecated("Modify the content view controller's titleLabel directly. This property will be removed in the next update."))); +@property (nonatomic, strong) NSString *titleFontName __attribute__((deprecated("Modify the content view controller's titleLabel directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat titleFontSize __attribute__((deprecated("Modify the content view controller's titleLabel directly. This property will be removed in the next update."))); -//////////////////////////////////////////////////////////////////// +@property (nonatomic, strong) UIColor *bodyTextColor __attribute__((deprecated("Modify the content view controller's bodyLabel directly. This property will be removed in the next update."))); +@property (nonatomic, strong) NSString *bodyFontName __attribute__((deprecated("Modify the content view controller's bodyLabel directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat bodyFontSize __attribute__((deprecated("Modify the content view controller's bodyLabel directly."))); +@property (nonatomic, strong) UIColor *buttonTextColor __attribute__((deprecated("Modify the content view controller's actionButton directly. This property will be removed in the next update."))); +@property (nonatomic, strong) NSString *buttonFontName __attribute__((deprecated("Modify the content view controller's actionButton directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat buttonFontSize __attribute__((deprecated("Modify the content view controller's actionButton directly. This property will be removed in the next update."))); -//////////////////////////////////////////////////////////////////// -// Delegate methods for internal use. -- (void)setCurrentPage:(OnboardingContentViewController *)currentPage; -- (void)setNextPage:(OnboardingContentViewController *)nextPage; -//////////////////////////////////////////////////////////////////// +@property (nonatomic, strong) NSString *fontName __attribute__((deprecated("Modify the content view controller's labels directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat topPadding __attribute__((deprecated("Modify the content view controller's topPadding directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat underIconPadding __attribute__((deprecated("Modify the content view controller's underIconPadding directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat underTitlePadding __attribute__((deprecated("Modify the content view controller's underTitlePadding directly. This property will be removed in the next update."))); +@property (nonatomic) CGFloat bottomPadding __attribute__((deprecated("Modify the content view controller's bottomPadding directly. This property will be removed in the next update."))); @end diff --git a/Source/OnboardingViewController.m b/Source/OnboardingViewController.m index 3f7807f..481c400 100644 --- a/Source/OnboardingViewController.m +++ b/Source/OnboardingViewController.m @@ -19,29 +19,37 @@ static NSString * const kSkipButtonText = @"Skip"; -@implementation OnboardingViewController { - NSURL *_videoURL; - UIPageViewController *_pageVC; - - OnboardingContentViewController *_currentPage; - OnboardingContentViewController *_upcomingPage; -} + +@interface OnboardingViewController () + +@property (nonatomic, strong) OnboardingContentViewController *currentPage; +@property (nonatomic, strong) OnboardingContentViewController *upcomingPage; + +@property (nonatomic, strong) UIPageViewController *pageVC; +@property (nonatomic, strong) NSURL *videoURL; + +@end + + +@implementation OnboardingViewController - (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:UIApplicationWillEnterForegroundNotification]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - Initializing with images + (instancetype)onboardWithBackgroundImage:(UIImage *)backgroundImage contents:(NSArray *)contents { - OnboardingViewController *onboardingVC = [[self alloc] initWithBackgroundImage:backgroundImage contents:contents]; - return onboardingVC; + return [[self alloc] initWithBackgroundImage:backgroundImage contents:contents]; } - (instancetype)initWithBackgroundImage:(UIImage *)backgroundImage contents:(NSArray *)contents { self = [self initWithContents:contents]; - - // store the passed in view controllers array + + if (self == nil) { + return nil; + } + self.backgroundImage = backgroundImage; return self; @@ -51,15 +59,17 @@ - (instancetype)initWithBackgroundImage:(UIImage *)backgroundImage contents:(NSA #pragma mark - Initializing with video files + (instancetype)onboardWithBackgroundVideoURL:(NSURL *)backgroundVideoURL contents:(NSArray *)contents { - OnboardingViewController *onboardingVC = [[self alloc] initWithBackgroundVideoURL:backgroundVideoURL contents:contents]; - return onboardingVC; + return [[self alloc] initWithBackgroundVideoURL:backgroundVideoURL contents:contents]; } - (instancetype)initWithBackgroundVideoURL:(NSURL *)backgroundVideoURL contents:(NSArray *)contents { self = [self initWithContents:contents]; - - // store the passed in video URL - _videoURL = backgroundVideoURL; + + if (self == nil) { + return nil; + } + + self.videoURL = backgroundVideoURL; return self; } @@ -69,23 +79,26 @@ - (instancetype)initWithBackgroundVideoURL:(NSURL *)backgroundVideoURL contents: - (instancetype)initWithContents:(NSArray *)contents { self = [super init]; + + if (self == nil) { + return nil; + } - // store the passed in view controllers array + // Store the passed in view controllers array self.viewControllers = contents; - // set the default properties + // Set the default properties self.shouldMaskBackground = YES; self.shouldBlurBackground = NO; self.shouldFadeTransitions = NO; self.fadePageControlOnLastPage = NO; self.fadeSkipButtonOnLastPage = NO; self.swipingEnabled = YES; - self.hidePageControl = NO; self.allowSkipping = NO; self.skipHandler = ^{}; - // create the initial exposed components so they can be customized + // Create the initial exposed components so they can be customized self.pageControl = [UIPageControl new]; self.pageControl.numberOfPages = self.viewControllers.count; self.pageControl.userInteractionEnabled = NO; @@ -95,7 +108,7 @@ - (instancetype)initWithContents:(NSArray *)contents { [self.skipButton addTarget:self action:@selector(handleSkipButtonPressed) forControlEvents:UIControlEventTouchUpInside]; self.skipButton.titleLabel.adjustsFontSizeToFitWidth = YES; - // create the movie player controller + // Create the movie player controller self.moviePlayerController = [MPMoviePlayerController new]; // Handle when the app enters the foreground. @@ -197,10 +210,8 @@ - (void)generateView { } // create and configure the page control - if (!self.hidePageControl) { - self.pageControl.frame = CGRectMake(0, CGRectGetMaxY(self.view.frame) - self.underPageControlPadding - kPageControlHeight, self.view.frame.size.width, kPageControlHeight); - [self.view addSubview:self.pageControl]; - } + self.pageControl.frame = CGRectMake(0, CGRectGetMaxY(self.view.frame) - self.underPageControlPadding - kPageControlHeight, self.view.frame.size.width, kPageControlHeight); + [self.view addSubview:self.pageControl]; // if we allow skipping, setup the skip button if (self.allowSkipping) { @@ -359,8 +370,7 @@ - (UIViewController *)pageViewController:(UIPageViewController *)pageViewControl // return the previous view controller in the array unless we're at the beginning if (viewController == [self.viewControllers firstObject]) { return nil; - } - else { + } else { NSInteger priorPageIndex = [self.viewControllers indexOfObject:viewController] - 1; return self.viewControllers[priorPageIndex]; } @@ -370,8 +380,7 @@ - (UIViewController *)pageViewController:(UIPageViewController *)pageViewControl // return the next view controller in the array unless we're at the end if (viewController == [self.viewControllers lastObject]) { return nil; - } - else { + } else { NSInteger nextPageIndex = [_viewControllers indexOfObject:viewController] + 1; return self.viewControllers[nextPageIndex]; }