Commit d956de37 by Dima Bart

Merge pull request #157 from Shopify/task/ui-migration

Migrate view classes from framework to advanced sample app
parents 53102c3a 5c64de6e
// //
// BUYTheme+Additions.h // Theme+Additions.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,52 +24,13 @@ ...@@ -24,52 +24,13 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYTheme.h" #import "Theme.h"
#import "BUYPaymentButton.h"
#define BUY_RGB(r, g, b) BUY_RGBA(r, g, b, 1) @interface Theme (Additions)
#define BUY_RGBA(r, g, b, a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
@interface BUYTheme (Additions)
#pragma mark - Colors #pragma mark - Colors
/** /**
* The product view background color used in various views based on the theme
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)backgroundColor;
/**
* The background color for selected table view cells based on the theme
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)selectedBackgroundColor;
/**
* The product view's table view separator color based on the theme
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)separatorColor;
/**
* The product view's table view disclosure indicator color based on the theme
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)disclosureIndicatorColor;
/**
* The Checkout button's text color based on the theme
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)checkoutButtonTextColor;
/**
* Top gradient color * Top gradient color
* *
* @return A dark color * @return A dark color
...@@ -77,82 +38,33 @@ ...@@ -77,82 +38,33 @@
+ (UIColor*)topGradientViewTopColor; + (UIColor*)topGradientViewTopColor;
/** /**
* The background color for the error toast view in the product view
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)errorTintOverlayColor;
/**
* The navigation bar's title color based on the theme
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)navigationBarTitleColor;
/**
* The variant selection's navigation bar's title color based on the theme
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)navigationBarTitleVariantSelectionColor;
/**
* The color for the product title text
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)productTitleColor;
/**
* The color for the product "compare at" text * The color for the product "compare at" text
* *
* @return The color appropriate for the BUYThemeStyle * @return The color appropriate for the ThemeStyle
*/ */
+ (UIColor*)comparePriceTextColor; + (UIColor*)comparePriceTextColor;
/** /**
* The color for the product's description text * The color for the product's description text
* *
* @return The color appropriate for the BUYThemeStyle * @return The color appropriate for the ThemeStyle
*/ */
+ (UIColor*)descriptionTextColor; + (UIColor*)descriptionTextColor;
/** /**
* The color for the variant option name text
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)variantOptionNameTextColor;
/**
* The color for the variant price text * The color for the variant price text
* *
* @return The color appropriate for the BUYThemeStyle * @return The color appropriate for the ThemeStyle
*/ */
+ (UIColor*)variantPriceTextColor; + (UIColor*)variantPriceTextColor;
/** /**
* The color for the variant "sold out" text * The color for the variant "sold out" text
* *
* @return The color appropriate for the BUYThemeStyle * @return The color appropriate for the ThemeStyle
*/ */
+ (UIColor*)variantSoldOutTextColor; + (UIColor*)variantSoldOutTextColor;
/**
* The background color for the variant selection bread crumb view
*
* @return The color appropriate for the BUYThemeStyle
*/
- (UIColor*)variantBreadcrumbsBackground;
/**
* The text color for the variant selection bread crumb view
*
* @return The color appropriate for the BUYThemeStyle
*/
+ (UIColor*)variantBreadcrumbsTextColor;
#pragma mark - Padding and Sizes #pragma mark - Padding and Sizes
/** /**
...@@ -258,34 +170,4 @@ extern CGFloat const kBuyBottomGradientHeightWithoutPageControl; ...@@ -258,34 +170,4 @@ extern CGFloat const kBuyBottomGradientHeightWithoutPageControl;
*/ */
+ (UIFont*)errorLabelFont; + (UIFont*)errorLabelFont;
#pragma mark - Misc
/**
* Blur effect style for for the product view
*
* @return The blur effect appropriate for the theme
*/
- (UIBlurEffect*)blurEffect;
/**
* Activity indicator style for the product view when loading a product
*
* @return The activity indicator style appropriate for the theme
*/
- (UIActivityIndicatorViewStyle)activityIndicatorViewStyle;
/**
* Navigation bar style for the product view
*
* @return The navigation bar style appropriate for the theme
*/
- (UIBarStyle)navigationBarStyle;
/**
* Apple Pay button style for the product view
*
* @return The button style appropriate for the theme
*/
- (BUYPaymentButtonStyle)paymentButtonStyle;
@end @end
// //
// BUYTheme+Additions.m // Theme+Additions.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,63 +24,19 @@ ...@@ -24,63 +24,19 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYTheme+Additions.h" #import "Theme+Additions.h"
#import "UIFont+BUYAdditions.h" #import "UIFont+BUYAdditions.h"
#import "UIColor+BUYAdditions.h"
@implementation BUYTheme (Additions) @implementation Theme (Additions)
#pragma mark - Colors #pragma mark - Colors
- (UIColor*)backgroundColor
{
return self.style == BUYThemeStyleDark ? BUY_RGB(26, 26, 26) : [UIColor whiteColor];
}
- (UIColor*)selectedBackgroundColor
{
return self.style == BUYThemeStyleDark ? BUY_RGB(60, 60, 60) : BUY_RGB(242, 242, 242);
}
- (UIColor*)separatorColor
{
return self.style == BUYThemeStyleDark ? BUY_RGB(76, 76, 76) : BUY_RGB(217, 217, 217);
}
- (UIColor*)disclosureIndicatorColor
{
return self.style == BUYThemeStyleDark ? BUY_RGB(76, 76, 76) : BUY_RGB(191, 191, 191);
}
- (UIColor*)checkoutButtonTextColor
{
return self.style == BUYThemeStyleLight ? [UIColor whiteColor] : [UIColor blackColor];
}
+ (UIColor*)topGradientViewTopColor + (UIColor*)topGradientViewTopColor
{ {
return [UIColor colorWithWhite:0 alpha:0.25f]; return [UIColor colorWithWhite:0 alpha:0.25f];
} }
- (UIColor*)errorTintOverlayColor
{
return self.style == BUYThemeStyleDark ? BUY_RGBA(255, 66, 66, 0.75f) : BUY_RGBA(209, 44, 44, 0.75f);
}
- (UIColor*)navigationBarTitleColor
{
return self.style == BUYThemeStyleDark ? [UIColor whiteColor] : [UIColor blackColor];
}
- (UIColor*)navigationBarTitleVariantSelectionColor
{
return self.style == BUYThemeStyleDark ? BUY_RGB(229, 229, 229) : [UIColor blackColor];
}
- (UIColor*)productTitleColor
{
return self.style == BUYThemeStyleDark ? [UIColor whiteColor] : [UIColor blackColor];
}
+ (UIColor*)comparePriceTextColor + (UIColor*)comparePriceTextColor
{ {
return [UIColor colorWithWhite:0.4f alpha:1]; return [UIColor colorWithWhite:0.4f alpha:1];
...@@ -91,11 +47,6 @@ ...@@ -91,11 +47,6 @@
return [UIColor colorWithWhite:0.4f alpha:1]; return [UIColor colorWithWhite:0.4f alpha:1];
} }
- (UIColor*)variantOptionNameTextColor
{
return self.style == BUYThemeStyleDark ? BUY_RGB(76, 76, 76) : BUY_RGB(191, 191, 191);
}
+ (UIColor*)variantPriceTextColor + (UIColor*)variantPriceTextColor
{ {
return BUY_RGB(140, 140, 140); return BUY_RGB(140, 140, 140);
...@@ -106,16 +57,6 @@ ...@@ -106,16 +57,6 @@
return BUY_RGB(220, 96, 96); return BUY_RGB(220, 96, 96);
} }
- (UIColor*)variantBreadcrumbsBackground
{
return self.style == BUYThemeStyleDark ? BUY_RGB(13, 13, 13) : BUY_RGB(229, 229, 229);
}
+ (UIColor*)variantBreadcrumbsTextColor
{
return BUY_RGB(140, 140, 140);
}
#pragma mark - Padding and Sizes #pragma mark - Padding and Sizes
CGFloat const kBuyPaddingSmall = 8.0f; CGFloat const kBuyPaddingSmall = 8.0f;
...@@ -180,26 +121,4 @@ CGFloat const kBuyBottomGradientHeightWithoutPageControl = 20.0f; ...@@ -180,26 +121,4 @@ CGFloat const kBuyBottomGradientHeightWithoutPageControl = 20.0f;
return [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; return [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
} }
#pragma mark - Misc
- (UIBlurEffect*)blurEffect
{
return [UIBlurEffect effectWithStyle:self.style == BUYThemeStyleDark ? UIBlurEffectStyleDark : UIBlurEffectStyleLight];
}
- (UIActivityIndicatorViewStyle)activityIndicatorViewStyle
{
return self.style == BUYThemeStyleDark ? UIActivityIndicatorViewStyleWhite : UIActivityIndicatorViewStyleGray;
}
- (UIBarStyle)navigationBarStyle
{
return self.style == BUYThemeStyleDark ? UIBarStyleBlack : UIBarStyleDefault;
}
- (BUYPaymentButtonStyle)paymentButtonStyle
{
return self.style == BUYThemeStyleLight ? BUYPaymentButtonStyleBlack : BUYPaymentButtonStyleWhite;
}
@end @end
// //
// BUYTheme.h // Theme.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -29,49 +29,38 @@ ...@@ -29,49 +29,38 @@
/** /**
* The theme style for the BUYProductViewController * The theme style for the BUYProductViewController
*/ */
typedef NS_ENUM(NSInteger, BUYThemeStyle){ typedef NS_ENUM(NSInteger, ThemeStyle) {
/** /**
* Light theme style, providing light backgrounds and light styled UIVisualEffectViews * Light theme style, providing light backgrounds and light styled UIVisualEffectViews
*/ */
BUYThemeStyleLight, ThemeStyleLight,
/** /**
* Dark theme style, providing dark backgrounds and dark styled UIVisualEffectViews * Dark theme style, providing dark backgrounds and dark styled UIVisualEffectViews
*/ */
BUYThemeStyleDark ThemeStyleDark
}; };
/** /**
* This class provides properties used for theming the BUYProductViewController UI elements. * This class provides properties used for theming the BUYProductViewController UI elements.
*/ */
@interface BUYTheme : NSObject @interface Theme : NSObject
/** /**
* Used for the highlight color * Used for the highlight color. Default is the tintColor of the app's keyWindow if not nil,
* other falls back to a "Shopify green" color
*/ */
@property (nonatomic, strong) UIColor *tintColor; @property (nonatomic, strong) UIColor *tintColor;
/** /**
* Theme style for the views * Theme style for the views
*/ */
@property (nonatomic, assign) BUYThemeStyle style; @property (nonatomic, assign) ThemeStyle style;
/** /**
* Determines whether a blurred scaled-up product image should appear behind the product details. Default is YES * Determines whether a blurred scaled-up product image should appear behind the product details. Default is YES
*/ */
@property (nonatomic, assign) BOOL showsProductImageBackground; @property (nonatomic, assign) BOOL showsProductImageBackground;
@end - (void)styleProductViewController;
/**
* Protocol to enable theming for any class
*/
@protocol BUYThemeable <NSObject>
/**
* Sets the theme for the view, and its subviews
*
* @param theme The new theme to apply
*/
- (void)setTheme:(BUYTheme *)theme;
@end @end
//
// Theme.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "DisclosureIndicatorView.h"
#import "ErrorView.h"
#import "NavigationController.h"
#import "OptionSelectionNavigationController.h"
#import "OptionValueCell.h"
#import "ProductDescriptionCell.h"
#import "ProductHeaderCell.h"
#import "ProductVariantCell.h"
#import "OptionBreadCrumbsView.h"
#import "HeaderOverlayView.h"
#import "ProductViewNavigationController.h"
#import "ProductViewController.h"
#import "ProductView.h"
#import "Theme.h"
#import "Theme+Additions.h"
#import "VariantOptionView.h"
#import "VisualEffectView.h"
#import "UIColor+BUYAdditions.h"
#import "CheckoutButton.h"
@implementation Theme
- (instancetype)init
{
self = [super init];
if (self) {
self.style = ThemeStyleLight;
self.showsProductImageBackground = YES;
}
return self;
}
- (void)styleProductViewController
{
UIColor *contentBackgroundColor = self.style == ThemeStyleDark ? BUY_RGB(26, 26, 26) : [UIColor whiteColor];
UIColor *contentBackgroundSelectedColor = self.style == ThemeStyleDark ? BUY_RGB(60, 60, 60) : BUY_RGB(242, 242, 242);
UIColor *contentBackgroundComplimentaryColor = self.style == ThemeStyleDark ? [UIColor whiteColor] : [UIColor blackColor];
UIColor *primaryColor = self.tintColor;
UIColor *secondaryLightColor = self.style == ThemeStyleDark ? BUY_RGB(13, 13, 13) : BUY_RGB(229, 229, 229);
UIColor *secondaryMediumColor = self.style == ThemeStyleDark ? BUY_RGB(76, 76, 76) : BUY_RGB(217, 217, 217);
UIColor *secondaryDarkColor = self.style == ThemeStyleDark ? BUY_RGB(76, 76, 76) : BUY_RGB(191, 191, 191);
UIBarStyle barStyle = self.style == ThemeStyleDark ? UIBarStyleBlack : UIBarStyleDefault;
UIColor *errorColor = self.style == ThemeStyleDark ? BUY_RGBA(255, 66, 66, 0.75f) : BUY_RGBA(209, 44, 44, 0.75f);
UIBlurEffectStyle blurEffectStyle = self.style == ThemeStyleDark ? UIBlurEffectStyleDark : UIBlurEffectStyleLight;
UIScrollViewIndicatorStyle scrollViewIndicatorStyle = self.style == ThemeStyleDark ? UIScrollViewIndicatorStyleWhite : UIScrollViewIndicatorStyleDefault;
// Add some Theme-specific styling:
UIColor *navigationBarTitleColor = self.style == ThemeStyleDark ? [UIColor whiteColor] : [UIColor blackColor];
UIColor *navigationBarTitleVariantSelectionColor = self.style == ThemeStyleDark ? BUY_RGB(229, 229, 229) : [UIColor blackColor];
if ([[UINavigationBar class] respondsToSelector:@selector(appearanceWhenContainedInInstancesOfClasses:)]) {
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[OptionSelectionNavigationController class]]] setTitleTextAttributes:@{ NSForegroundColorAttributeName : navigationBarTitleVariantSelectionColor }];
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[NavigationController class]]] setTitleTextAttributes:@{ NSForegroundColorAttributeName : navigationBarTitleColor }];
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[NavigationController class]]] setTintColor:primaryColor];
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[NavigationController class]]] setBarStyle:barStyle];
[[UITableView appearanceWhenContainedInInstancesOfClasses:@[[NavigationController class]]] setBackgroundColor:contentBackgroundColor];
[[UITableView appearanceWhenContainedInInstancesOfClasses:@[[NavigationController class]]] setSeparatorColor:secondaryMediumColor];
[[UIScrollView appearanceWhenContainedInInstancesOfClasses:@[[NavigationController class]]] setIndicatorStyle:scrollViewIndicatorStyle];
[[UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[ProductViewController class]]] setColor:primaryColor];
} else {
[[UINavigationBar appearanceWhenContainedIn:[OptionSelectionNavigationController class], nil] setTitleTextAttributes:@{ NSForegroundColorAttributeName : navigationBarTitleVariantSelectionColor }];
[[UINavigationBar appearanceWhenContainedIn:[NavigationController class], nil] setTitleTextAttributes:@{ NSForegroundColorAttributeName : navigationBarTitleColor }];
[[UINavigationBar appearanceWhenContainedIn:[NavigationController class], nil] setTintColor:primaryColor];
[[UINavigationBar appearanceWhenContainedIn:[NavigationController class], nil] setBarStyle:barStyle];
[[UITableView appearanceWhenContainedIn:[NavigationController class], nil] setBackgroundColor:contentBackgroundColor];
[[UITableView appearanceWhenContainedIn:[NavigationController class], nil] setSeparatorColor:secondaryMediumColor];
[[UIScrollView appearanceWhenContainedIn:[NavigationController class], nil] setIndicatorStyle:scrollViewIndicatorStyle];
[[UIActivityIndicatorView appearanceWhenContainedIn:[ProductViewController class], nil] setColor:primaryColor];
}
[[ProductView appearance] setTintColor:primaryColor];
[[ProductView appearance] setShowsProductImageBackground:self.showsProductImageBackground];
[[VariantOptionView appearance] setOptionNameTextColor:self.style == ThemeStyleDark ? BUY_RGB(76, 76, 76) : BUY_RGB(191, 191, 191)];
[[VisualEffectView appearance] setBlurEffectStyle:blurEffectStyle];
[[CheckoutButton appearance] setBackgroundColor:primaryColor];
[[CheckoutButton appearance] setTextColor:contentBackgroundColor];
[[DisclosureIndicatorView appearance] setTintColor:secondaryDarkColor];
[[ErrorView appearance] setOverlayColor:errorColor];
[[ProductView appearance] setBackgroundColor:contentBackgroundColor];
[[ProductHeaderCell appearance] setProductTitleColor:contentBackgroundComplimentaryColor];
[[ProductHeaderCell appearance] setBackgroundColor:contentBackgroundColor];
[[ProductDescriptionCell appearance] setBackgroundColor:contentBackgroundColor];
[[ProductVariantCell appearance] setBackgroundColor:contentBackgroundColor];
[[ProductVariantCell appearance] setSelectedBackgroundViewBackgroundColor:contentBackgroundSelectedColor];
[[VariantOptionView appearance] setBackgroundColor:contentBackgroundColor];
[[OptionValueCell appearance] setTintColor:primaryColor];
[[OptionValueCell appearance] setBackgroundColor:contentBackgroundColor];
[[OptionValueCell appearance] setSelectedBackgroundViewBackgroundColor:contentBackgroundSelectedColor];
[[HeaderOverlayView appearance] setOverlayBackgroundColor:contentBackgroundColor];
[[OptionBreadCrumbsView appearance] setBackgroundColor:secondaryLightColor];
[[OptionBreadCrumbsView appearance] setVariantOptionTextColor:BUY_RGB(140, 140, 140)];
}
@end
//
// VisualEffectView.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import UIKit;
@interface VisualEffectView : UIVisualEffectView
- (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle UI_APPEARANCE_SELECTOR;
@end
//
// VisualEffectView.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "VisualEffectView.h"
@implementation VisualEffectView
- (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle
{
self.effect = [UIBlurEffect effectWithStyle:blurEffectStyle];
}
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="rS3-R9-Ivy"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9059" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="rS3-R9-Ivy">
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9049"/>
</dependencies> </dependencies>
<scenes> <scenes>
<!--Master--> <!--Master-->
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
<navigationController title="Master" id="rS3-R9-Ivy" sceneMemberID="viewController"> <navigationController title="Master" id="rS3-R9-Ivy" sceneMemberID="viewController">
<navigationBar key="navigationBar" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="yXu-0R-QUA"> <navigationBar key="navigationBar" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="yXu-0R-QUA">
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<animations/>
</navigationBar> </navigationBar>
<connections> <connections>
<segue destination="pGg-6v-bdr" kind="relationship" relationship="rootViewController" id="RxB-wf-QIq"/> <segue destination="pGg-6v-bdr" kind="relationship" relationship="rootViewController" id="RxB-wf-QIq"/>
...@@ -27,6 +28,7 @@ ...@@ -27,6 +28,7 @@
<tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="mLL-gJ-YKr"> <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="mLL-gJ-YKr">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/> <color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<sections/> <sections/>
<connections> <connections>
......
...@@ -25,8 +25,9 @@ ...@@ -25,8 +25,9 @@
// //
@import UIKit; @import UIKit;
@class BUYCheckout; @import Buy;
@class BUYClient; @import PassKit;
@import SafariServices;
extern NSString * const CheckoutCallbackNotification; extern NSString * const CheckoutCallbackNotification;
......
...@@ -24,16 +24,14 @@ ...@@ -24,16 +24,14 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import <Buy/Buy.h>
#import "CheckoutViewController.h" #import "CheckoutViewController.h"
#import "GetCompletionStatusOperation.h" #import "GetCompletionStatusOperation.h"
#import "SummaryItemsTableViewCell.h" #import "SummaryItemsTableViewCell.h"
#import "ProductListViewController.h" #import "UIButton+PaymentButton.h"
@import Buy;
@import PassKit;
@import SafariServices;
NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification"; NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
NSString * const MerchantId = @"";
@interface CheckoutViewController () <GetCompletionStatusOperationDelegate, SFSafariViewControllerDelegate, PKPaymentAuthorizationViewControllerDelegate> @interface CheckoutViewController () <GetCompletionStatusOperationDelegate, SFSafariViewControllerDelegate, PKPaymentAuthorizationViewControllerDelegate>
...@@ -87,7 +85,7 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification"; ...@@ -87,7 +85,7 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
[webCheckoutButton addTarget:self action:@selector(checkoutOnWeb) forControlEvents:UIControlEventTouchUpInside]; [webCheckoutButton addTarget:self action:@selector(checkoutOnWeb) forControlEvents:UIControlEventTouchUpInside];
[footerView addSubview:webCheckoutButton]; [footerView addSubview:webCheckoutButton];
UIButton *applePayButton = [BUYPaymentButton buttonWithType:BUYPaymentButtonTypeBuy style:BUYPaymentButtonStyleBlack]; UIButton *applePayButton = [UIButton paymentButtonWithType:PaymentButtonTypeBuy style:PaymentButtonStyleBlack];
applePayButton.translatesAutoresizingMaskIntoConstraints = NO; applePayButton.translatesAutoresizingMaskIntoConstraints = NO;
[applePayButton addTarget:self action:@selector(checkoutWithApplePay) forControlEvents:UIControlEventTouchUpInside]; [applePayButton addTarget:self action:@selector(checkoutWithApplePay) forControlEvents:UIControlEventTouchUpInside];
[footerView addSubview:applePayButton]; [footerView addSubview:applePayButton];
...@@ -139,11 +137,11 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification"; ...@@ -139,11 +137,11 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
return cell; return cell;
} }
- (void)addCreditCardToCheckout:(void (^)(BOOL success))callback - (void)addCreditCardToCheckout:(void (^)(BOOL success, id<BUYPaymentToken> token))callback
{ {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self.client storeCreditCard:[self creditCard] checkout:self.checkout completion:^(BUYCheckout *checkout, NSString *paymentSessionId, NSError *error) { [self.client storeCreditCard:[self creditCard] checkout:self.checkout completion:^(BUYCheckout *checkout, id<BUYPaymentToken> token, NSError *error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
...@@ -156,7 +154,7 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification"; ...@@ -156,7 +154,7 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
NSLog(@"Error applying credit card: %@", error); NSLog(@"Error applying credit card: %@", error);
} }
callback(error == nil && checkout); callback(error == nil && checkout, token);
}]; }];
} }
...@@ -212,14 +210,14 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification"; ...@@ -212,14 +210,14 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
__weak CheckoutViewController *welf = self; __weak CheckoutViewController *welf = self;
// First, the credit card must be stored on the checkout // First, the credit card must be stored on the checkout
[self addCreditCardToCheckout:^(BOOL success) { [self addCreditCardToCheckout:^(BOOL success, id<BUYPaymentToken> token) {
if (success) { if (success) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
// Upon successfully adding the credit card to the checkout, complete checkout must be called immediately // Upon successfully adding the credit card to the checkout, complete checkout must be called immediately
[welf.client completeCheckout:welf.checkout completion:^(BUYCheckout *checkout, NSError *error) { [welf.client completeCheckout:welf.checkout paymentToken:token completion:^(BUYCheckout *checkout, NSError *error) {
if (error == nil && checkout) { if (error == nil && checkout) {
...@@ -292,7 +290,7 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification"; ...@@ -292,7 +290,7 @@ NSString * const CheckoutCallbackNotification = @"CheckoutCallbackNotification";
{ {
PKPaymentRequest *paymentRequest = [[PKPaymentRequest alloc] init]; PKPaymentRequest *paymentRequest = [[PKPaymentRequest alloc] init];
[paymentRequest setMerchantIdentifier:MERCHANT_ID]; [paymentRequest setMerchantIdentifier:MerchantId];
[paymentRequest setRequiredBillingAddressFields:PKAddressFieldAll]; [paymentRequest setRequiredBillingAddressFields:PKAddressFieldAll];
[paymentRequest setRequiredShippingAddressFields:self.checkout.requiresShipping ? PKAddressFieldAll : PKAddressFieldEmail|PKAddressFieldPhone]; [paymentRequest setRequiredShippingAddressFields:self.checkout.requiresShipping ? PKAddressFieldAll : PKAddressFieldEmail|PKAddressFieldPhone];
[paymentRequest setSupportedNetworks:@[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard]]; [paymentRequest setSupportedNetworks:@[PKPaymentNetworkVisa, PKPaymentNetworkMasterCard]];
......
//
// BUYPaymentButton.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
typedef NS_ENUM(NSInteger, BUYPaymentButtonStyle) {
BUYPaymentButtonStyleWhite = 0,
BUYPaymentButtonStyleWhiteOutline,
BUYPaymentButtonStyleBlack
};
typedef NS_ENUM(NSInteger, BUYPaymentButtonType) {
BUYPaymentButtonTypePlain = 0,
BUYPaymentButtonTypeBuy,
BUYPaymentButtonTypeSetup NS_ENUM_AVAILABLE_IOS(9_0)
};
@interface BUYPaymentButton : UIButton
+ (instancetype)buttonWithType:(BUYPaymentButtonType)buttonType style:(BUYPaymentButtonStyle)buttonStyle;
@end
//
// ImageKit.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import Foundation;
@import UIKit;
/**
* Image generator for a variety of images using the BUYProductViewController.
*/
@interface ImageKit : NSObject
/**
* Generates a close button image for the variant selection navigation bar.
*
* @param frame The frame size of the image
*
* @return A close button image
*/
+ (UIImage*)imageOfVariantCloseImageWithFrame: (CGRect)frame;
/**
* Generates a checkmark image for use of displaying the previously selected variant
*
* @param frame The frame size of the image
*
* @return A checkmark for the previous variant selection
*/
+ (UIImage*)imageOfPreviousSelectionIndicatorImageWithFrame: (CGRect)frame;
/**
* Generates a custom disclosure indicator image
*
* @param frame The frame size of the image
* @param color The color for the disclosure indicator
*
* @return A disclusore indicator image
*/
+ (UIImage*)imageOfDisclosureIndicatorImageWithFrame: (CGRect)frame color:(UIColor*)color;
/**
* Generates a close button image for the product view's navigation bar
*
* @param frame The frame size of the image
* @param color The color for the close button image
* @param hasShadow True if the X should have a drop shadow
*
* @return A close button image
*/
+ (UIImage*)imageOfProductViewCloseImageWithFrame: (CGRect)frame color:(UIColor*)color hasShadow:(BOOL)hasShadow;
/**
* Generates a custom back button image for the variant selection navigation bar
*
* @param frame The frame size of the image
*
* @return A custom back button image
*/
+ (UIImage*)imageOfVariantBackImageWithFrame: (CGRect)frame;
@end
//
// UIButton+PaymentButton.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import UIKit;
typedef NS_ENUM(NSInteger, PaymentButtonStyle) {
PaymentButtonStyleWhite = 0,
PaymentButtonStyleWhiteOutline,
PaymentButtonStyleBlack
};
typedef NS_ENUM(NSInteger, PaymentButtonType) {
PaymentButtonTypePlain = 0,
PaymentButtonTypeBuy,
PaymentButtonTypeSetup NS_ENUM_AVAILABLE_IOS(9_0)
};
@interface UIButton (PaymentButton)
+ (UIButton *)paymentButtonWithType:(PaymentButtonType)buttonType style:(PaymentButtonStyle)buttonStyle;
@end
//
// UIButton+PaymentButton.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import PassKit;
#import "CheckoutButton.h"
#import "UIImage+BUYAdditions.h"
#import "UIImage+PaymentButton.h"
#import "UIButton+PaymentButton.h"
@implementation UIButton (PaymentButton)
+ (UIButton *)paymentButtonWithType:(PaymentButtonType)buttonType style:(PaymentButtonStyle)buttonStyle
{
if ([PKPaymentButton class]) {
return [PKPaymentButton buttonWithType:(PKPaymentButtonType)buttonType style:(PKPaymentButtonStyle)buttonStyle];
}
UIButton *button = [CheckoutButton new];
UIColor *fillColor;
UIColor *strokeColor;
switch (buttonStyle) {
case PaymentButtonStyleBlack:
fillColor = [UIColor blackColor];
break;
case PaymentButtonStyleWhite:
fillColor = [UIColor whiteColor];
break;
case PaymentButtonStyleWhiteOutline:
fillColor = [UIColor whiteColor];
strokeColor = [UIColor blackColor];
break;
}
UIImage *backgroundImage = [[UIImage templateImageWithFill:fillColor stroke:strokeColor edgeInsets:UIEdgeInsetsMake(4.0f, 4.0f, 4.0f, 4.0f)] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[button setBackgroundImage:backgroundImage forState:UIControlStateNormal];
UIImage *image = [UIImage paymentButtonImageWithFrame:CGRectMake(0, 0, 120.0f, 44.0f) style:buttonStyle type:buttonType];
[button setImage:image forState:UIControlStateNormal];
return button;
}
@end
//
// UIImage+PaymentButton.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import <UIKit/UIKit.h>
#import "UIButton+PaymentButton.h"
@interface UIImage (PaymentButton)
+ (UIImage *)paymentButtonImageWithFrame:(CGRect)originalFrame style:(PaymentButtonStyle)style type:(PaymentButtonType)type;
@end
...@@ -53,7 +53,7 @@ typedef NS_ENUM(NSInteger, UITableViewDiscountGiftCardSection) { ...@@ -53,7 +53,7 @@ typedef NS_ENUM(NSInteger, UITableViewDiscountGiftCardSection) {
@implementation PreCheckoutViewController @implementation PreCheckoutViewController
- (instancetype)initWithClient:(BUYClient *)client checkout:(BUYCheckout *)checkout; - (instancetype)initWithClient:(BUYClient *)client checkout:(BUYCheckout *)checkout
{ {
NSParameterAssert(client); NSParameterAssert(client);
NSParameterAssert(checkout); NSParameterAssert(checkout);
...@@ -68,7 +68,8 @@ typedef NS_ENUM(NSInteger, UITableViewDiscountGiftCardSection) { ...@@ -68,7 +68,8 @@ typedef NS_ENUM(NSInteger, UITableViewDiscountGiftCardSection) {
return self; return self;
} }
- (void)viewDidLoad { - (void)viewDidLoad
{
[super viewDidLoad]; [super viewDidLoad];
self.title = @"Add Discount or Gift Card(s)"; self.title = @"Add Discount or Gift Card(s)";
...@@ -192,7 +193,7 @@ typedef NS_ENUM(NSInteger, UITableViewDiscountGiftCardSection) { ...@@ -192,7 +193,7 @@ typedef NS_ENUM(NSInteger, UITableViewDiscountGiftCardSection) {
style:UIAlertActionStyleDefault style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) { handler:^(UIAlertAction *action) {
BUYDiscount *discount = [[BUYDiscount alloc] initWithCode:[alertController.textFields[0] text]]; BUYDiscount *discount = [self.client.modelManager discountWithCode:[alertController.textFields[0] text]];
self.checkout.discount = discount; self.checkout.discount = discount;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
......
//
// ActionableFooterView.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import UIKit;
#import "CheckoutButton.h"
#import "UIButton+PaymentButton.h"
@interface ActionableFooterView : UIView
- (void)setApplePayAvailable:(BOOL)applePayAvailable requiresSetup:(BOOL)requiresSetup;
@property (nonatomic, readonly) CheckoutButton *actionButton;
/**
* On iOS 8.3+ this will be a PKPaymentButton
*/
@property (nonatomic, readonly) UIButton *paymentButton;
@property (nonatomic) BOOL buttonsEnabled;
/**
* A view which sits above the buttons.
* Can be used to add a description or other content to the footer.
*/
@property (nonatomic, readonly) UIView *extensionView;
@property (nonatomic) PaymentButtonStyle paymentButtonStyle UI_APPEARANCE_SELECTOR;
@property (nonatomic) UIColor *separatorColor UI_APPEARANCE_SELECTOR;
@end
//
// ActionableFooterView.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "ActionableFooterView.h"
@interface ActionableFooterView()
@property (nonatomic) BOOL applePayAvailable;
@property (nonatomic) BOOL applePayRequiresSetup;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *applePayLeadingConstraint;
@property (strong, nonatomic) IBOutlet NSLayoutConstraint *firstButtonTrailingConstraint;
@property (strong, nonatomic) IBOutlet CheckoutButton *actionButton;
@property (strong, nonatomic) IBOutlet UIView *paymentButtonContainer;
@property (strong, nonatomic) IBOutlet UIView *extensionView;
@property (strong, nonatomic) UIButton *paymentButton;
@end
@implementation ActionableFooterView
- (instancetype)init
{
return [[NSBundle bundleForClass:self.class] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil].firstObject;
}
- (void)awakeFromNib
{
[super awakeFromNib];
self.actionButton.backgroundColor = nil;
[self.actionButton setTitle:nil forState:UIControlStateNormal];
self.paymentButtonStyle = PaymentButtonStyleBlack;
self.paymentButtonContainer.backgroundColor = nil;
}
- (void)setApplePayAvailable:(BOOL)applePayAvailable requiresSetup:(BOOL)requiresSetup
{
_applePayAvailable = applePayAvailable;
_applePayRequiresSetup = requiresSetup;
self.paymentButtonContainer.hidden = !applePayAvailable;
PaymentButtonType type = self.applePayRequiresSetup ? PaymentButtonTypeSetup : PaymentButtonTypeBuy;
if (!self.paymentButton) {
_paymentButton = [UIButton paymentButtonWithType:type style:self.paymentButtonStyle];
[self.paymentButtonContainer addSubview:self.paymentButton];
self.paymentButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.paymentButtonContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_paymentButton]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(_paymentButton)]];
[self.paymentButtonContainer addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_paymentButton]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(_paymentButton)]];
}
[self setNeedsUpdateConstraints];
}
- (void)setLayoutMargins:(UIEdgeInsets)layoutMargins
{
[super setLayoutMargins:layoutMargins];
self.applePayLeadingConstraint.constant = self.layoutMargins.left;
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self setNeedsUpdateConstraints];
}
- (void)setButtonsEnabled:(BOOL)buttonsEnabled
{
self.actionButton.enabled = buttonsEnabled;
self.paymentButton.enabled = buttonsEnabled;
self.paymentButton.alpha = buttonsEnabled ? 1.0f : 0.5f;
}
- (void)updateConstraints
{
if (self.applePayAvailable) {
CGFloat trailingConstant = (CGRectGetWidth(self.bounds) - self.layoutMargins.right) * 0.5f;
self.firstButtonTrailingConstraint.constant = trailingConstant;
} else {
self.firstButtonTrailingConstraint.constant = 0.0f;
}
[super updateConstraints];
}
- (void)setSeparatorColor:(UIColor *)separatorColor
{
self.layer.shadowColor = separatorColor.CGColor;
self.layer.shadowOffset = CGSizeMake(0, - 1 / [UIScreen mainScreen].scale);
self.layer.shadowRadius = 0.0f;
self.layer.shadowOpacity = 1.0f;
}
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ActionableFooterView">
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2he-LW-1Mz">
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" id="tOf-FE-2kG">
<rect key="frame" x="0.0" y="0.0" width="320" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jjm-5z-kCD" userLabel="paymentContainer">
<rect key="frame" x="165" y="46" width="145" height="44"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Eoe-n5-0iT" customClass="CheckoutButton">
<rect key="frame" x="10" y="46" width="145" height="44"/>
<color key="backgroundColor" red="0.00059560901718214154" green="0.70855957269668579" blue="0.99971479177474976" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="44" id="Yod-FN-PBJ"/>
</constraints>
<state key="normal" title="Add to Cart">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QbM-Nc-TSH">
<rect key="frame" x="0.0" y="10" width="320" height="36"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="Eoe-n5-0iT" secondAttribute="trailing" constant="155" id="4jE-2h-gr6"/>
<constraint firstItem="Eoe-n5-0iT" firstAttribute="leading" secondItem="tOf-FE-2kG" secondAttribute="leadingMargin" id="ACp-Cb-o3A"/>
<constraint firstItem="jjm-5z-kCD" firstAttribute="height" secondItem="Eoe-n5-0iT" secondAttribute="height" id="DoE-w1-naR"/>
<constraint firstItem="QbM-Nc-TSH" firstAttribute="leading" secondItem="tOf-FE-2kG" secondAttribute="leading" id="Q7m-41-SnI"/>
<constraint firstItem="QbM-Nc-TSH" firstAttribute="top" secondItem="tOf-FE-2kG" secondAttribute="topMargin" id="Tbz-Z9-Xyu"/>
<constraint firstItem="jjm-5z-kCD" firstAttribute="leading" secondItem="Eoe-n5-0iT" secondAttribute="trailing" constant="10" id="UrY-kJ-C6C"/>
<constraint firstAttribute="bottomMargin" secondItem="Eoe-n5-0iT" secondAttribute="bottom" id="WHF-0k-ScU"/>
<constraint firstItem="jjm-5z-kCD" firstAttribute="width" secondItem="Eoe-n5-0iT" secondAttribute="width" id="fop-kv-C9D"/>
<constraint firstItem="jjm-5z-kCD" firstAttribute="centerY" secondItem="Eoe-n5-0iT" secondAttribute="centerY" id="g86-Vu-ZpD"/>
<constraint firstItem="Eoe-n5-0iT" firstAttribute="top" secondItem="QbM-Nc-TSH" secondAttribute="bottom" id="pwX-zC-1fR"/>
<constraint firstAttribute="trailing" secondItem="QbM-Nc-TSH" secondAttribute="trailing" id="yYy-IX-gUN"/>
</constraints>
</view>
<blurEffect style="extraLight"/>
</visualEffectView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="2he-LW-1Mz" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="9VD-ID-fkE"/>
<constraint firstItem="2he-LW-1Mz" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="EFC-N3-UwK"/>
<constraint firstItem="2he-LW-1Mz" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="nzn-ri-Fq0"/>
<constraint firstItem="2he-LW-1Mz" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="xUR-8Z-blk"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<edgeInsets key="layoutMargins" top="10" left="10" bottom="10" right="10"/>
<connections>
<outlet property="actionButton" destination="Eoe-n5-0iT" id="slV-do-iC5"/>
<outlet property="applePayLeadingConstraint" destination="UrY-kJ-C6C" id="CBg-TA-i3I"/>
<outlet property="extensionView" destination="QbM-Nc-TSH" id="1rQ-Kd-AvH"/>
<outlet property="firstButtonTrailingConstraint" destination="4jE-2h-gr6" id="kTF-KV-LGT"/>
<outlet property="paymentButtonContainer" destination="jjm-5z-kCD" id="tgu-5h-QO3"/>
</connections>
<point key="canvasLocation" x="142" y="525"/>
</view>
</objects>
</document>
// //
// BUYImageView.h // AsyncImageView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,14 +25,14 @@ ...@@ -25,14 +25,14 @@
// //
@import UIKit; @import UIKit;
#import "BUYTheme.h" @import Buy;
extern float const imageDuration; extern float const imageDuration;
/** /**
* Provides an easy way to asynchronously load images from a URL * Provides an easy way to asynchronously load images from a URL
*/ */
@interface BUYImageView : UIImageView <BUYThemeable> @interface AsyncImageView : UIImageView
@property (nonatomic, assign) BOOL showsActivityIndicator; @property (nonatomic, assign) BOOL showsActivityIndicator;
...@@ -65,4 +65,13 @@ extern float const imageDuration; ...@@ -65,4 +65,13 @@ extern float const imageDuration;
*/ */
- (BOOL)isPortraitOrSquare; - (BOOL)isPortraitOrSquare;
/**
* Load an image asynchronously from an BUYImageLink and animate the transition. Will use the optimal image size for the ImageView
*
* @param imageLink the BUYImageLink that points to the image asset to use
* @param animateChange YES to animation the image transition
* @param completion Completion block returning the image or an error
*/
- (void)loadImageLink:(BUYImageLink *)imageLink animateChange:(BOOL)animateChange completion:(void (^)(UIImage *image, NSError *error))completion;
@end @end
// //
// BUYImageView.m // AsyncImageView.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,19 +24,47 @@ ...@@ -24,19 +24,47 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYImageView.h" #import "AsyncImageView.h"
#import "BUYTheme+Additions.h"
float const imageDuration = 0.1f; float const imageDuration = 0.1f;
@interface BUYImageView () static NSUInteger const memoryCacheSize = 10 * 1024 * 1024; // 10 MB
static NSUInteger const diskCacheSize = 50 * 1024 * 1024; // 50 MB
@interface NSURLSession (BUYImageSession)
+ (instancetype)buy_sharedImageSession;
@end
@implementation NSURLSession (BUYImageSession)
+ (instancetype)buy_sharedImageSession
{
static NSURLSession *_sharedSession = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:memoryCacheSize diskCapacity:diskCacheSize diskPath:nil];
_sharedSession = [NSURLSession sessionWithConfiguration:config];
});
return _sharedSession;
}
@end
@interface AsyncImageView ()
@property (nonatomic, strong) NSURLSessionDataTask *task; @property (nonatomic, strong) NSURLSessionDataTask *task;
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView; @property (nonatomic, strong) UIActivityIndicatorView *activityIndicatorView;
@end @end
@implementation BUYImageView @implementation AsyncImageView
- (instancetype)init - (instancetype)init
{ {
...@@ -104,7 +132,7 @@ float const imageDuration = 0.1f; ...@@ -104,7 +132,7 @@ float const imageDuration = 0.1f;
if (self.showsActivityIndicator) { if (self.showsActivityIndicator) {
[self.activityIndicatorView startAnimating]; [self.activityIndicatorView startAnimating];
} }
self.task = [[NSURLSession sharedSession] dataTaskWithURL:imageURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { self.task = [[NSURLSession buy_sharedImageSession] dataTaskWithURL:imageURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:data]; UIImage *image = [UIImage imageWithData:data];
if (setImage) { if (setImage) {
...@@ -119,19 +147,23 @@ float const imageDuration = 0.1f; ...@@ -119,19 +147,23 @@ float const imageDuration = 0.1f;
[self.task resume]; [self.task resume];
} }
- (void)cancelImageTask { - (void)cancelImageTask
{
[self.task cancel]; [self.task cancel];
self.task = nil; self.task = nil;
} }
- (void)setTheme:(BUYTheme *)theme - (BOOL)isPortraitOrSquare
{ {
self.activityIndicatorView.activityIndicatorViewStyle = [theme activityIndicatorViewStyle]; return self.image.size.height >= self.image.size.width;
} }
- (BOOL)isPortraitOrSquare - (void)loadImageLink:(BUYImageLink *)imageLink animateChange:(BOOL)animateChange completion:(void (^)(UIImage *image, NSError *error))completion;
{ {
return self.image.size.height >= self.image.size.width; BUYImageURLSize size = [self buy_imageSize];
NSURL *url = [imageLink imageURLWithSize:size];
[self loadImageWithURL:url animateChange:animateChange completion:completion];
} }
@end @end
// //
// BUYProductViewFooter.h // CheckoutButton.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,49 +25,31 @@ ...@@ -25,49 +25,31 @@
// //
@import UIKit; @import UIKit;
@class BUYProductVariant;
@class BUYTheme;
#import "BUYPaymentButton.h"
#import "BUYCheckoutButton.h"
@interface BUYProductViewFooter : UIView
/** /**
* A footer view for the BUYProductView that includes a checkout button and an Apple Pay button (if available) * A themed UIButton that includes a UIActivityIndicator
*
* @param theme The theme for the footer view
* @param showApplePaySetup Show Apple Pay button with 'Set Up Apple Pay' text as determined by the presenter
*
* @return A footer view with a checkout and Apple Pay button
*/ */
- (instancetype)initWithTheme:(BUYTheme *)theme showApplePaySetup:(BOOL)showApplePaySetup; @interface CheckoutButton : UIButton
/** /**
* A checkout button themed with the BUYTheme tintColor. * Creates a checkout button with UIButtonTypeSystem which inherits its color from the superview's tintColor
* This button is used for web checkout *
*/ * @return A CheckoutButton with UIButtonTypeSystem
@property (nonatomic, strong) BUYCheckoutButton *checkoutButton;
/**
* An Apple Pay button, shown if Apple Pay is enabled and available on the device.
* The BUYPaymentButton is a PKPaymentButton on iOS 8.3 and greater.
* Styled to match the given theme.
*/ */
@property (nonatomic, strong) BUYPaymentButton *buyPaymentButton; +(instancetype)checkoutButton;
/** /**
* Sets the Apple Pay visible (or hides it). This updates the layout for the buttons. * Show a UIActivityIndicator in place of text while loading
* *
* @param isApplePayAvailable True when the Apple Pay button is visible * @param show Show or hide the UIActivityIndicator
*/ */
- (void)setApplePayButtonVisible:(BOOL)isApplePayAvailable; - (void)showActivityIndicator:(BOOL)show;
/** /**
* Lowers the alpha and disable the buttons if the `available` property on the * Sets the button text color
* BUYProductVariant is false.
* *
* @param productVariant The selected product variant that determines availability and functionality of the buttons. * @param color The color for the button
*/ */
- (void)updateButtonsForProductVariant:(BUYProductVariant *)productVariant; - (void)setTextColor:(UIColor*)color UI_APPEARANCE_SELECTOR;
@end @end
// //
// BUYCheckoutButton.m // CheckoutButton.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,39 +24,61 @@ ...@@ -24,39 +24,61 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYCheckoutButton.h" #import "CheckoutButton.h"
#import "BUYTheme+Additions.h" #import "UIColor+BUYAdditions.h"
#import "UIImage+BUYAdditions.h"
@interface BUYCheckoutButton () @interface CheckoutButton ()
@property (nonatomic, weak) BUYTheme *theme;
@property (nonatomic, strong) UIActivityIndicatorView *activityIndicator; @property (nonatomic, strong) UIActivityIndicatorView *activityIndicator;
@property (nonatomic, strong) UIColor *textColor;
@end @end
@implementation BUYCheckoutButton @implementation CheckoutButton
- (void)setHighlighted:(BOOL)highlighted + (instancetype)checkoutButton
{ {
[super setHighlighted:highlighted]; CheckoutButton *checkoutButton = [super buttonWithType:UIButtonTypeSystem];
self.backgroundColor = highlighted ? [self.theme.tintColor colorWithAlphaComponent:0.4f] : self.theme.tintColor; [checkoutButton commonInit];
return checkoutButton;
} }
- (void)setTheme:(BUYTheme *)theme - (instancetype)initWithFrame:(CGRect)frame
{ {
_theme = theme; self = [super initWithFrame:frame];
self.tintColor = theme.tintColor; if (self) {
self.backgroundColor = self.tintColor; [self commonInit];
UIColor *textColor = [theme checkoutButtonTextColor]; }
[self setTitleColor:textColor forState:UIControlStateNormal]; return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit
{
[self setBackgroundImage:[UIImage templateButtonBackgroundImage] forState:UIControlStateNormal];
[self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[self setTitleColor:[[UIColor whiteColor] colorWithAlphaComponent:0.5f] forState:UIControlStateDisabled];
}
- (void)setTextColor:(UIColor*)color
{
_textColor = color;
[self setTitleColor:color forState:UIControlStateNormal];
} }
- (UIActivityIndicatorView *)activityIndicator - (UIActivityIndicatorView *)activityIndicator
{ {
if (_activityIndicator == nil) { if (_activityIndicator == nil) {
_activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
UIActivityIndicatorViewStyle style = self.theme.style == BUYThemeStyleLight ? UIActivityIndicatorViewStyleWhite : UIActivityIndicatorViewStyleGray;
_activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:style];
_activityIndicator.translatesAutoresizingMaskIntoConstraints = NO; _activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
_activityIndicator.hidesWhenStopped = YES; _activityIndicator.hidesWhenStopped = YES;
[self addSubview:_activityIndicator]; [self addSubview:_activityIndicator];
...@@ -77,7 +99,7 @@ ...@@ -77,7 +99,7 @@
} }
else { else {
[self.activityIndicator stopAnimating]; [self.activityIndicator stopAnimating];
[self setTheme:self.theme]; [self setTitleColor:self.textColor forState:UIControlStateNormal];
} }
} }
......
//
// DisclosureIndicatorView.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import UIKit;
@interface DisclosureIndicatorView : UIImageView
@end
// //
// BUYTheme.m // DisclosureIndicatorView.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,20 +24,17 @@ ...@@ -24,20 +24,17 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYTheme.h" #import "DisclosureIndicatorView.h"
#import "ImageKit.h"
@implementation BUYTheme @implementation DisclosureIndicatorView
- (instancetype)init - (instancetype)init
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
self.tintColor = [UIColor colorWithRed:0.48f green:0.71f blue:0.36f alpha:1.0f]; self.image = [[ImageKit imageOfDisclosureIndicatorImageWithFrame:CGRectMake(0, 0, 10, 16) color:[UIColor blackColor]] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
self.style = BUYThemeStyleLight;
self.showsProductImageBackground = YES;
} }
return self; return self;
} }
......
// //
// BUYProductViewErrorView.h // ErrorView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,12 +25,11 @@ ...@@ -25,12 +25,11 @@
// //
@import UIKit; @import UIKit;
@class BUYTheme;
/** /**
* A customer toast error view to use in the BUYProductView. * A customer toast error view to use in the ProductView.
*/ */
@interface BUYProductViewErrorView : UIView @interface ErrorView : UIView
/** /**
* The label containing the error message. * The label containing the error message.
...@@ -47,13 +46,8 @@ ...@@ -47,13 +46,8 @@
*/ */
@property (nonatomic, strong) NSLayoutConstraint *visibleConstraint; @property (nonatomic, strong) NSLayoutConstraint *visibleConstraint;
/** - (void)setOverlayColor:(UIColor*)color UI_APPEARANCE_SELECTOR;
* Initializer that create an error view with a theme.
* - (void)presentErrorViewWithMessage:(NSString *)errorMessage completion:(void (^)(void))callback;
* @param theme The theme for the error view.
*
* @return An error view.
*/
- (instancetype)initWithTheme:(BUYTheme*)theme;
@end @end
// //
// BUYProductViewErrorView.m // ErrorView.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,18 +24,24 @@ ...@@ -24,18 +24,24 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductViewErrorView.h" #import "ErrorView.h"
#import "BUYTheme.h"
#import "BUYTheme+Additions.h"
@implementation BUYProductViewErrorView static CGFloat const kPaddingMedium = 12.0f;
static CGFloat const kPaddingExtraLarge = 16.0f;
- (instancetype)initWithTheme:(BUYTheme*)theme @interface ErrorView ()
@property (nonatomic, strong) UIView *overlayView;
@end
@implementation ErrorView
- (instancetype)init
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]; UIVisualEffectView *visualEffectView = [[UIVisualEffectView alloc] init];
UIVisualEffectView *visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
visualEffectView.translatesAutoresizingMaskIntoConstraints = NO; visualEffectView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:visualEffectView]; [self addSubview:visualEffectView];
...@@ -48,29 +54,28 @@ ...@@ -48,29 +54,28 @@
metrics:nil metrics:nil
views:NSDictionaryOfVariableBindings(visualEffectView)]]; views:NSDictionaryOfVariableBindings(visualEffectView)]];
UIView *redTintOverlayView = [[UIView alloc] init]; _overlayView = [[UIView alloc] init];
redTintOverlayView.translatesAutoresizingMaskIntoConstraints = NO; _overlayView.translatesAutoresizingMaskIntoConstraints = NO;
redTintOverlayView.backgroundColor = [theme errorTintOverlayColor]; [visualEffectView.contentView addSubview:_overlayView];
[visualEffectView.contentView addSubview:redTintOverlayView]; [visualEffectView.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_overlayView]|"
[visualEffectView.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[redTintOverlayView]|"
options:0 options:0
metrics:nil metrics:nil
views:NSDictionaryOfVariableBindings(redTintOverlayView)]]; views:NSDictionaryOfVariableBindings(_overlayView)]];
[visualEffectView.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[redTintOverlayView]|" [visualEffectView.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_overlayView]|"
options:0 options:0
metrics:nil metrics:nil
views:NSDictionaryOfVariableBindings(redTintOverlayView)]]; views:NSDictionaryOfVariableBindings(_overlayView)]];
_errorLabel = [[UILabel alloc] init]; _errorLabel = [[UILabel alloc] init];
_errorLabel.translatesAutoresizingMaskIntoConstraints = NO; _errorLabel.translatesAutoresizingMaskIntoConstraints = NO;
_errorLabel.textAlignment = NSTextAlignmentCenter; _errorLabel.textAlignment = NSTextAlignmentCenter;
_errorLabel.font = [BUYTheme errorLabelFont]; _errorLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
_errorLabel.textColor = [UIColor whiteColor]; _errorLabel.textColor = [UIColor whiteColor];
_errorLabel.backgroundColor = [UIColor clearColor]; _errorLabel.backgroundColor = [UIColor clearColor];
_errorLabel.numberOfLines = 0; _errorLabel.numberOfLines = 0;
[visualEffectView.contentView addSubview:_errorLabel]; [visualEffectView.contentView addSubview:_errorLabel];
NSDictionary *metricsDictionary = @{ @"paddingExtraLarge" : @(kBuyPaddingExtraLarge), @"paddingMedium" : @(kBuyPaddingMedium) }; NSDictionary *metricsDictionary = @{ @"paddingExtraLarge" : @(kPaddingExtraLarge), @"paddingMedium" : @(kPaddingMedium) };
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(paddingExtraLarge)-[_errorLabel]-(paddingExtraLarge)-|" [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(paddingExtraLarge)-[_errorLabel]-(paddingExtraLarge)-|"
options:0 options:0
...@@ -84,4 +89,38 @@ ...@@ -84,4 +89,38 @@
return self; return self;
} }
- (void)setOverlayColor:(UIColor*)color
{
self.overlayView.backgroundColor = color;
}
- (void)presentErrorViewWithMessage:(NSString *)errorMessage completion:(void (^)(void))callback
{
self.errorLabel.text = errorMessage;
[NSLayoutConstraint deactivateConstraints:@[self.hiddenConstraint]];
[NSLayoutConstraint activateConstraints:@[self.visibleConstraint]];
[UIView animateWithDuration:0.3f
delay:0
usingSpringWithDamping:0.8f
initialSpringVelocity:10
options:0
animations:^{
self.alpha = 1;
[self layoutIfNeeded];
}
completion:^(BOOL finished) {
[NSLayoutConstraint deactivateConstraints:@[self.visibleConstraint]];
[NSLayoutConstraint activateConstraints:@[self.hiddenConstraint]];
[UIView animateWithDuration:0.3f delay:3.0f options:0 animations:^{
self.alpha = 0;
[self layoutIfNeeded];
} completion:^(BOOL finished) {
[self removeFromSuperview];
if (callback) callback();
}];
}];
}
@end @end
// //
// BUYGradientView.h // GradientView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
/** /**
* Easily create a view with a vertical gradient with two colors * Easily create a view with a vertical gradient with two colors
*/ */
@interface BUYGradientView : UIView @interface GradientView : UIView
/** /**
* The color of the top part of the gradient * The color of the top part of the gradient
......
// //
// BUYGradientView.m // GradientView.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,9 +24,9 @@ ...@@ -24,9 +24,9 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYGradientView.h" #import "GradientView.h"
@implementation BUYGradientView @implementation GradientView
- (instancetype)init - (instancetype)init
{ {
...@@ -34,14 +34,16 @@ ...@@ -34,14 +34,16 @@
if (self) { if (self) {
self.backgroundColor = [UIColor clearColor]; self.backgroundColor = [UIColor clearColor];
self.topColor = [UIColor colorWithWhite:0 alpha:0.5f]; self.topColor = [UIColor colorWithWhite:0 alpha:0.5f];
self.bottomColor = [UIColor clearColor]; self.bottomColor = [UIColor colorWithWhite:0 alpha:0];
} }
return self; return self;
} }
- (void)drawRect:(CGRect)rect {
- (void)drawRect:(CGRect)rect
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext(); CGContextRef context = UIGraphicsGetCurrentContext();
NSArray *gradientColors = [NSArray arrayWithObjects:(id)self.topColor.CGColor, (id)self.bottomColor.CGColor, nil]; NSArray *gradientColors = [NSArray arrayWithObjects:(id)self.topColor.CGColor, (id)self.bottomColor.CGColor, nil];
CGFloat gradientLocations[] = {0, 1}; CGFloat gradientLocations[] = {0, 1};
......
// //
// BUYProductViewHeaderBackgroundImageView.h // HeaderBackgroundView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,24 +25,14 @@ ...@@ -25,24 +25,14 @@
// //
@import UIKit; @import UIKit;
@class BUYImageView; @class AsyncImageView;
@class BUYTheme;
@class BUYImageLink; @class BUYImageLink;
/** /**
* A background for the product view that displays the currently displayed * A background for the product view that displays the currently displayed
* product or variant image behind a blurry UIVisualEffectView. * product or variant image behind a blurry UIVisualEffectView.
*/ */
@interface BUYProductViewHeaderBackgroundImageView : UIView @interface HeaderBackgroundView : UIView
/**
* Initializer that takes a BUYTheme
*
* @param theme The product view theme
*
* @return An instance of BUYProductViewHeaderBackgroundImageView
*/
- (instancetype)initWithTheme:(BUYTheme*)theme;
/** /**
* Set the product image on the image view * Set the product image on the image view
......
// //
// BUYProductViewHeaderBackgroundImageView.m // HeaderBackgroundView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,25 +24,23 @@ ...@@ -24,25 +24,23 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductViewHeaderBackgroundImageView.h" #import "AsyncImageView.h"
#import "BUYTheme.h" #import "HeaderBackgroundView.h"
#import "BUYImageView.h" #import "VisualEffectView.h"
#import "BUYImageLink.h"
#import "BUYTheme+Additions.h"
@interface BUYProductViewHeaderBackgroundImageView () @interface HeaderBackgroundView ()
@property (nonatomic, strong) BUYImageView *productImageView; @property (nonatomic, strong) AsyncImageView *productImageView;
@end @end
@implementation BUYProductViewHeaderBackgroundImageView @implementation HeaderBackgroundView
- (instancetype)initWithTheme:(BUYTheme*)theme - (instancetype)init
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
self.productImageView = [[BUYImageView alloc] init]; self.productImageView = [[AsyncImageView alloc] init];
self.productImageView.clipsToBounds = YES; self.productImageView.clipsToBounds = YES;
self.productImageView.translatesAutoresizingMaskIntoConstraints = NO; self.productImageView.translatesAutoresizingMaskIntoConstraints = NO;
self.productImageView.backgroundColor = [UIColor clearColor]; self.productImageView.backgroundColor = [UIColor clearColor];
...@@ -64,7 +62,7 @@ ...@@ -64,7 +62,7 @@
multiplier:1.0 multiplier:1.0
constant:0.0]]; constant:0.0]];
UIVisualEffectView *visualEffectView = [[UIVisualEffectView alloc] initWithEffect:[theme blurEffect]]; VisualEffectView *visualEffectView = [[VisualEffectView alloc] init];
visualEffectView.translatesAutoresizingMaskIntoConstraints = NO; visualEffectView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:visualEffectView]; [self addSubview:visualEffectView];
...@@ -88,8 +86,7 @@ ...@@ -88,8 +86,7 @@
- (void)setBackgroundProductImage:(BUYImageLink *)image - (void)setBackgroundProductImage:(BUYImageLink *)image
{ {
NSString *string = [image.src stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@".%@", [image.src pathExtension]] withString:[NSString stringWithFormat:@"_small.%@", [image.src pathExtension]]]; NSURL *url = [image imageURLWithSize:BUYImageURLSize100x100];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@", string]];
[self.productImageView loadImageWithURL:url animateChange:YES completion:NULL]; [self.productImageView loadImageWithURL:url animateChange:YES completion:NULL];
} }
......
// //
// BUYProductViewHeaderOverlay.h // HeaderOverlayView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,23 +25,15 @@ ...@@ -25,23 +25,15 @@
// //
@import UIKit; @import UIKit;
@class BUYTheme;
/** /**
* An overlay + blur effect when the product view scrolls up * An overlay + blur effect when the product view scrolls up
* to hide the image view and create a nice transition into * to hide the image view and create a nice transition into
* the navigation bar. * the navigation bar.
*/ */
@interface BUYProductViewHeaderOverlay : UIView @interface HeaderOverlayView : UIView
/** @property (nonatomic) UIColor *overlayBackgroundColor UI_APPEARANCE_SELECTOR;
* Initializer that takes a BUYTheme
*
* @param theme The product view theme
*
* @return An instance of BUYProductViewHeaderOverlay
*/
- (instancetype)initWithTheme:(BUYTheme*)theme;
/** /**
* Used to determine the current visibility of the effect based on content offset and the navigationbar height. * Used to determine the current visibility of the effect based on content offset and the navigationbar height.
......
// //
// BUYProductViewHeaderOverlay.m // HeaderOverlayView.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,20 +24,19 @@ ...@@ -24,20 +24,19 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductViewHeaderOverlay.h" #import "HeaderOverlayView.h"
#import "BUYTheme.h" #import "VisualEffectView.h"
#import "BUYTheme+Additions.h"
@interface BUYProductViewHeaderOverlay () @interface HeaderOverlayView ()
@property (nonatomic, strong) UIView *overlayView; @property (nonatomic, strong) UIView *overlayView;
@property (nonatomic, strong) UIView *visualEffectContainerView; @property (nonatomic, strong) UIView *visualEffectContainerView;
@end @end
@implementation BUYProductViewHeaderOverlay @implementation HeaderOverlayView
- (instancetype)initWithTheme:(BUYTheme*)theme - (instancetype)init
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
...@@ -51,7 +50,7 @@ ...@@ -51,7 +50,7 @@
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_visualEffectContainerView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_visualEffectContainerView)]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_visualEffectContainerView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_visualEffectContainerView)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_visualEffectContainerView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_visualEffectContainerView)]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_visualEffectContainerView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_visualEffectContainerView)]];
UIVisualEffectView *visualEffectView = [[UIVisualEffectView alloc] initWithEffect:[theme blurEffect]]; VisualEffectView *visualEffectView = [[VisualEffectView alloc] init];
visualEffectView.translatesAutoresizingMaskIntoConstraints = NO; visualEffectView.translatesAutoresizingMaskIntoConstraints = NO;
[_visualEffectContainerView addSubview:visualEffectView]; [_visualEffectContainerView addSubview:visualEffectView];
...@@ -60,7 +59,6 @@ ...@@ -60,7 +59,6 @@
_overlayView = [[UIView alloc] init]; _overlayView = [[UIView alloc] init];
_overlayView.alpha = 0; _overlayView.alpha = 0;
_overlayView.backgroundColor = [theme backgroundColor];
_overlayView.translatesAutoresizingMaskIntoConstraints = NO; _overlayView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_overlayView]; [self addSubview:_overlayView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_overlayView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_overlayView)]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_overlayView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_overlayView)]];
...@@ -69,6 +67,11 @@ ...@@ -69,6 +67,11 @@
return self; return self;
} }
-(void)setOverlayBackgroundColor:(UIColor *)overlayBackgroundColor
{
_overlayView.backgroundColor = overlayBackgroundColor;
}
static CGFloat visualEffectViewThreshold = 200.0f; static CGFloat visualEffectViewThreshold = 200.0f;
static CGFloat overlayViewThreshold = 100.0f; static CGFloat overlayViewThreshold = 100.0f;
......
// //
// BUYProductDescriptionCell.h // ProductDescriptionCell.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,12 +25,11 @@ ...@@ -25,12 +25,11 @@
// //
@import UIKit; @import UIKit;
#import "BUYTheme.h"
/** /**
* Table view cell containing the product description * Table view cell containing the product description
*/ */
@interface BUYProductDescriptionCell : UITableViewCell <BUYThemeable> @interface ProductDescriptionCell : UITableViewCell
/** /**
* Converts the product description from HTML to an attributed string * Converts the product description from HTML to an attributed string
......
// //
// BUYProductDescriptionCell.m // ProductDescriptionCell.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,18 +24,16 @@ ...@@ -24,18 +24,16 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductDescriptionCell.h" #import "ProductDescriptionCell.h"
#import "BUYTheme.h" #import "Theme+Additions.h"
#import "BUYTheme+Additions.h"
@interface BUYProductDescriptionCell () @interface ProductDescriptionCell ()
@property (nonatomic, strong) UIColor *textColor; @property (nonatomic, strong) UIColor *textColor;
@property (nonatomic, strong) UILabel *descriptionLabel; @property (nonatomic, strong) UILabel *descriptionLabel;
@property (nonatomic, strong) BUYTheme *theme;
@end @end
@implementation BUYProductDescriptionCell @implementation ProductDescriptionCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{ {
...@@ -54,7 +52,7 @@ ...@@ -54,7 +52,7 @@
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_descriptionLabel]-|" options:0 metrics:nil views:views]]; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_descriptionLabel]-|" options:0 metrics:nil views:views]];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_descriptionLabel]-|" options:0 metrics:nil views:views]]; [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_descriptionLabel]-|" options:0 metrics:nil views:views]];
_textColor = [BUYTheme descriptionTextColor]; _textColor = [Theme descriptionTextColor];
} }
return self; return self;
...@@ -85,10 +83,9 @@ ...@@ -85,10 +83,9 @@
return self.descriptionLabel.attributedText.string; return self.descriptionLabel.attributedText.string;
} }
- (void)setTheme:(BUYTheme *)theme - (void)setBackgroundColor:(UIColor *)backgroundColor
{ {
_theme = theme; [super setBackgroundColor:backgroundColor];
self.backgroundColor = [theme backgroundColor];
self.descriptionLabel.backgroundColor = self.backgroundColor; self.descriptionLabel.backgroundColor = self.backgroundColor;
} }
......
// //
// BUYProductHeaderCell.h // ProductHeaderCell.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,13 +25,12 @@ ...@@ -25,13 +25,12 @@
// //
@import UIKit; @import UIKit;
#import "BUYTheme.h" @import Buy;
@class BUYProductVariant;
/** /**
* Table view cell containing the product title, price and compare-at/sold out text * Table view cell containing the product title, price and compare-at/sold out text
*/ */
@interface BUYProductHeaderCell : UITableViewCell <BUYThemeable> @interface ProductHeaderCell : UITableViewCell
/** /**
* Sets the title, price and optionally compare-at/sold out texts * Sets the title, price and optionally compare-at/sold out texts
...@@ -41,4 +40,6 @@ ...@@ -41,4 +40,6 @@
*/ */
- (void)setProductVariant:(BUYProductVariant *)productVariant withCurrencyFormatter:(NSNumberFormatter*)currencyFormatter; - (void)setProductVariant:(BUYProductVariant *)productVariant withCurrencyFormatter:(NSNumberFormatter*)currencyFormatter;
- (void)setProductTitleColor:(UIColor*)color UI_APPEARANCE_SELECTOR;
@end @end
// //
// BUYProductHeaderCell.m // ProductHeaderCell.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,23 +24,20 @@ ...@@ -24,23 +24,20 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductHeaderCell.h" #import "ProductHeaderCell.h"
#import "BUYProductVariant.h"
#import "BUYProduct.h"
#import "UIFont+BUYAdditions.h" #import "UIFont+BUYAdditions.h"
#import "BUYTheme+Additions.h" #import "Theme+Additions.h"
@interface BUYProductHeaderCell () @interface ProductHeaderCell ()
@property (nonatomic, strong) UILabel *titleLabel; @property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *priceLabel; @property (nonatomic, strong) UILabel *priceLabel;
@property (nonatomic, strong) UILabel *comparePriceLabel; @property (nonatomic, strong) UILabel *comparePriceLabel;
@property (nonatomic, strong) BUYTheme *theme;
@property (nonatomic, strong) BUYProductVariant *productVariant; @property (nonatomic, strong) BUYProductVariant *productVariant;
@end @end
@implementation BUYProductHeaderCell @implementation ProductHeaderCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{ {
...@@ -52,7 +49,7 @@ ...@@ -52,7 +49,7 @@
_titleLabel = [[UILabel alloc] init]; _titleLabel = [[UILabel alloc] init];
_titleLabel.textColor = [UIColor blackColor]; _titleLabel.textColor = [UIColor blackColor];
_titleLabel.font = [BUYTheme productTitleFont]; _titleLabel.font = [Theme productTitleFont];
_titleLabel.numberOfLines = 0; _titleLabel.numberOfLines = 0;
_titleLabel.translatesAutoresizingMaskIntoConstraints = NO; _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:_titleLabel]; [self.contentView addSubview:_titleLabel];
...@@ -62,7 +59,7 @@ ...@@ -62,7 +59,7 @@
[self.contentView addSubview:priceView]; [self.contentView addSubview:priceView];
_priceLabel = [[UILabel alloc] init]; _priceLabel = [[UILabel alloc] init];
_priceLabel.font = [BUYTheme productPriceFont]; _priceLabel.font = [Theme productPriceFont];
_priceLabel.translatesAutoresizingMaskIntoConstraints = NO; _priceLabel.translatesAutoresizingMaskIntoConstraints = NO;
_priceLabel.textAlignment = NSTextAlignmentRight; _priceLabel.textAlignment = NSTextAlignmentRight;
[_priceLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; [_priceLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
...@@ -70,9 +67,9 @@ ...@@ -70,9 +67,9 @@
[priceView addSubview:_priceLabel]; [priceView addSubview:_priceLabel];
_comparePriceLabel = [[UILabel alloc] init]; _comparePriceLabel = [[UILabel alloc] init];
_comparePriceLabel.textColor = [BUYTheme comparePriceTextColor]; _comparePriceLabel.textColor = [Theme comparePriceTextColor];
_comparePriceLabel.textAlignment = NSTextAlignmentRight; _comparePriceLabel.textAlignment = NSTextAlignmentRight;
_comparePriceLabel.font = [BUYTheme productComparePriceFont]; _comparePriceLabel.font = [Theme productComparePriceFont];
_comparePriceLabel.translatesAutoresizingMaskIntoConstraints = NO; _comparePriceLabel.translatesAutoresizingMaskIntoConstraints = NO;
[_comparePriceLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; [_comparePriceLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[_comparePriceLabel setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; [_comparePriceLabel setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
...@@ -105,14 +102,14 @@ ...@@ -105,14 +102,14 @@
self.priceLabel.text = [currencyFormatter stringFromNumber:productVariant.price]; self.priceLabel.text = [currencyFormatter stringFromNumber:productVariant.price];
} }
if (productVariant.available.boolValue && productVariant.compareAtPrice) { if (productVariant.availableValue == YES && productVariant.compareAtPrice) {
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[currencyFormatter stringFromNumber:productVariant.compareAtPrice] NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:[currencyFormatter stringFromNumber:productVariant.compareAtPrice]
attributes:@{NSStrikethroughStyleAttributeName: @(NSUnderlineStyleSingle)}]; attributes:@{NSStrikethroughStyleAttributeName: @(NSUnderlineStyleSingle)}];
self.comparePriceLabel.attributedText = attributedString; self.comparePriceLabel.attributedText = attributedString;
self.comparePriceLabel.textColor = [BUYTheme comparePriceTextColor]; self.comparePriceLabel.textColor = [Theme comparePriceTextColor];
} else if (productVariant.available == NO) { } else if (productVariant.available == NO) {
self.comparePriceLabel.text = @"Sold Out"; self.comparePriceLabel.text = NSLocalizedString(@"Sold Out", @"Sold out text displayed on product view");
self.comparePriceLabel.textColor = [BUYTheme variantSoldOutTextColor]; self.comparePriceLabel.textColor = [Theme variantSoldOutTextColor];
} else { } else {
self.comparePriceLabel.attributedText = nil; self.comparePriceLabel.attributedText = nil;
} }
...@@ -121,12 +118,10 @@ ...@@ -121,12 +118,10 @@
[self layoutIfNeeded]; [self layoutIfNeeded];
} }
- (void)setTheme:(BUYTheme *)theme - (void)setBackgroundColor:(UIColor *)backgroundColor
{ {
_theme = theme; [super setBackgroundColor:backgroundColor];
self.backgroundColor = [theme backgroundColor];
self.titleLabel.backgroundColor = self.priceLabel.backgroundColor = self.comparePriceLabel.backgroundColor = self.backgroundColor; self.titleLabel.backgroundColor = self.priceLabel.backgroundColor = self.comparePriceLabel.backgroundColor = self.backgroundColor;
self.titleLabel.textColor = [theme productTitleColor];
} }
- (void)tintColorDidChange - (void)tintColorDidChange
...@@ -135,4 +130,9 @@ ...@@ -135,4 +130,9 @@
self.priceLabel.textColor = self.tintColor; self.priceLabel.textColor = self.tintColor;
} }
- (void)setProductTitleColor:(UIColor*)color
{
self.titleLabel.textColor = color;
}
@end @end
// //
// BUYProductImageCollectionViewCell.h // ProductImageCell.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,17 +25,17 @@ ...@@ -25,17 +25,17 @@
// //
@import UIKit; @import UIKit;
@class BUYImageView; @class AsyncImageView;
/** /**
* A custom collection view cell for the BUYProductViewController's product image(s) * A custom collection view cell for the BUYProductViewController's product image(s)
*/ */
@interface BUYProductImageCollectionViewCell : UICollectionViewCell @interface ProductImageCell : UICollectionViewCell
/** /**
* The image view containing a product image * The image view containing a product image
*/ */
@property (nonatomic, strong) BUYImageView *productImageView; @property (nonatomic, strong) AsyncImageView *productImageView;
/** /**
* The height to display the product image * The height to display the product image
......
// //
// BUYProductImageCollectionViewCell.m // ProductImageCell.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,10 +24,10 @@ ...@@ -24,10 +24,10 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductImageCollectionViewCell.h" #import "ProductImageCell.h"
#import "BUYImageView.h" #import "AsyncImageView.h"
@implementation BUYProductImageCollectionViewCell @implementation ProductImageCell
- (instancetype)initWithFrame:(CGRect)frame - (instancetype)initWithFrame:(CGRect)frame
{ {
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
self.contentView.backgroundColor = [UIColor clearColor]; self.contentView.backgroundColor = [UIColor clearColor];
_productImageView = [[BUYImageView alloc] init]; _productImageView = [[AsyncImageView alloc] init];
_productImageView.clipsToBounds = YES; _productImageView.clipsToBounds = YES;
_productImageView.translatesAutoresizingMaskIntoConstraints = NO; _productImageView.translatesAutoresizingMaskIntoConstraints = NO;
_productImageView.backgroundColor = [UIColor clearColor]; _productImageView.backgroundColor = [UIColor clearColor];
......
// //
// BUYProductVariantCell.h // ProductVariantCell.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,13 +25,12 @@ ...@@ -25,13 +25,12 @@
// //
@import UIKit; @import UIKit;
#import "BUYTheme.h" @import Buy;
@class BUYProductVariant;
/** /**
* Table view cell containing the product's variant options * Table view cell containing the product's variant options
*/ */
@interface BUYProductVariantCell : UITableViewCell <BUYThemeable> @interface ProductVariantCell : UITableViewCell
/** /**
* Set the options for a product variant * Set the options for a product variant
...@@ -40,4 +39,6 @@ ...@@ -40,4 +39,6 @@
*/ */
- (void)setOptionsForProductVariant:(BUYProductVariant *)productVariant; - (void)setOptionsForProductVariant:(BUYProductVariant *)productVariant;
- (void)setSelectedBackgroundViewBackgroundColor:(UIColor *)selectedBackgroundViewBackgroundColor UI_APPEARANCE_SELECTOR;
@end @end
// //
// BUYProductVariantCell.m // ProductVariantCell.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,23 +24,24 @@ ...@@ -24,23 +24,24 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductVariantCell.h" #import "DisclosureIndicatorView.h"
#import "BUYVariantOptionView.h" #import "ImageKit.h"
#import "BUYProductVariant.h" #import "ProductVariantCell.h"
#import "BUYOptionValue.h" #import "Theme+Additions.h"
#import "BUYImageKit.h" #import "VariantOptionView.h"
@interface BUYProductVariantCell () @interface ProductVariantCell ()
@property (nonatomic, strong) BUYVariantOptionView *optionView1;
@property (nonatomic, strong) BUYVariantOptionView *optionView2; @property (nonatomic, strong) VariantOptionView *optionView1;
@property (nonatomic, strong) BUYVariantOptionView *optionView3; @property (nonatomic, strong) VariantOptionView *optionView2;
@property (nonatomic, strong) VariantOptionView *optionView3;
@property (nonatomic, strong) NSArray *disclosureConstraints; @property (nonatomic, strong) NSArray *disclosureConstraints;
@property (nonatomic, strong) NSArray *noDisclosureConstraints; @property (nonatomic, strong) NSArray *noDisclosureConstraints;
@property (nonatomic, strong) UIImageView *disclosureIndicatorImageView; @property (nonatomic, strong) DisclosureIndicatorView *disclosureIndicatorImageView;
@property (nonatomic, strong) BUYTheme *theme;
@end @end
@implementation BUYProductVariantCell @implementation ProductVariantCell
CGFloat const buttonWidth = 10.0f; CGFloat const buttonWidth = 10.0f;
...@@ -53,22 +54,22 @@ CGFloat const buttonWidth = 10.0f; ...@@ -53,22 +54,22 @@ CGFloat const buttonWidth = 10.0f;
UIView *backgroundView = [[UIView alloc] init]; UIView *backgroundView = [[UIView alloc] init];
[self setSelectedBackgroundView:backgroundView]; [self setSelectedBackgroundView:backgroundView];
_optionView1 = [[BUYVariantOptionView alloc] init]; _optionView1 = [[VariantOptionView alloc] init];
_optionView1.translatesAutoresizingMaskIntoConstraints = NO; _optionView1.translatesAutoresizingMaskIntoConstraints = NO;
[_optionView1 setContentCompressionResistancePriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal]; [_optionView1 setContentCompressionResistancePriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];
[self.contentView addSubview:_optionView1]; [self.contentView addSubview:_optionView1];
_optionView2 = [[BUYVariantOptionView alloc] init]; _optionView2 = [[VariantOptionView alloc] init];
_optionView2.translatesAutoresizingMaskIntoConstraints = NO; _optionView2.translatesAutoresizingMaskIntoConstraints = NO;
[_optionView3 setContentCompressionResistancePriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal]; [_optionView3 setContentCompressionResistancePriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];
[self.contentView addSubview:_optionView2]; [self.contentView addSubview:_optionView2];
_optionView3 = [[BUYVariantOptionView alloc] init]; _optionView3 = [[VariantOptionView alloc] init];
_optionView3.translatesAutoresizingMaskIntoConstraints = NO; _optionView3.translatesAutoresizingMaskIntoConstraints = NO;
[_optionView3 setContentCompressionResistancePriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal]; [_optionView3 setContentCompressionResistancePriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];
[self.contentView addSubview:_optionView3]; [self.contentView addSubview:_optionView3];
_disclosureIndicatorImageView = [[UIImageView alloc] init]; _disclosureIndicatorImageView = [[DisclosureIndicatorView alloc] init];
_disclosureIndicatorImageView.translatesAutoresizingMaskIntoConstraints = NO; _disclosureIndicatorImageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:_disclosureIndicatorImageView]; [self.contentView addSubview:_disclosureIndicatorImageView];
...@@ -97,7 +98,7 @@ CGFloat const buttonWidth = 10.0f; ...@@ -97,7 +98,7 @@ CGFloat const buttonWidth = 10.0f;
- (void)setOptionsForProductVariant:(BUYProductVariant *)productVariant - (void)setOptionsForProductVariant:(BUYProductVariant *)productVariant
{ {
NSArray *productOptions = productVariant.options.allObjects; NSArray *productOptions = [productVariant.options allObjects];
switch (productVariant.options.count) { switch (productVariant.options.count) {
case 3: case 3:
...@@ -137,16 +138,9 @@ CGFloat const buttonWidth = 10.0f; ...@@ -137,16 +138,9 @@ CGFloat const buttonWidth = 10.0f;
} }
} }
- (void)setTheme:(BUYTheme *)theme - (void)setSelectedBackgroundViewBackgroundColor:(UIColor *)selectedBackgroundViewBackgroundColor
{ {
_theme = theme; self.selectedBackgroundView.backgroundColor = selectedBackgroundViewBackgroundColor;
self.backgroundColor = [theme backgroundColor];
self.selectedBackgroundView.backgroundColor = [theme selectedBackgroundColor];
self.disclosureIndicatorImageView.image = [BUYImageKit imageOfDisclosureIndicatorImageWithFrame:CGRectMake(0, 0, buttonWidth, 16) color:[theme disclosureIndicatorColor]];
[self.optionView1 setTheme:theme];
[self.optionView2 setTheme:theme];
[self.optionView3 setTheme:theme];
} }
@end @end
// //
// BUYProductView.h // ProductView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,18 +25,16 @@ ...@@ -25,18 +25,16 @@
// //
@import UIKit; @import UIKit;
@class BUYProductViewHeader; @import Buy;
@class BUYProductViewHeaderBackgroundImageView; @class ActionableFooterView;
@class BUYProductViewFooter; @class GradientView;
@class BUYGradientView; @class ProductViewHeader;
@class BUYTheme; @class HeaderBackgroundView;
@class BUYImageLink;
@class BUYProduct;
/** /**
* The BUYProductViewController's main view, containing everything needed to display the UI for the product * The BUYProductViewController's main view, containing everything needed to display the UI for the product
*/ */
@interface BUYProductView : UIView @interface ProductView : UIView
/** /**
* The table view containg the product's image(s) in the tableHeaderView, title, price, * The table view containg the product's image(s) in the tableHeaderView, title, price,
...@@ -64,24 +62,19 @@ ...@@ -64,24 +62,19 @@
/** /**
* The tableHeaderView containting the product image(s) (if available) * The tableHeaderView containting the product image(s) (if available)
*/ */
@property (nonatomic, strong) BUYProductViewHeader *productViewHeader; @property (nonatomic, strong) ProductViewHeader *productViewHeader;
@property (nonatomic, strong) BUYProductViewHeaderBackgroundImageView *backgroundImageView; @property (nonatomic, strong) HeaderBackgroundView *backgroundImageView;
/** /**
* The footer view containing the Checkout button, and - if enabled - Apple Pay button. * The footer view containing the Checkout button, and - if enabled - Apple Pay button.
*/ */
@property (nonatomic, strong) BUYProductViewFooter *productViewFooter; @property (nonatomic, strong) ActionableFooterView *productViewFooter;
/** /**
* A gradient view that sits at the top of the product images (if available). * A gradient view that sits at the top of the product images (if available).
* This view is invisible when the navigation bar is visible. * This view is invisible when the navigation bar is visible.
*/ */
@property (nonatomic, strong) BUYGradientView *topGradientView; @property (nonatomic, strong) GradientView *topGradientView;
/**
* The theme of the product view.
*/
@property (nonatomic, weak) BUYTheme *theme;
/** /**
* Helps determine logic for setting up product images in the BUYProductViewController * Helps determine logic for setting up product images in the BUYProductViewController
...@@ -91,14 +84,14 @@ ...@@ -91,14 +84,14 @@
/** /**
* Initializer for the product view using a rect, product to display and theme * Initializer for the product view using a rect, product to display and theme
* *
* @param rect The rect is needed for the UICollectionView in the BUYProductViewHeader to setup the cell's bounds * @param rect The rect is needed for the UICollectionView in the ProductViewHeader to setup the cell's bounds
* @param product The product to display in the product view. Only used in the initializer to * @param product The product to display in the product view. Only used in the initializer to
* @param theme The theme for the product view * @param theme The theme for the product view
* @param showApplePaySetup Show Apple Pay button with 'Set Up Apple Pay' text as determined by the presenter * @param showApplePaySetup Show Apple Pay button with 'Set Up Apple Pay' text as determined by the presenter
* *
* @return An instance of the BUYProductView * @return An instance of the ProductView
*/ */
- (instancetype)initWithFrame:(CGRect)rect product:(BUYProduct*)product theme:(BUYTheme*)theme shouldShowApplePaySetup:(BOOL)showApplePaySetup; - (instancetype)initWithFrame:(CGRect)rect product:(BUYProduct*)product shouldShowApplePaySetup:(BOOL)showApplePaySetup;
/** /**
* The BUYProductViewController is the UITableViewDelegate, so it receives the UIScrollView delegate method calls. * The BUYProductViewController is the UITableViewDelegate, so it receives the UIScrollView delegate method calls.
...@@ -138,4 +131,6 @@ ...@@ -138,4 +131,6 @@
*/ */
- (void)setTopInset:(CGFloat)topInset; - (void)setTopInset:(CGFloat)topInset;
- (void)setShowsProductImageBackground:(BOOL)showsProductImageBackground UI_APPEARANCE_SELECTOR;
@end @end
// //
// BUYProductView.m // ProductView.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,38 +24,32 @@ ...@@ -24,38 +24,32 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductView.h" #import "ProductView.h"
#import "BUYProductViewHeader.h" #import "ProductViewHeader.h"
#import "BUYProductViewHeaderBackgroundImageView.h" #import "HeaderBackgroundView.h"
#import "BUYProductViewFooter.h" #import "ActionableFooterView.h"
#import "BUYGradientView.h" #import "GradientView.h"
#import "BUYProductVariantCell.h" #import "ProductVariantCell.h"
#import "BUYProductDescriptionCell.h" #import "ProductDescriptionCell.h"
#import "BUYProductHeaderCell.h" #import "ProductHeaderCell.h"
#import "BUYImageLink.h" #import "AsyncImageView.h"
#import "BUYImageView.h" #import "ErrorView.h"
#import "BUYProduct.h" #import "Theme+Additions.h"
#import "BUYProductViewErrorView.h"
#import "BUYTheme.h"
#import "BUYTheme+Additions.h"
@interface BUYProductView () @interface ProductView ()
@property (nonatomic, strong) UILabel *poweredByShopifyLabel; @property (nonatomic, strong) ErrorView *errorView;
@property (nonatomic, strong) NSLayoutConstraint *poweredByShopifyLabelConstraint;
@property (nonatomic, strong) BUYProductViewErrorView *errorView;
@property (nonatomic, strong) NSLayoutConstraint *topInsetConstraint; @property (nonatomic, strong) NSLayoutConstraint *topInsetConstraint;
@end @end
@implementation BUYProductView @implementation ProductView
- (instancetype)initWithFrame:(CGRect)rect product:(BUYProduct*)product theme:(BUYTheme*)theme shouldShowApplePaySetup:(BOOL)showApplePaySetup - (instancetype)initWithFrame:(CGRect)rect product:(BUYProduct*)product shouldShowApplePaySetup:(BOOL)showApplePaySetup
{ {
self = [super initWithFrame:rect]; self = [super initWithFrame:rect];
if (self) { if (self) {
_backgroundImageView = [[BUYProductViewHeaderBackgroundImageView alloc] initWithTheme:theme]; _backgroundImageView = [[HeaderBackgroundView alloc] init];
_backgroundImageView.hidden = theme.showsProductImageBackground == NO;
_backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO; _backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_backgroundImageView]; [self addSubview:_backgroundImageView];
...@@ -75,7 +69,6 @@ ...@@ -75,7 +69,6 @@
constant:0.0]]; constant:0.0]];
_stickyFooterView = [UIView new]; _stickyFooterView = [UIView new];
_stickyFooterView.backgroundColor = [theme backgroundColor];
_stickyFooterView.translatesAutoresizingMaskIntoConstraints = NO; _stickyFooterView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_stickyFooterView]; [self addSubview:_stickyFooterView];
...@@ -114,9 +107,9 @@ ...@@ -114,9 +107,9 @@
_tableView.layoutMargins = UIEdgeInsetsMake(_tableView.layoutMargins.top, kBuyPaddingExtraLarge, _tableView.layoutMargins.bottom, kBuyPaddingMedium); _tableView.layoutMargins = UIEdgeInsetsMake(_tableView.layoutMargins.top, kBuyPaddingExtraLarge, _tableView.layoutMargins.bottom, kBuyPaddingMedium);
[self addSubview:_tableView]; [self addSubview:_tableView];
[_tableView registerClass:[BUYProductHeaderCell class] forCellReuseIdentifier:@"headerCell"]; [_tableView registerClass:[ProductHeaderCell class] forCellReuseIdentifier:@"headerCell"];
[_tableView registerClass:[BUYProductVariantCell class] forCellReuseIdentifier:@"variantCell"]; [_tableView registerClass:[ProductVariantCell class] forCellReuseIdentifier:@"variantCell"];
[_tableView registerClass:[BUYProductDescriptionCell class] forCellReuseIdentifier:@"descriptionCell"]; [_tableView registerClass:[ProductDescriptionCell class] forCellReuseIdentifier:@"descriptionCell"];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_tableView]|" [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_tableView]|"
options:0 options:0
...@@ -138,35 +131,13 @@ ...@@ -138,35 +131,13 @@
CGFloat size = MIN(CGRectGetWidth(rect), CGRectGetHeight(rect)); CGFloat size = MIN(CGRectGetWidth(rect), CGRectGetHeight(rect));
if ([product.images count] > 0) { if ([product.images count] > 0) {
_productViewHeader = [[BUYProductViewHeader alloc] initWithFrame:CGRectMake(0, 0, size, size) theme:theme]; _productViewHeader = [[ProductViewHeader alloc] initWithFrame:CGRectMake(0, 0, size, size)];
_tableView.tableHeaderView = self.productViewHeader; _tableView.tableHeaderView = self.productViewHeader;
} else { } else {
_tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 1)]; _tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 1)];
} }
_poweredByShopifyLabel = [[UILabel alloc] init]; _productViewFooter = [ActionableFooterView new];
_poweredByShopifyLabel.translatesAutoresizingMaskIntoConstraints = NO;
_poweredByShopifyLabel.backgroundColor = [UIColor clearColor];
_poweredByShopifyLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
_poweredByShopifyLabel.text = @"Powered by Shopify";
_poweredByShopifyLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:_poweredByShopifyLabel];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_poweredByShopifyLabel]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(_poweredByShopifyLabel)]];
_poweredByShopifyLabelConstraint = [NSLayoutConstraint constraintWithItem:_poweredByShopifyLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[self addConstraint:_poweredByShopifyLabelConstraint];
_productViewFooter = [[BUYProductViewFooter alloc] initWithTheme:theme showApplePaySetup:showApplePaySetup];
_productViewFooter.translatesAutoresizingMaskIntoConstraints = NO; _productViewFooter.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_productViewFooter]; [self addSubview:_productViewFooter];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_productViewFooter]|" [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_productViewFooter]|"
...@@ -179,8 +150,8 @@ ...@@ -179,8 +150,8 @@
views:NSDictionaryOfVariableBindings(_productViewFooter)]]; views:NSDictionaryOfVariableBindings(_productViewFooter)]];
if (_productViewHeader) { if (_productViewHeader) {
_topGradientView = [[BUYGradientView alloc] init]; _topGradientView = [[GradientView alloc] init];
_topGradientView.topColor = [BUYTheme topGradientViewTopColor]; _topGradientView.topColor = [Theme topGradientViewTopColor];
_topGradientView.translatesAutoresizingMaskIntoConstraints = NO; _topGradientView.translatesAutoresizingMaskIntoConstraints = NO;
_topGradientView.userInteractionEnabled = NO; _topGradientView.userInteractionEnabled = NO;
[self addSubview:_topGradientView]; [self addSubview:_topGradientView];
...@@ -194,8 +165,6 @@ ...@@ -194,8 +165,6 @@
metrics:@{ @"height" : @(kBuyTopGradientViewHeight) } metrics:@{ @"height" : @(kBuyTopGradientViewHeight) }
views:NSDictionaryOfVariableBindings(_topGradientView)]]; views:NSDictionaryOfVariableBindings(_topGradientView)]];
} }
self.theme = theme;
} }
return self; return self;
} }
...@@ -206,17 +175,6 @@ ...@@ -206,17 +175,6 @@
[self setInsets:UIEdgeInsetsMake(self.tableView.contentInset.top, self.tableView.contentInset.left, CGRectGetHeight(self.bounds) - CGRectGetMinY(self.productViewFooter.frame), self.tableView.contentInset.right) appendToCurrentInset:NO]; [self setInsets:UIEdgeInsetsMake(self.tableView.contentInset.top, self.tableView.contentInset.left, CGRectGetHeight(self.bounds) - CGRectGetMinY(self.productViewFooter.frame), self.tableView.contentInset.right) appendToCurrentInset:NO];
} }
- (void)setTheme:(BUYTheme *)theme
{
_theme = theme;
self.tintColor = _theme.tintColor;
self.tableView.separatorColor = [theme separatorColor];
self.backgroundColor = [theme backgroundColor];
self.stickyFooterView.backgroundColor = self.backgroundColor;
self.backgroundImageView.hidden = theme.showsProductImageBackground == NO;
self.poweredByShopifyLabel.textColor = self.tableView.separatorColor;
}
- (void)updateBackgroundImage:(NSArray *)images - (void)updateBackgroundImage:(NSArray *)images
{ {
if ([images count] > 0) { if ([images count] > 0) {
...@@ -253,17 +211,14 @@ ...@@ -253,17 +211,14 @@
CGFloat opaqueOffset = CGRectGetHeight(self.productViewHeader.bounds); CGFloat opaqueOffset = CGRectGetHeight(self.productViewHeader.bounds);
CGFloat whiteStartingOffset = opaqueOffset - CGRectGetHeight(self.topGradientView.bounds); CGFloat whiteStartingOffset = opaqueOffset - CGRectGetHeight(self.topGradientView.bounds);
self.topGradientView.alpha = -(scrollView.contentOffset.y - whiteStartingOffset) / (opaqueOffset - whiteStartingOffset); self.topGradientView.alpha = -(scrollView.contentOffset.y - whiteStartingOffset) / (opaqueOffset - whiteStartingOffset);
CGFloat labelOffset = scrollView.contentSize.height - (scrollView.contentOffset.y + CGRectGetHeight(scrollView.bounds));
self.poweredByShopifyLabelConstraint.constant = (CGRectGetHeight(scrollView.bounds) / 3) + labelOffset;
} }
#pragma mark - Error Handling #pragma mark - Error Handling
- (BUYProductViewErrorView *)errorView - (ErrorView *)errorView
{ {
if (_errorView == nil) { if (_errorView == nil) {
_errorView = [[BUYProductViewErrorView alloc] initWithTheme:self.theme]; _errorView = [[ErrorView alloc] init];
_errorView.alpha = 0; _errorView.alpha = 0;
_errorView.translatesAutoresizingMaskIntoConstraints = NO; _errorView.translatesAutoresizingMaskIntoConstraints = NO;
[self insertSubview:_errorView belowSubview:self.productViewFooter]; [self insertSubview:_errorView belowSubview:self.productViewFooter];
...@@ -298,36 +253,9 @@ ...@@ -298,36 +253,9 @@
- (void)showErrorWithMessage:(NSString*)errorMessage - (void)showErrorWithMessage:(NSString*)errorMessage
{ {
self.errorView.errorLabel.text = errorMessage; [self.errorView presentErrorViewWithMessage:errorMessage completion:^{
[NSLayoutConstraint deactivateConstraints:@[self.errorView.hiddenConstraint]]; self.errorView = nil;
[NSLayoutConstraint activateConstraints:@[self.errorView.visibleConstraint]]; }];
[UIView animateWithDuration:0.3f
delay:0
usingSpringWithDamping:0.8f
initialSpringVelocity:10
options:0
animations:^{
self.errorView.alpha = 1;
[self.errorView layoutIfNeeded];
}
completion:^(BOOL finished) {
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(removeErrorView) userInfo:nil repeats:NO];
}];
}
- (void)removeErrorView
{
[NSLayoutConstraint deactivateConstraints:@[self.errorView.visibleConstraint]];
[NSLayoutConstraint activateConstraints:@[self.errorView.hiddenConstraint]];
[UIView animateWithDuration:0.3f
animations:^{
self.errorView.alpha = 0;
[self.errorView layoutIfNeeded];
}
completion:^(BOOL finished) {
[self.errorView removeFromSuperview];
self.errorView = nil;
}];
} }
- (void)setInsets:(UIEdgeInsets)edgeInsets appendToCurrentInset:(BOOL)appendToCurrentInset - (void)setInsets:(UIEdgeInsets)edgeInsets appendToCurrentInset:(BOOL)appendToCurrentInset
...@@ -344,4 +272,16 @@ ...@@ -344,4 +272,16 @@
self.topInsetConstraint.constant = topInset; self.topInsetConstraint.constant = topInset;
} }
#pragma mark - Appearance
- (void)setBackgroundColor:(UIColor *)backgroundColor {
[super setBackgroundColor:backgroundColor];
_stickyFooterView.backgroundColor = backgroundColor;
}
- (void)setShowsProductImageBackground:(BOOL)showsProductImageBackground
{
_backgroundImageView.hidden = showsProductImageBackground == NO;
}
@end @end
// //
// BUYProductViewController.h // ProductViewController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,11 +24,13 @@ ...@@ -24,11 +24,13 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYTheme.h" @import UIKit;
#import "BUYViewController.h" @import Buy;
#import "BUYClient.h"
@interface BUYProductViewController : BUYViewController <BUYThemeable> #import "Theme.h"
#import "PaymentViewController.h"
@interface ProductViewController : PaymentViewController
/** /**
* Creates a BUYProductViewController with a BUYClient and a theme * Creates a BUYProductViewController with a BUYClient and a theme
...@@ -37,11 +39,21 @@ ...@@ -37,11 +39,21 @@
* use `initWithClient:` * use `initWithClient:`
* *
* @param client A BUYClient configured to your shop * @param client A BUYClient configured to your shop
* @param theme A BUYTheme * @param theme A Theme
*
* @return A PaymentViewController
*/
- (instancetype)initWithClient:(BUYClient *)client theme:(Theme *)theme;
/**
* Creates a BUYProductViewController with a BUYClient and a cart
*
* @param client A BUYClient configured to your shop
* @param cart A cart that enables a "Add to cart" button instead of the web checout button
* *
* @return A BUYViewController * @return A PaymentViewController with an "Add to cart" button
*/ */
- (instancetype)initWithClient:(BUYClient *)client theme:(BUYTheme *)theme; - (instancetype)initWithClient:(BUYClient *)client cart:(BUYCart *)cart;
/** /**
* Loads the product details * Loads the product details
......
// //
// BUYProductViewHeader.h // ProductViewHeader.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,16 +25,17 @@ ...@@ -25,16 +25,17 @@
// //
@import UIKit; @import UIKit;
@class BUYImageView; @import Buy;
@class AsyncImageView;
@class BUYProductVariant; @class BUYProductVariant;
@class BUYTheme; @class HeaderOverlayView;
@class BUYProductViewHeaderOverlay;
/** /**
* The tableHeaderView containing a horizontally scrolling collection view * The tableHeaderView containing a horizontally scrolling collection view
* that display product images. * that display product images.
*/ */
@interface BUYProductViewHeader : UIView @interface ProductViewHeader : UIView
/** /**
* A horiztonally scrolling collection view containing product images. * A horiztonally scrolling collection view containing product images.
...@@ -45,7 +46,7 @@ ...@@ -45,7 +46,7 @@
* An overlay view containing effects views (light or dark view and a UIVisualEffectsView) that becomes * An overlay view containing effects views (light or dark view and a UIVisualEffectsView) that becomes
* visible as the user scrolls the contents up. This provides a more seamless transition for the navigation bar. * visible as the user scrolls the contents up. This provides a more seamless transition for the navigation bar.
*/ */
@property (nonatomic, strong) BUYProductViewHeaderOverlay *productViewHeaderOverlay; @property (nonatomic, strong) HeaderOverlayView *productViewHeaderOverlay;
/** /**
* Initializer with a frame to setup the product image collection view and theme the header * Initializer with a frame to setup the product image collection view and theme the header
...@@ -55,11 +56,11 @@ ...@@ -55,11 +56,11 @@
* *
* @return The product view header to display in the BUYProductViewController's table view tableHeaderView * @return The product view header to display in the BUYProductViewController's table view tableHeaderView
*/ */
- (instancetype)initWithFrame:(CGRect)frame theme:(BUYTheme*)theme; - (instancetype)initWithFrame:(CGRect)frame;
/** /**
* A method to set the content offset of the table view and calculate the height of the image * A method to set the content offset of the table view and calculate the height of the image
* used to set the correct height and offset for the stickyFooterView in the BUYProductView. * used to set the correct height and offset for the stickyFooterView in the ProductView.
* *
* @param scrollView The table view being scrolled * @param scrollView The table view being scrolled
* *
......
// //
// BUYProductViewHeader.m // ProductViewHeader.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,29 +24,26 @@ ...@@ -24,29 +24,26 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYProductViewHeader.h" #import "ProductViewHeader.h"
#import "BUYImageView.h" #import "AsyncImageView.h"
#import "BUYGradientView.h" #import "GradientView.h"
#import "BUYProductImageCollectionViewCell.h" #import "ProductImageCell.h"
#import "BUYImageLink.h" #import "Theme+Additions.h"
#import "BUYProductVariant.h" #import "HeaderOverlayView.h"
#import "BUYTheme.h"
#import "BUYTheme+Additions.h"
#import "BUYProductViewHeaderOverlay.h"
@interface BUYProductViewHeader () @interface ProductViewHeader ()
@property (nonatomic, strong) UIPageControl *pageControl; @property (nonatomic, strong) UIPageControl *pageControl;
@property (nonatomic, strong) UIView *bottomGradientContainerView; @property (nonatomic, strong) UIView *bottomGradientContainerView;
@property (nonatomic, strong) BUYGradientView *bottomGradientView; @property (nonatomic, strong) GradientView *bottomGradientView;
@property (nonatomic, strong) NSLayoutConstraint *bottomGradientViewLayoutConstraintHeight; @property (nonatomic, strong) NSLayoutConstraint *bottomGradientViewLayoutConstraintHeight;
@property (nonatomic, strong) NSLayoutConstraint *bottomGradientViewBottomContraint; @property (nonatomic, strong) NSLayoutConstraint *bottomGradientViewBottomContraint;
@end @end
@implementation BUYProductViewHeader @implementation ProductViewHeader
- (instancetype)initWithFrame:(CGRect)frame theme:(BUYTheme*)theme - (instancetype)initWithFrame:(CGRect)frame
{ {
self = [super initWithFrame:frame]; self = [super initWithFrame:frame];
if (self) { if (self) {
...@@ -63,7 +60,7 @@ ...@@ -63,7 +60,7 @@
_collectionView.pagingEnabled = YES; _collectionView.pagingEnabled = YES;
_collectionView.clipsToBounds = NO; _collectionView.clipsToBounds = NO;
_collectionView.translatesAutoresizingMaskIntoConstraints = NO; _collectionView.translatesAutoresizingMaskIntoConstraints = NO;
[_collectionView registerClass:[BUYProductImageCollectionViewCell class] forCellWithReuseIdentifier:@"Cell"]; [_collectionView registerClass:[ProductImageCell class] forCellWithReuseIdentifier:@"Cell"];
[self addSubview:_collectionView]; [self addSubview:_collectionView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_collectionView]|" [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_collectionView]|"
...@@ -101,7 +98,7 @@ ...@@ -101,7 +98,7 @@
constant:kBuyBottomGradientHeightWithoutPageControl]; constant:kBuyBottomGradientHeightWithoutPageControl];
[self addConstraint:_bottomGradientViewLayoutConstraintHeight]; [self addConstraint:_bottomGradientViewLayoutConstraintHeight];
_bottomGradientView = [[BUYGradientView alloc] init]; _bottomGradientView = [[GradientView alloc] init];
_bottomGradientView.userInteractionEnabled = NO; _bottomGradientView.userInteractionEnabled = NO;
_bottomGradientView.topColor = [UIColor clearColor]; _bottomGradientView.topColor = [UIColor clearColor];
_bottomGradientView.bottomColor = [UIColor colorWithWhite:0 alpha:0.05f]; _bottomGradientView.bottomColor = [UIColor colorWithWhite:0 alpha:0.05f];
...@@ -160,7 +157,7 @@ ...@@ -160,7 +157,7 @@
multiplier:1.0 multiplier:1.0
constant:kBuyPageControlHeight]]; constant:kBuyPageControlHeight]];
_productViewHeaderOverlay = [[BUYProductViewHeaderOverlay alloc] initWithTheme:theme]; _productViewHeaderOverlay = [[HeaderOverlayView alloc] init];
_productViewHeaderOverlay.translatesAutoresizingMaskIntoConstraints = NO; _productViewHeaderOverlay.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_productViewHeaderOverlay]; [self addSubview:_productViewHeaderOverlay];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_productViewHeaderOverlay]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_productViewHeaderOverlay)]]; [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_productViewHeaderOverlay]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_productViewHeaderOverlay)]];
...@@ -192,7 +189,7 @@ ...@@ -192,7 +189,7 @@
CGRect visibleRect = (CGRect){.origin = self.collectionView.contentOffset, .size = self.collectionView.bounds.size}; CGRect visibleRect = (CGRect){.origin = self.collectionView.contentOffset, .size = self.collectionView.bounds.size};
CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect)); CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
NSIndexPath *visibleIndexPath = [self.collectionView indexPathForItemAtPoint:visiblePoint]; NSIndexPath *visibleIndexPath = [self.collectionView indexPathForItemAtPoint:visiblePoint];
BUYProductImageCollectionViewCell *cell = (BUYProductImageCollectionViewCell*)[self.collectionView cellForItemAtIndexPath:visibleIndexPath]; ProductImageCell *cell = (ProductImageCell*)[self.collectionView cellForItemAtIndexPath:visibleIndexPath];
if (cell) { if (cell) {
[cell setContentOffset:scrollView.contentOffset]; [cell setContentOffset:scrollView.contentOffset];
return cell.productImageViewConstraintHeight.constant; return cell.productImageViewConstraintHeight.constant;
......
// //
// BUYNavigationController.h // ProductViewNavigationController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,10 +24,9 @@ ...@@ -24,10 +24,9 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
@import UIKit; #import "NavigationController.h"
#import "BUYTheme.h"
@protocol BUYNavigationControllerDelegate <NSObject> @protocol ProductViewNavigationControllerDelegate <NSObject>
/** /**
* Delegate callback when the BUYProductViewController will dismiss * Delegate callback when the BUYProductViewController will dismiss
...@@ -45,10 +44,7 @@ ...@@ -45,10 +44,7 @@
@end @end
/** @interface ProductViewNavigationController : NavigationController
* A custom navigation controller used in the BUYProductViewController, adding a close button and the ability to theme the navigation bar
*/
@interface BUYNavigationController : UINavigationController <BUYThemeable>
/** /**
* The close button has two styles; white or tint color. This methods updates the button image with the preferred style. * The close button has two styles; white or tint color. This methods updates the button image with the preferred style.
...@@ -60,9 +56,8 @@ ...@@ -60,9 +56,8 @@
- (void)updateCloseButtonImageWithTintColor:(BOOL)tintColor duration:(CGFloat)duration; - (void)updateCloseButtonImageWithTintColor:(BOOL)tintColor duration:(CGFloat)duration;
/** /**
* The BUYNavigationControllerDelegate * The SmoothNavigationControllerDelegate
*/ */
@property (nonatomic, weak) id <BUYNavigationControllerDelegate> navigationDelegate; @property (nonatomic, weak) id <ProductViewNavigationControllerDelegate> navigationDelegate;
@end @end
// //
// BUYNavigationController.m // ProductViewNavigationController.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,16 +24,12 @@ ...@@ -24,16 +24,12 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYNavigationController.h" #import "ProductViewNavigationController.h"
#import "BUYImageKit.h" #import "ImageKit.h"
#import "BUYTheme+Additions.h" #import "Theme+Additions.h"
#import "BUYProductViewController.h" #import "ProductViewController.h"
@interface BUYNavigationController () @implementation ProductViewNavigationController
@property (nonatomic, strong) BUYTheme *theme;
@end
@implementation BUYNavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController - (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{ {
...@@ -45,18 +41,23 @@ ...@@ -45,18 +41,23 @@
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:closeButton]; UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:closeButton];
self.topViewController.navigationItem.leftBarButtonItem = barButtonItem; self.topViewController.navigationItem.leftBarButtonItem = barButtonItem;
if ([[UINavigationBar class] respondsToSelector:@selector(appearanceWhenContainedInInstancesOfClasses:)]) { if ([[UINavigationBar class] respondsToSelector:@selector(appearanceWhenContainedInInstancesOfClasses:)]) {
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[BUYNavigationController class]]] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault]; [[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[ProductViewNavigationController class]]] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
} else { } else {
[[UINavigationBar appearanceWhenContainedIn:[BUYNavigationController class], nil] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault]; [[UINavigationBar appearanceWhenContainedIn:[ProductViewNavigationController class], nil] setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
} }
[self updateCloseButtonImageWithTintColor:YES duration:0];
return self; return self;
} }
- (void)updateCloseButtonImageWithTintColor:(BOOL)tintColor duration:(CGFloat)duration - (void)updateCloseButtonImageWithTintColor:(BOOL)tintColor duration:(CGFloat)duration
{ {
UIButton *button = (UIButton*)self.topViewController.navigationItem.leftBarButtonItem.customView; UIButton *button = (UIButton*)self.topViewController.navigationItem.leftBarButtonItem.customView;
UIImage *newButtonImage = [BUYImageKit imageOfProductViewCloseImageWithFrame:button.bounds color:tintColor ? self.theme.tintColor : [UIColor whiteColor] hasShadow:tintColor == NO]; UIImage *newButtonImage = [ImageKit imageOfProductViewCloseImageWithFrame:button.bounds color:[UIColor whiteColor] hasShadow:tintColor == NO];
if (tintColor) {
newButtonImage = [newButtonImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}
[UIView transitionWithView:button.imageView [UIView transitionWithView:button.imageView
duration:duration duration:duration
options:(UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionBeginFromCurrentState) options:(UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionBeginFromCurrentState)
...@@ -86,18 +87,6 @@ ...@@ -86,18 +87,6 @@
return self.topViewController.shouldAutorotate; return self.topViewController.shouldAutorotate;
} }
- (void)setTheme:(BUYTheme *)theme
{
_theme = theme;
self.navigationBar.barStyle = [theme navigationBarStyle];
[self updateCloseButtonImageWithTintColor:NO duration:0];
if ([[UINavigationBar class] respondsToSelector:@selector(appearanceWhenContainedInInstancesOfClasses:)]) {
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[BUYNavigationController class]]] setTitleTextAttributes:@{ NSForegroundColorAttributeName: [theme navigationBarTitleColor] }];
} else {
[[UINavigationBar appearanceWhenContainedIn:[BUYNavigationController class], nil] setTitleTextAttributes:@{ NSForegroundColorAttributeName: [theme navigationBarTitleColor] }];
}
}
-(UIViewController *)childViewControllerForStatusBarStyle -(UIViewController *)childViewControllerForStatusBarStyle
{ {
return [self childViewController]; return [self childViewController];
...@@ -110,7 +99,7 @@ ...@@ -110,7 +99,7 @@
-(UIViewController*)childViewController -(UIViewController*)childViewController
{ {
if ([self.visibleViewController isKindOfClass:[BUYProductViewController class]] == NO) { if ([self.visibleViewController isKindOfClass:[ProductViewController class]] == NO) {
return self.viewControllers[0]; return self.viewControllers[0];
} }
return self.visibleViewController; return self.visibleViewController;
......
// //
// BUYPresentationControllerWithNavigationController.h // ProductViewPresentationController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,16 +25,15 @@ ...@@ -25,16 +25,15 @@
// //
@import UIKit; @import UIKit;
#import "BUYTheme.h"
@protocol BUYPresentationControllerWithNavigationControllerDelegate; @protocol ProductViewNavigationControllerDelegate;
/** /**
* Initialized the BUYProductViewController inside a navigation controller (`BUYNavigationController`). * Initialized the BUYProductViewController inside a navigation controller (`ProductViewNavigationController`).
*/ */
@interface BUYPresentationControllerWithNavigationController : UIPresentationController <UIAdaptivePresentationControllerDelegate, BUYThemeable> @interface ProductViewPresentationController : UIPresentationController <UIAdaptivePresentationControllerDelegate>
@property (nonatomic, weak) id <BUYNavigationControllerDelegate> navigationDelegate; @property (nonatomic, weak) id <ProductViewNavigationControllerDelegate> navigationDelegate;
/** /**
* The desired presentation style * The desired presentation style
......
// //
// BUYPresentationControllerWithNavigationController.m // ProductViewPresentationController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,23 +24,16 @@ ...@@ -24,23 +24,16 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYNavigationController.h" #import "OptionSelectionNavigationController.h"
#import "BUYOptionSelectionNavigationController.h" #import "ProductViewPresentationController.h"
#import "BUYPresentationControllerWithNavigationController.h" #import "ProductViewNavigationController.h"
#import "BUYImageKit.h"
@interface BUYPresentationControllerWithNavigationController () @implementation ProductViewPresentationController
@property (nonatomic, strong) BUYTheme *theme;
@end
@implementation BUYPresentationControllerWithNavigationController
- (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style - (UIViewController *)presentationController:(UIPresentationController *)controller viewControllerForAdaptivePresentationStyle:(UIModalPresentationStyle)style
{ {
BUYNavigationController *navigationController = [[BUYNavigationController alloc] initWithRootViewController:controller.presentedViewController]; ProductViewNavigationController *navigationController = [[ProductViewNavigationController alloc] initWithRootViewController:controller.presentedViewController];
navigationController.navigationDelegate = self.navigationDelegate; navigationController.navigationDelegate = self.navigationDelegate;
[navigationController setTheme:self.theme];
return navigationController; return navigationController;
} }
...@@ -59,11 +52,4 @@ ...@@ -59,11 +52,4 @@
return (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) ? UIModalPresentationFullScreen : UIModalPresentationFormSheet; return (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) ? UIModalPresentationFullScreen : UIModalPresentationFormSheet;
} }
- (void)setTheme:(BUYTheme *)theme
{
_theme = theme;
BUYNavigationController *navigationController = (BUYNavigationController*)self.presentedViewController;
[navigationController setTheme:theme];
}
@end @end
// //
// BUYVariantOptionBreadCrumbsView.h // OptionBreadCrumbsView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,12 +25,11 @@ ...@@ -25,12 +25,11 @@
// //
@import UIKit; @import UIKit;
#import "BUYTheme.h"
/** /**
* A view containing the current selection of variant option values that sits below the navigation controller for the variant selection. * A view containing the current selection of variant option values that sits below the navigation controller for the variant selection.
*/ */
@interface BUYVariantOptionBreadCrumbsView : UIView <BUYThemeable> @interface OptionBreadCrumbsView : UIView
/** /**
* Auto Layout constraint for setting the bread crumbs as hidden * Auto Layout constraint for setting the bread crumbs as hidden
...@@ -49,4 +48,9 @@ ...@@ -49,4 +48,9 @@
*/ */
- (void)setSelectedBuyOptionValues:(NSArray*)optionValues; - (void)setSelectedBuyOptionValues:(NSArray*)optionValues;
/**
* The color of the labels
*/
-(void)setVariantOptionTextColor:(UIColor*)color UI_APPEARANCE_SELECTOR;
@end @end
// //
// BUYVariantOptionBreadCrumbsView.m // OptionBreadCrumbsView.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,10 +24,10 @@ ...@@ -24,10 +24,10 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYVariantOptionBreadCrumbsView.h" #import "OptionBreadCrumbsView.h"
#import "BUYTheme+Additions.h" #import "Theme+Additions.h"
@interface BUYVariantOptionBreadCrumbsView () @interface OptionBreadCrumbsView ()
@property (nonatomic, strong) UILabel *optionOneLabel; @property (nonatomic, strong) UILabel *optionOneLabel;
@property (nonatomic, strong) UILabel *optionTwoLabel; @property (nonatomic, strong) UILabel *optionTwoLabel;
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
@end @end
@implementation BUYVariantOptionBreadCrumbsView @implementation OptionBreadCrumbsView
- (instancetype)init - (instancetype)init
{ {
...@@ -47,15 +47,13 @@ ...@@ -47,15 +47,13 @@
_optionOneLabel = [UILabel new]; _optionOneLabel = [UILabel new];
_optionOneLabel.translatesAutoresizingMaskIntoConstraints = NO; _optionOneLabel.translatesAutoresizingMaskIntoConstraints = NO;
_optionOneLabel.font = [BUYTheme variantBreadcrumbsFont]; _optionOneLabel.font = [Theme variantBreadcrumbsFont];
_optionOneLabel.textColor = [BUYTheme variantBreadcrumbsTextColor]; _optionOneLabel.text = NSLocalizedString(@"Selected: ", @"Prefix for selected option value in variant selector");
_optionOneLabel.text = @"Selected: ";
[self addSubview:_optionOneLabel]; [self addSubview:_optionOneLabel];
_optionTwoLabel = [UILabel new]; _optionTwoLabel = [UILabel new];
_optionTwoLabel.translatesAutoresizingMaskIntoConstraints = NO; _optionTwoLabel.translatesAutoresizingMaskIntoConstraints = NO;
_optionTwoLabel.font = [BUYTheme variantBreadcrumbsFont]; _optionTwoLabel.font = [Theme variantBreadcrumbsFont];
_optionTwoLabel.textColor = [BUYTheme variantBreadcrumbsTextColor];
_optionTwoLabel.alpha = 0; _optionTwoLabel.alpha = 0;
[_optionTwoLabel setContentHuggingPriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal]; [_optionTwoLabel setContentHuggingPriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];
[self addSubview:_optionTwoLabel]; [self addSubview:_optionTwoLabel];
...@@ -78,11 +76,6 @@ ...@@ -78,11 +76,6 @@
return self; return self;
} }
- (void)setTheme:(BUYTheme *)theme
{
self.backgroundColor = [theme variantBreadcrumbsBackground];
}
- (void)setSelectedBuyOptionValues:(NSArray*)optionValues - (void)setSelectedBuyOptionValues:(NSArray*)optionValues
{ {
if ([optionValues count] == 1) { if ([optionValues count] == 1) {
...@@ -128,4 +121,16 @@ ...@@ -128,4 +121,16 @@
}]; }];
} }
-(void)setBackgroundColor:(UIColor *)backgroundColor
{
[super setBackgroundColor:backgroundColor];
self.optionOneLabel.backgroundColor = self.optionTwoLabel.backgroundColor = self.backgroundColor;
}
-(void)setVariantOptionTextColor:(UIColor*)color
{
self.optionOneLabel.textColor = self.optionTwoLabel.textColor = color;
}
@end @end
// //
// BUYNavigationController.h // OptionSelectionNavigationController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,14 +25,13 @@ ...@@ -25,14 +25,13 @@
// //
@import UIKit; @import UIKit;
#import "BUYNavigationController.h" #import "NavigationController.h"
#import "BUYTheme.h" #import "OptionBreadCrumbsView.h"
#import "BUYVariantOptionBreadCrumbsView.h"
/** /**
* Custom navigation controller for variant option selection * Custom navigation controller for variant option selection
*/ */
@interface BUYOptionSelectionNavigationController : BUYNavigationController <BUYThemeable> @interface OptionSelectionNavigationController : NavigationController
/** /**
* Used to set whether to dismiss with dropping animation when variant selection is cancelled instead of confirmed * Used to set whether to dismiss with dropping animation when variant selection is cancelled instead of confirmed
...@@ -42,6 +41,6 @@ ...@@ -42,6 +41,6 @@
/** /**
* The bread crumbs view for variant option selections * The bread crumbs view for variant option selections
*/ */
@property (nonatomic, strong) BUYVariantOptionBreadCrumbsView *breadsCrumbsView; @property (nonatomic, strong) OptionBreadCrumbsView *breadsCrumbsView;
@end @end
// //
// BUYNavigationController.m // OptionSelectionNavigationController.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,16 +24,15 @@ ...@@ -24,16 +24,15 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYOptionSelectionNavigationController.h" #import "OptionSelectionNavigationController.h"
#import "BUYPresentationControllerForVariantSelection.h" #import "VariantSelectionPresentationController.h"
#import "BUYImageKit.h" #import "ImageKit.h"
#import "BUYTheme+Additions.h"
@interface BUYOptionSelectionNavigationController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning> @interface OptionSelectionNavigationController () <UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning>
@end @end
@implementation BUYOptionSelectionNavigationController @implementation OptionSelectionNavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController - (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{ {
...@@ -46,18 +45,18 @@ ...@@ -46,18 +45,18 @@
self.view.clipsToBounds = YES; self.view.clipsToBounds = YES;
// Add custom back button // Add custom back button
UIImage *buttonImage = [[BUYImageKit imageOfVariantBackImageWithFrame:CGRectMake(0, 0, 12, 18)] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; UIImage *buttonImage = [[ImageKit imageOfVariantBackImageWithFrame:CGRectMake(0, 0, 12, 18)] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
if ([[UIBarButtonItem class] respondsToSelector:@selector(appearanceWhenContainedInInstancesOfClasses:)]) { if ([[UIBarButtonItem class] respondsToSelector:@selector(appearanceWhenContainedInInstancesOfClasses:)]) {
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[BUYNavigationController class]]] setBackButtonBackgroundImage:[buttonImage resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 0)] [[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[NavigationController class]]] setBackButtonBackgroundImage:[buttonImage resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 0)]
forState:UIControlStateNormal forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault]; barMetrics:UIBarMetricsDefault];
} else { } else {
[[UIBarButtonItem appearanceWhenContainedIn:[BUYNavigationController class], nil] setBackButtonBackgroundImage:[buttonImage resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 0)] [[UIBarButtonItem appearanceWhenContainedIn:[NavigationController class], nil] setBackButtonBackgroundImage:[buttonImage resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 0)]
forState:UIControlStateNormal forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault]; barMetrics:UIBarMetricsDefault];
} }
_breadsCrumbsView = [BUYVariantOptionBreadCrumbsView new]; _breadsCrumbsView = [OptionBreadCrumbsView new];
_breadsCrumbsView.translatesAutoresizingMaskIntoConstraints = NO; _breadsCrumbsView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_breadsCrumbsView]; [self.view addSubview:_breadsCrumbsView];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_breadsCrumbsView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]]; [self.view addConstraint:[NSLayoutConstraint constraintWithItem:_breadsCrumbsView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]];
...@@ -71,19 +70,11 @@ ...@@ -71,19 +70,11 @@
return self; return self;
} }
- (void)setTheme:(BUYTheme *)theme
{
[self.breadsCrumbsView setTheme:theme];
self.navigationBar.barStyle = [theme navigationBarStyle];
[self.navigationBar setTitleTextAttributes:@{ NSForegroundColorAttributeName : [theme navigationBarTitleVariantSelectionColor] }];
self.navigationBar.tintColor = theme.tintColor;
}
#pragma mark - Transitioning Delegate Methods #pragma mark - Transitioning Delegate Methods
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source - (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
{ {
BUYPresentationControllerForVariantSelection *presentationController = [[BUYPresentationControllerForVariantSelection alloc] initWithPresentedViewController:presented presentingViewController:presenting]; VariantSelectionPresentationController *presentationController = [[VariantSelectionPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
return presentationController; return presentationController;
} }
...@@ -126,7 +117,7 @@ ...@@ -126,7 +117,7 @@
UIView *dismissedView = [transitionContext viewForKey:UITransitionContextFromViewKey]; UIView *dismissedView = [transitionContext viewForKey:UITransitionContextFromViewKey];
CGRect frame = dismissedView.frame; CGRect frame = dismissedView.frame;
CGAffineTransform transform; CGAffineTransform transform;
BUYOptionSelectionNavigationController *optionSelectionNavigationController = (BUYOptionSelectionNavigationController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; OptionSelectionNavigationController *optionSelectionNavigationController = (OptionSelectionNavigationController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
if (optionSelectionNavigationController.dismissWithCancelAnimation) { if (optionSelectionNavigationController.dismissWithCancelAnimation) {
frame.origin.y += 150; frame.origin.y += 150;
int angle = arc4random_uniform(20) - 10; int angle = arc4random_uniform(20) - 10;
......
// //
// BUYOptionSelectionViewController.h // OptionSelectionViewController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,12 +25,10 @@ ...@@ -25,12 +25,10 @@
// //
@import UIKit; @import UIKit;
#import "BUYTheme.h" @import Buy;
@class OptionSelectionViewController;
@class BUYOptionValue; @protocol OptionSelectionDelegate <NSObject>
@class BUYOptionSelectionViewController;
@protocol BUYOptionSelectionDelegate <NSObject>
/** /**
* Called when a user selected an option value * Called when a user selected an option value
...@@ -38,21 +36,21 @@ ...@@ -38,21 +36,21 @@
* @param controller The BUYOptionSelectionViewController * @param controller The BUYOptionSelectionViewController
* @param optionValue The selected BUYOptionValue * @param optionValue The selected BUYOptionValue
*/ */
- (void)optionSelectionController:(BUYOptionSelectionViewController *)controller didSelectOptionValue:(BUYOptionValue *)optionValue; - (void)optionSelectionController:(OptionSelectionViewController *)controller didSelectOptionValue:(BUYOptionValue *)optionValue;
/** /**
* Called when a user pops the navigation controller and backs out of the current option * Called when a user pops the navigation controller and backs out of the current option
* *
* @param controller The BUYOptionSelectionViewController * @param controller The BUYOptionSelectionViewController
*/ */
- (void)optionSelectionControllerDidBackOutOfChoosingOption:(BUYOptionSelectionViewController *)controller; - (void)optionSelectionControllerDidBackOutOfChoosingOption:(OptionSelectionViewController *)controller;
@end @end
/** /**
* The view controller containing a table view with variant options for selection * The view controller containing a table view with variant options for selection
*/ */
@interface BUYOptionSelectionViewController : UITableViewController <BUYThemeable> @interface OptionSelectionViewController : UITableViewController
/** /**
* Initalizer for the BUYOptionSelectionViewController * Initalizer for the BUYOptionSelectionViewController
...@@ -92,6 +90,6 @@ ...@@ -92,6 +90,6 @@
/** /**
* Delegate to inform about option selections and cancellations * Delegate to inform about option selections and cancellations
*/ */
@property (nonatomic, weak) id <BUYOptionSelectionDelegate> delegate; @property (nonatomic, weak) id <OptionSelectionDelegate> delegate;
@end @end
// //
// BUYOptionSelectionViewController.m // OptionSelectionViewController.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,20 +24,19 @@ ...@@ -24,20 +24,19 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYImageKit.h" #import "ImageKit.h"
#import "BUYOptionSelectionViewController.h" #import "OptionSelectionViewController.h"
#import "BUYOptionValue.h" #import "OptionValueCell.h"
#import "BUYOptionValueCell.h" #import "Theme+Additions.h"
#import "BUYTheme.h"
#import "BUYTheme+Additions.h" @interface OptionSelectionViewController ()
@interface BUYOptionSelectionViewController ()
@property (nonatomic, strong) NSArray *optionValues; @property (nonatomic, strong) NSArray *optionValues;
@property (nonatomic, weak) BUYTheme *theme;
@property (nonatomic, strong) NSArray *filteredProductVariantsForSelectionOption; @property (nonatomic, strong) NSArray *filteredProductVariantsForSelectionOption;
@end @end
@implementation BUYOptionSelectionViewController @implementation OptionSelectionViewController
- (instancetype)initWithOptionValues:(NSArray *)optionValues filteredProductVariantsForSelectionOption:(NSArray*)filteredProductVariantsForSelectionOption - (instancetype)initWithOptionValues:(NSArray *)optionValues filteredProductVariantsForSelectionOption:(NSArray*)filteredProductVariantsForSelectionOption
{ {
...@@ -55,18 +54,10 @@ ...@@ -55,18 +54,10 @@
return self; return self;
} }
- (void)setTheme:(BUYTheme *)theme
{
_theme = theme;
self.view.backgroundColor = [theme backgroundColor];
self.tableView.backgroundColor = self.view.backgroundColor;
self.tableView.separatorColor = [theme separatorColor];
}
- (void)viewDidLoad - (void)viewDidLoad
{ {
[super viewDidLoad]; [super viewDidLoad];
[self.tableView registerClass:[BUYOptionValueCell class] forCellReuseIdentifier:@"Cell"]; [self.tableView registerClass:[OptionValueCell class] forCellReuseIdentifier:@"Cell"];
self.tableView.tableFooterView = [UIView new]; self.tableView.tableFooterView = [UIView new];
} }
...@@ -97,15 +88,15 @@ ...@@ -97,15 +88,15 @@
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ {
BUYOptionValue *optionValue = self.optionValues[indexPath.row]; BUYOptionValue *optionValue = self.optionValues[indexPath.row];
BUYOptionValueCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; OptionValueCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
cell.selectedImageView.hidden = ![optionValue isEqual:self.selectedOptionValue]; cell.selectedImageView.hidden = ![optionValue isEqual:self.selectedOptionValue];
if (self.isLastOption) { if (self.isLastOption) {
cell.accessoryType = UITableViewCellAccessoryNone; cell.accessoryType = UITableViewCellAccessoryNone;
BUYProductVariant *productVariant = (BUYProductVariant*)self.filteredProductVariantsForSelectionOption[indexPath.row]; BUYProductVariant *productVariant = (BUYProductVariant*)self.filteredProductVariantsForSelectionOption[indexPath.row];
[cell setOptionValue:optionValue productVariant:productVariant currencyFormatter:self.currencyFormatter theme:self.theme]; [cell setOptionValue:optionValue productVariant:productVariant currencyFormatter:self.currencyFormatter];
} else { } else {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[cell setOptionValue:optionValue productVariant:nil currencyFormatter:nil theme:self.theme]; [cell setOptionValue:optionValue productVariant:nil currencyFormatter:nil];
} }
[cell setNeedsLayout]; [cell setNeedsLayout];
[cell layoutIfNeeded]; [cell layoutIfNeeded];
......
// //
// BUYOptionValueCell.h // OptionValueCell.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,14 +25,12 @@ ...@@ -25,14 +25,12 @@
// //
@import UIKit; @import UIKit;
@class BUYTheme; @import Buy;
@class BUYOptionValue;
@class BUYProductVariant;
/** /**
* A table view cell displaying the option value * A table view cell displaying the option value
*/ */
@interface BUYOptionValueCell : UITableViewCell @interface OptionValueCell : UITableViewCell
/** /**
* Image view containing a checkmark for current option value selection * Image view containing a checkmark for current option value selection
...@@ -45,9 +43,10 @@ ...@@ -45,9 +43,10 @@
* @param optionValue The option value to display * @param optionValue The option value to display
* @param productVariant The product variant matching the option value * @param productVariant The product variant matching the option value
* @param currencyFormatter A formatter with the shop's currency * @param currencyFormatter A formatter with the shop's currency
* @param theme The current theme
*/ */
- (void)setOptionValue:(BUYOptionValue *)optionValue productVariant:(BUYProductVariant*)productVariant currencyFormatter:(NSNumberFormatter*)currencyFormatter theme:(BUYTheme *)theme; - (void)setOptionValue:(BUYOptionValue *)optionValue productVariant:(BUYProductVariant*)productVariant currencyFormatter:(NSNumberFormatter*)currencyFormatter;
- (void)setSelectedBackgroundViewBackgroundColor:(UIColor *)selectedBackgroundViewBackgroundColor UI_APPEARANCE_SELECTOR;
@end @end
// //
// BUYOptionValueCell.m // OptionValueCell.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,24 +24,24 @@ ...@@ -24,24 +24,24 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYOptionValueCell.h" #import "DisclosureIndicatorView.h"
#import "BUYImageKit.h" #import "ImageKit.h"
#import "BUYOptionValue.h" #import "OptionValueCell.h"
#import "BUYProductVariant.h" #import "Theme+Additions.h"
#import "BUYTheme.h"
#import "BUYTheme+Additions.h" @interface OptionValueCell()
@interface BUYOptionValueCell()
@property (nonatomic, strong) NSArray *priceConstraints; @property (nonatomic, strong) NSArray *priceConstraints;
@property (nonatomic, strong) NSArray *noPriceConstraints; @property (nonatomic, strong) NSArray *noPriceConstraints;
@property (nonatomic, strong) NSArray *disclosureConstraints; @property (nonatomic, strong) NSArray *disclosureConstraints;
@property (nonatomic, strong) NSArray *noDisclosureConstraints; @property (nonatomic, strong) NSArray *noDisclosureConstraints;
@property (nonatomic, strong) UIImageView *disclosureIndicatorImageView; @property (nonatomic, strong) DisclosureIndicatorView *disclosureIndicatorImageView;
@property (nonatomic, strong) UILabel *titleLabel; @property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *priceLabel; @property (nonatomic, strong) UILabel *priceLabel;
@end @end
@implementation BUYOptionValueCell @implementation OptionValueCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{ {
...@@ -50,7 +50,6 @@ ...@@ -50,7 +50,6 @@
UIView *backgroundView = [[UIView alloc] init]; UIView *backgroundView = [[UIView alloc] init];
[self setSelectedBackgroundView:backgroundView]; [self setSelectedBackgroundView:backgroundView];
self.textLabel.backgroundColor = [UIColor clearColor];
self.layoutMargins = UIEdgeInsetsMake(kBuyPaddingLarge, kBuyPaddingExtraLarge, kBuyPaddingLarge, kBuyPaddingLarge); self.layoutMargins = UIEdgeInsetsMake(kBuyPaddingLarge, kBuyPaddingExtraLarge, kBuyPaddingLarge, kBuyPaddingLarge);
UIView *labelContainerView = [[UIView alloc] init]; UIView *labelContainerView = [[UIView alloc] init];
...@@ -60,15 +59,15 @@ ...@@ -60,15 +59,15 @@
_titleLabel = [[UILabel alloc] init]; _titleLabel = [[UILabel alloc] init];
_titleLabel.translatesAutoresizingMaskIntoConstraints = NO; _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[_titleLabel setFont:[BUYTheme variantOptionValueFont]]; [_titleLabel setFont:[Theme variantOptionValueFont]];
[labelContainerView addSubview:_titleLabel]; [labelContainerView addSubview:_titleLabel];
_priceLabel = [[UILabel alloc] init]; _priceLabel = [[UILabel alloc] init];
_priceLabel.translatesAutoresizingMaskIntoConstraints = NO; _priceLabel.translatesAutoresizingMaskIntoConstraints = NO;
[_priceLabel setFont:[BUYTheme variantOptionPriceFont]]; [_priceLabel setFont:[Theme variantOptionPriceFont]];
[labelContainerView addSubview:_priceLabel]; [labelContainerView addSubview:_priceLabel];
UIImage *selectedImage = [BUYImageKit imageOfPreviousSelectionIndicatorImageWithFrame:CGRectMake(0, 0, 20, 20)]; UIImage *selectedImage = [ImageKit imageOfPreviousSelectionIndicatorImageWithFrame:CGRectMake(0, 0, 20, 20)];
selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
_selectedImageView = [[UIImageView alloc] initWithImage:selectedImage]; _selectedImageView = [[UIImageView alloc] initWithImage:selectedImage];
...@@ -77,7 +76,7 @@ ...@@ -77,7 +76,7 @@
[_selectedImageView setContentHuggingPriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal]; [_selectedImageView setContentHuggingPriority:UILayoutPriorityFittingSizeLevel forAxis:UILayoutConstraintAxisHorizontal];
[self.contentView addSubview:_selectedImageView]; [self.contentView addSubview:_selectedImageView];
_disclosureIndicatorImageView = [[UIImageView alloc] init]; _disclosureIndicatorImageView = [[DisclosureIndicatorView alloc] init];
_disclosureIndicatorImageView.translatesAutoresizingMaskIntoConstraints = NO; _disclosureIndicatorImageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:_disclosureIndicatorImageView]; [self.contentView addSubview:_disclosureIndicatorImageView];
...@@ -127,23 +126,28 @@ ...@@ -127,23 +126,28 @@
} }
} }
- (void)setOptionValue:(BUYOptionValue *)optionValue productVariant:(BUYProductVariant*)productVariant currencyFormatter:(NSNumberFormatter*)currencyFormatter theme:(BUYTheme *)theme -(void)tintColorDidChange
{
[super tintColorDidChange];
self.titleLabel.textColor = self.tintColor;
}
- (void)setSelectedBackgroundViewBackgroundColor:(UIColor *)selectedBackgroundViewBackgroundColor
{
self.selectedBackgroundView.backgroundColor = selectedBackgroundViewBackgroundColor;
}
- (void)setOptionValue:(BUYOptionValue *)optionValue productVariant:(BUYProductVariant*)productVariant currencyFormatter:(NSNumberFormatter*)currencyFormatter
{ {
self.titleLabel.textColor = theme.tintColor;
self.selectedImageView.tintColor = theme.tintColor;
self.backgroundColor = [theme backgroundColor];
self.selectedBackgroundView.backgroundColor = [theme selectedBackgroundColor];
self.disclosureIndicatorImageView.image = [BUYImageKit imageOfDisclosureIndicatorImageWithFrame:CGRectMake(0, 0, 10, 16) color:[theme disclosureIndicatorColor]];
self.titleLabel.text = optionValue.value; self.titleLabel.text = optionValue.value;
if (productVariant) { if (productVariant) {
if (productVariant.available) { if (productVariant.available) {
self.priceLabel.text = [currencyFormatter stringFromNumber:productVariant.price]; self.priceLabel.text = [currencyFormatter stringFromNumber:productVariant.price];
self.priceLabel.textColor = [BUYTheme variantPriceTextColor]; self.priceLabel.textColor = [Theme variantPriceTextColor];
} else { } else {
self.priceLabel.text = @"Sold Out"; self.priceLabel.text = NSLocalizedString(@"Sold Out", @"Sold out string displayed on option selector");
self.priceLabel.textColor = [BUYTheme variantSoldOutTextColor]; self.priceLabel.textColor = [Theme variantSoldOutTextColor];
} }
[NSLayoutConstraint activateConstraints:self.priceConstraints]; [NSLayoutConstraint activateConstraints:self.priceConstraints];
[NSLayoutConstraint deactivateConstraints:self.noPriceConstraints]; [NSLayoutConstraint deactivateConstraints:self.noPriceConstraints];
......
// //
// BUYVariantOptionView.h // VariantOptionView.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -26,13 +26,11 @@ ...@@ -26,13 +26,11 @@
@import UIKit; @import UIKit;
@class BUYOptionValue; @class BUYOptionValue;
#import "BUYTheme.h"
#import "BUYTheme+Additions.h"
/** /**
* A view that contains a variant option name and option value for use to display the selected product variant * A view that contains a variant option name and option value for use to display the selected product variant
*/ */
@interface BUYVariantOptionView : UIView <BUYThemeable> @interface VariantOptionView : UIView
/** /**
* Sets the text on the labels for the option value * Sets the text on the labels for the option value
...@@ -41,4 +39,6 @@ ...@@ -41,4 +39,6 @@
*/ */
- (void)setTextForOptionValue:(BUYOptionValue*)optionValue; - (void)setTextForOptionValue:(BUYOptionValue*)optionValue;
- (void)setOptionNameTextColor:(UIColor*)color UI_APPEARANCE_SELECTOR;
@end @end
// //
// BUYVariantOptionView.m // VariantOptionView.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,18 +24,21 @@ ...@@ -24,18 +24,21 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYVariantOptionView.h" @import UIKit;
#import "BUYOptionValue.h" @import Buy;
#import "Theme+Additions.h"
#import "VariantOptionView.h"
#import "UIFont+BUYAdditions.h" #import "UIFont+BUYAdditions.h"
@interface BUYVariantOptionView () @interface VariantOptionView ()
@property (nonatomic, strong) UILabel *optionNameLabel; @property (nonatomic, strong) UILabel *optionNameLabel;
@property (nonatomic, strong) UILabel *optionValueLabel; @property (nonatomic, strong) UILabel *optionValueLabel;
@end @end
@implementation BUYVariantOptionView @implementation VariantOptionView
- (instancetype)initWithFrame:(CGRect)frame - (instancetype)initWithFrame:(CGRect)frame
{ {
...@@ -47,7 +50,7 @@ ...@@ -47,7 +50,7 @@
_optionNameLabel = [[UILabel alloc] init]; _optionNameLabel = [[UILabel alloc] init];
_optionNameLabel.textColor = [UIColor colorWithWhite:0.6f alpha:1]; _optionNameLabel.textColor = [UIColor colorWithWhite:0.6f alpha:1];
_optionNameLabel.translatesAutoresizingMaskIntoConstraints = NO; _optionNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
[_optionNameLabel setFont:[BUYTheme variantOptionNameFont]]; [_optionNameLabel setFont:[Theme variantOptionNameFont]];
[self addSubview:_optionNameLabel]; [self addSubview:_optionNameLabel];
// Configure option value label // Configure option value label
...@@ -55,7 +58,7 @@ ...@@ -55,7 +58,7 @@
_optionValueLabel.textColor = self.tintColor; _optionValueLabel.textColor = self.tintColor;
_optionValueLabel.translatesAutoresizingMaskIntoConstraints = NO; _optionValueLabel.translatesAutoresizingMaskIntoConstraints = NO;
[_optionValueLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal]; [_optionValueLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[_optionValueLabel setFont:[BUYTheme variantOptionValueFont]]; [_optionValueLabel setFont:[Theme variantOptionValueFont]];
[self addSubview:_optionValueLabel]; [self addSubview:_optionValueLabel];
NSDictionary *views = NSDictionaryOfVariableBindings(_optionNameLabel, _optionValueLabel); NSDictionary *views = NSDictionaryOfVariableBindings(_optionNameLabel, _optionValueLabel);
...@@ -82,12 +85,16 @@ ...@@ -82,12 +85,16 @@
self.optionValueLabel.textColor = self.tintColor; self.optionValueLabel.textColor = self.tintColor;
} }
- (void)setTheme:(BUYTheme *)theme - (void)setBackgroundColor:(UIColor *)backgroundColor
{ {
self.optionNameLabel.textColor = [theme variantOptionNameTextColor]; [super setBackgroundColor:backgroundColor];
self.backgroundColor = [theme backgroundColor];
self.optionNameLabel.backgroundColor = self.backgroundColor; self.optionNameLabel.backgroundColor = self.backgroundColor;
self.optionValueLabel.backgroundColor = self.backgroundColor; self.optionValueLabel.backgroundColor = self.backgroundColor;
} }
- (void)setOptionNameTextColor:(UIColor*)color
{
self.optionNameLabel.textColor = color;
}
@end @end
// //
// PresentationController.h // VariantSelectionPresentationController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
/** /**
* A presentation controller containing the view controllers for variant selection * A presentation controller containing the view controllers for variant selection
*/ */
@interface BUYPresentationControllerForVariantSelection : UIPresentationController @interface VariantSelectionPresentationController : UIPresentationController
/** /**
* Blurred effects view that surrounds the variant selection navigation controller * Blurred effects view that surrounds the variant selection navigation controller
......
// //
// BUYPresentationController.m // VariantSelectionPresentationController.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,9 +24,9 @@ ...@@ -24,9 +24,9 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYPresentationControllerForVariantSelection.h" #import "VariantSelectionPresentationController.h"
@implementation BUYPresentationControllerForVariantSelection @implementation VariantSelectionPresentationController
- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController - (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController
{ {
......
// //
// BUYVariantSelectionViewController.h // VariantSelectionViewController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,13 +25,11 @@ ...@@ -25,13 +25,11 @@
// //
@import UIKit; @import UIKit;
@import Buy;
@class BUYProduct; @class VariantSelectionViewController;
@class BUYVariantSelectionViewController;
@class BUYProductVariant;
@class BUYTheme;
@protocol BUYVariantSelectionDelegate <NSObject> @protocol VariantSelectionDelegate <NSObject>
/** /**
* Called when a user selects the last option for a product variant * Called when a user selects the last option for a product variant
...@@ -39,7 +37,7 @@ ...@@ -39,7 +37,7 @@
* @param controller The displayed variant selection view controller * @param controller The displayed variant selection view controller
* @param variant The selected product variant * @param variant The selected product variant
*/ */
- (void)variantSelectionController:(BUYVariantSelectionViewController *)controller didSelectVariant:(BUYProductVariant *)variant; - (void)variantSelectionController:(VariantSelectionViewController *)controller didSelectVariant:(BUYProductVariant *)variant;
/** /**
* Called when a user cancels out of the flow * Called when a user cancels out of the flow
...@@ -47,24 +45,23 @@ ...@@ -47,24 +45,23 @@
* @param controller The displayed variant selection view controller * @param controller The displayed variant selection view controller
* @param optionIndex The index matching the option value when the user cancelled selection in the flow * @param optionIndex The index matching the option value when the user cancelled selection in the flow
*/ */
- (void)variantSelectionControllerDidCancelVariantSelection:(BUYVariantSelectionViewController *)controller atOptionIndex:(NSUInteger)optionIndex; - (void)variantSelectionControllerDidCancelVariantSelection:(VariantSelectionViewController *)controller atOptionIndex:(NSUInteger)optionIndex;
@end @end
/** /**
* The view controller that manages the option selection view controllers * The view controller that manages the option selection view controllers
*/ */
@interface BUYVariantSelectionViewController : UIViewController @interface VariantSelectionViewController : UIViewController
/** /**
* Initalizer that takes a product and theme * Initalizer that takes a product
* *
* @param product The product to display variant options for selection * @param product The product to display variant options for selection
* @param theme The current theme
* *
* @return An instance of BUYVariantSelectionViewController * @return An instance of BUYVariantSelectionViewController
*/ */
- (instancetype)initWithProduct:(BUYProduct *)product theme:(BUYTheme*)theme; - (instancetype)initWithProduct:(BUYProduct *)product;
/** /**
* The product displaying variant option for * The product displaying variant option for
...@@ -84,6 +81,6 @@ ...@@ -84,6 +81,6 @@
/** /**
* The delegate that informs the product view of selection or cancellation * The delegate that informs the product view of selection or cancellation
*/ */
@property (nonatomic, weak) id <BUYVariantSelectionDelegate> delegate; @property (nonatomic, weak) id <VariantSelectionDelegate> delegate;
@end @end
// //
// BUYVariantSelectionViewController.m // VariantSelectionViewController.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,23 +24,16 @@ ...@@ -24,23 +24,16 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import "BUYImageKit.h" #import "ImageKit.h"
#import "BUYOptionSelectionNavigationController.h" #import "OptionSelectionNavigationController.h"
#import "BUYOptionSelectionViewController.h" #import "OptionSelectionViewController.h"
#import "BUYPresentationControllerForVariantSelection.h" #import "VariantSelectionPresentationController.h"
#import "BUYProduct.h" #import "VariantSelectionViewController.h"
#import "BUYProductVariant.h" #import "OptionBreadCrumbsView.h"
#import "BUYTheme.h"
#import "BUYTheme+Additions.h" @interface VariantSelectionViewController () <OptionSelectionDelegate>
#import "BUYVariantSelectionViewController.h"
#import "BUYVariantOptionBreadCrumbsView.h"
#import "BUYOption.h"
#import "BUYOptionValue.h"
@interface BUYVariantSelectionViewController () <BUYOptionSelectionDelegate>
@property (nonatomic, strong) BUYProduct *product; @property (nonatomic, strong) BUYProduct *product;
@property (nonatomic, weak) BUYTheme *theme;
@property (nonatomic, strong) NSMutableDictionary *selectedOptions; @property (nonatomic, strong) NSMutableDictionary *selectedOptions;
@property (nonatomic, assign) BOOL changedOptionSelection; @property (nonatomic, assign) BOOL changedOptionSelection;
@property (nonatomic, strong) NSArray *filteredProductVariantsForSelectionOption; @property (nonatomic, strong) NSArray *filteredProductVariantsForSelectionOption;
...@@ -48,9 +41,9 @@ ...@@ -48,9 +41,9 @@
@end @end
@implementation BUYVariantSelectionViewController @implementation VariantSelectionViewController
- (instancetype)initWithProduct:(BUYProduct *)product theme:(BUYTheme*)theme - (instancetype)initWithProduct:(BUYProduct *)product
{ {
NSParameterAssert(product); NSParameterAssert(product);
...@@ -60,7 +53,6 @@ ...@@ -60,7 +53,6 @@
self.product = product; self.product = product;
self.selectedOptions = [NSMutableDictionary new]; self.selectedOptions = [NSMutableDictionary new];
self.optionValueNames = [NSMutableArray new]; self.optionValueNames = [NSMutableArray new];
self.theme = theme;
} }
return self; return self;
...@@ -70,23 +62,23 @@ ...@@ -70,23 +62,23 @@
{ {
[super viewDidLoad]; [super viewDidLoad];
BUYOptionSelectionViewController *controller = [self nextOptionSelectionController]; OptionSelectionViewController *controller = [self nextOptionSelectionController];
// Add close button // Add close button
UIImage *closeButton = [[BUYImageKit imageOfVariantCloseImageWithFrame:CGRectMake(0, 0, 18, 20)] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; UIImage *closeButton = [[ImageKit imageOfVariantCloseImageWithFrame:CGRectMake(0, 0, 18, 20)] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithImage:closeButton style:UIBarButtonItemStylePlain target:self action:@selector(dismissPopover)]; UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithImage:closeButton style:UIBarButtonItemStylePlain target:self action:@selector(dismissPopover)];
controller.navigationItem.leftBarButtonItem = barButtonItem; controller.navigationItem.leftBarButtonItem = barButtonItem;
[self.navigationController pushViewController:controller animated:NO]; [self.navigationController pushViewController:controller animated:NO];
BUYOptionSelectionNavigationController *navigationController = (BUYOptionSelectionNavigationController*)self.navigationController; OptionSelectionNavigationController *navigationController = (OptionSelectionNavigationController*)self.navigationController;
UIVisualEffectView *backgroundView = [(BUYPresentationControllerForVariantSelection*)navigationController.presentationController backgroundView]; UIVisualEffectView *backgroundView = [(VariantSelectionPresentationController*)navigationController.presentationController backgroundView];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissPopover)]; UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissPopover)];
[backgroundView addGestureRecognizer:tapGestureRecognizer]; [backgroundView addGestureRecognizer:tapGestureRecognizer];
} }
- (void)presentNextOption - (void)presentNextOption
{ {
BUYOptionSelectionViewController *controller = [self nextOptionSelectionController]; OptionSelectionViewController *controller = [self nextOptionSelectionController];
[self.navigationController pushViewController:controller animated:YES]; [self.navigationController pushViewController:controller animated:YES];
} }
...@@ -107,37 +99,36 @@ ...@@ -107,37 +99,36 @@
return [[self.selectedOptions allKeys] count] == 1; return [[self.selectedOptions allKeys] count] == 1;
} }
- (BUYOptionSelectionViewController *)nextOptionSelectionController - (OptionSelectionViewController *)nextOptionSelectionController
{ {
NSUInteger index = [[self.selectedOptions allKeys] count]; NSUInteger index = [[self.selectedOptions allKeys] count];
BUYOption *option = self.product.options[index]; BUYOption *option = self.product.options[index];
NSArray *options = [self.product valuesForOption:option variants:self.filteredProductVariantsForSelectionOption]; NSArray *options = [self.product valuesForOption:option variants:self.filteredProductVariantsForSelectionOption];
BUYOptionSelectionViewController *optionController = [[BUYOptionSelectionViewController alloc] initWithOptionValues:options filteredProductVariantsForSelectionOption:self.filteredProductVariantsForSelectionOption]; OptionSelectionViewController *optionController = [[OptionSelectionViewController alloc] initWithOptionValues:options filteredProductVariantsForSelectionOption:self.filteredProductVariantsForSelectionOption];
optionController.theme = self.theme;
optionController.delegate = self; optionController.delegate = self;
optionController.selectedOptionValue = self.changedOptionSelection ? nil : [self.selectedProductVariant optionValueForName:option.name]; optionController.selectedOptionValue = self.changedOptionSelection ? nil : [self.selectedProductVariant optionValueForName:option.name];
optionController.isLastOption = [self isLastOption]; optionController.isLastOption = [self isLastOption];
optionController.currencyFormatter = self.currencyFormatter; optionController.currencyFormatter = self.currencyFormatter;
optionController.title = option.name; optionController.title = option.name;
if (index > 0) { if (index > 0) {
[[(BUYOptionSelectionNavigationController*)self.navigationController breadsCrumbsView] setSelectedBuyOptionValues:[self.optionValueNames copy]]; [[(OptionSelectionNavigationController*)self.navigationController breadsCrumbsView] setSelectedBuyOptionValues:[self.optionValueNames copy]];
optionController.tableView.contentInset = optionController.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(optionController.tableView.contentInset.top + CGRectGetHeight([[(BUYOptionSelectionNavigationController*)self.navigationController breadsCrumbsView] bounds]), optionController.tableView.contentInset.left, optionController.tableView.contentInset.bottom, optionController.tableView.contentInset.right); optionController.tableView.contentInset = optionController.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(optionController.tableView.contentInset.top + CGRectGetHeight([[(OptionSelectionNavigationController*)self.navigationController breadsCrumbsView] bounds]), optionController.tableView.contentInset.left, optionController.tableView.contentInset.bottom, optionController.tableView.contentInset.right);
} }
return optionController; return optionController;
} }
- (void)dismissPopover - (void)dismissPopover
{ {
[(BUYOptionSelectionNavigationController*)self.navigationController setDismissWithCancelAnimation:YES]; [(OptionSelectionNavigationController*)self.navigationController setDismissWithCancelAnimation:YES];
if ([self.delegate respondsToSelector:@selector(variantSelectionControllerDidCancelVariantSelection:atOptionIndex:)]) { if ([self.delegate respondsToSelector:@selector(variantSelectionControllerDidCancelVariantSelection:atOptionIndex:)]) {
[self.delegate variantSelectionControllerDidCancelVariantSelection:self atOptionIndex:self.selectedOptions.count]; [self.delegate variantSelectionControllerDidCancelVariantSelection:self atOptionIndex:self.selectedOptions.count];
} }
} }
#pragma mark - BUYOptionSelectionDelegate #pragma mark - OptionSelectionDelegate
- (void)optionSelectionController:(BUYOptionSelectionViewController *)controller didSelectOptionValue:(BUYOptionValue *)optionValue - (void)optionSelectionController:(OptionSelectionViewController *)controller didSelectOptionValue:(BUYOptionValue *)optionValue
{ {
self.changedOptionSelection |= ![controller.selectedOptionValue.value isEqualToString:optionValue.value]; self.changedOptionSelection |= ![controller.selectedOptionValue.value isEqualToString:optionValue.value];
self.selectedOptions[optionValue.name] = optionValue; self.selectedOptions[optionValue.name] = optionValue;
...@@ -156,13 +147,13 @@ ...@@ -156,13 +147,13 @@
} }
} }
- (void)optionSelectionControllerDidBackOutOfChoosingOption:(BUYOptionSelectionViewController *)controller - (void)optionSelectionControllerDidBackOutOfChoosingOption:(OptionSelectionViewController *)controller
{ {
BUYOption *option = [controller.optionValues firstObject]; BUYOption *option = [controller.optionValues firstObject];
[self.selectedOptions removeObjectForKey:option.name]; [self.selectedOptions removeObjectForKey:option.name];
[self.optionValueNames removeLastObject]; [self.optionValueNames removeLastObject];
self.filteredProductVariantsForSelectionOption = controller.filteredProductVariantsForSelectionOption; self.filteredProductVariantsForSelectionOption = controller.filteredProductVariantsForSelectionOption;
[[(BUYOptionSelectionNavigationController*)self.navigationController breadsCrumbsView] setSelectedBuyOptionValues:[self.optionValueNames copy]]; [[(OptionSelectionNavigationController*)self.navigationController breadsCrumbsView] setSelectedBuyOptionValues:[self.optionValueNames copy]];
} }
- (NSArray*)filteredProductVariantsForSelectionOption - (NSArray*)filteredProductVariantsForSelectionOption
......
// //
// BUYCheckoutButton.h // NavigationController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,18 +25,13 @@ ...@@ -25,18 +25,13 @@
// //
@import UIKit; @import UIKit;
#import "BUYTheme.h"
/** /**
* A themed UIButton that includes a UIActivityIndicator * A custom navigation controller used in the BUYProductViewController, adding a close button and the ability to theme the navigation bar
*/ */
@interface BUYCheckoutButton : UIButton <BUYThemeable> @interface NavigationController : UINavigationController
/** @property (nonatomic) BOOL automaticallyMasksSeparator;
* Show a UIActivityIndicator in place of text while loading
*
* @param show Show or hide the UIActivityIndicator
*/
- (void)showActivityIndicator:(BOOL)show;
@end @end
//
// NavigationController.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "NavigationController.h"
static CGFloat const kSeperatorVisibilityCurve = 0.01f;
static void *kObservationContext = &kObservationContext;
@interface NavigationController() <UINavigationBarDelegate>
@property (strong, nonatomic) UIView *separatorMask;
@property (readonly) UIScrollView *scrollView;
@end
@implementation NavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{
self = [super initWithRootViewController:rootViewController];
if (self) {
UINavigationBar *navigationBar = self.navigationBar;
self.separatorMask = [UIView new];
self.separatorMask.backgroundColor = [UIColor whiteColor];
self.separatorMask.hidden = YES;
[navigationBar addSubview:self.separatorMask];
}
return self;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGFloat height = 1.0f / [UIScreen mainScreen].scale;
UINavigationBar *navigationBar = self.navigationBar;
self.separatorMask.frame = CGRectMake(CGRectGetMinX(navigationBar.bounds), CGRectGetMaxY(navigationBar.bounds), CGRectGetWidth(navigationBar.bounds), height);
}
- (void)setAutomaticallyMasksSeparator:(BOOL)automaticallyMasksSeparator
{
self.separatorMask.hidden = !automaticallyMasksSeparator;
}
- (void)dealloc
{
[self.scrollView removeObserver:self forKeyPath:NSStringFromSelector(@selector(contentOffset))];
}
- (UIScrollView *)scrollView
{
UIView *view = self.topViewController.view;
if ([view isKindOfClass:[UIScrollView class]]) {
return (id)view;
} else {
return [view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
return [evaluatedObject isKindOfClass:[UIScrollView class]];
}]].firstObject;
}
}
- (CGFloat)seperatorAlphaWithOffset:(CGFloat)offset
{
CGFloat alpha = offset + CGRectGetMaxY(self.navigationBar.frame);
alpha *= kSeperatorVisibilityCurve;
return 1.0f - alpha;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if (context == kObservationContext) {
UIScrollView *scrollView = object;
if (scrollView != self.scrollView) {
[scrollView removeObserver:self forKeyPath:keyPath];
} else {
CGFloat alpha = [self seperatorAlphaWithOffset:self.scrollView.contentOffset.y];
self.separatorMask.alpha = alpha;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)updateObservedValues
{
if (!self.scrollView) {
return;
}
NSString *keyPath = NSStringFromSelector(@selector(contentOffset));
[self.scrollView addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:kObservationContext];
}
#pragma mark - UINavigationBarDelegate
- (void)navigationBar:(UINavigationBar *)navigationBar didPushItem:(UINavigationItem *)item
{
[self updateObservedValues];
}
- (void)navigationBar:(UINavigationBar *)navigationBar didPopItem:(UINavigationItem *)item
{
[self updateObservedValues];
}
@end
// //
// BUYViewController.h // PaymentViewController.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -26,69 +26,69 @@ ...@@ -26,69 +26,69 @@
@import UIKit; @import UIKit;
@import PassKit; @import PassKit;
#import "BUYClient.h" @import Buy;
#import "BUYCart.h"
#import "BUYCheckout.h"
@class BUYViewController; @class BUYCheckout;
@class BUYPaymentController;
@class PaymentViewController;
@protocol BUYViewControllerDelegate <NSObject> @protocol PaymentViewControllerDelegate <NSObject>
@optional @optional
// Look into having this protocol inherit from a generic one
/** /**
* This is called if there is an error in creating the BUYCheckout. These problems include not being connected to the Internet, * This is called if there is an error in creating the BUYCheckout. These problems include not being connected to the Internet,
* or if there is a validation error in the checkout. * or if there is a validation error in the checkout.
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
* @param error An NSError describing the failure * @param error An NSError describing the failure
*/ */
- (void)controller:(BUYViewController *)controller failedToCreateCheckout:(NSError *)error; - (void)controller:(PaymentViewController *)controller failedToCreateCheckout:(NSError *)error;
/** /**
* This failure occurs when either the application is not properly configured to handle Apple Pay, * This failure occurs when either the application is not properly configured to handle Apple Pay,
* or if the user does not have any credit cards configured in PassBook or cannot add any credit cards. * or if the user does not have any credit cards configured in PassBook or cannot add any credit cards.
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
*/ */
- (void)controllerFailedToStartApplePayProcess:(BUYViewController *)controller; - (void)controllerFailedToStartApplePayProcess:(PaymentViewController *)controller;
/** /**
* This failure occurs whenever an update to BUYCheckout fails (shipping address, billing address, etc.) * This failure occurs whenever an update to BUYCheckout fails (shipping address, billing address, etc.)
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
* @param checkout The BUYCheckout * @param checkout The BUYCheckout
* @param error An NSError describing the failure * @param error An NSError describing the failure
*/ */
- (void)controller:(BUYViewController *)controller failedToUpdateCheckout:(BUYCheckout *)checkout withError:(NSError *)error; - (void)controller:(PaymentViewController *)controller failedToUpdateCheckout:(BUYCheckout *)checkout withError:(NSError *)error;
/** /**
* This failure occurs when shipping rates cannot be retrieved. * This failure occurs when shipping rates cannot be retrieved.
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
* @param checkout The BUYCheckout * @param checkout The BUYCheckout
* @param error An NSError describing the failure * @param error An NSError describing the failure
*/ */
- (void)controller:(BUYViewController *)controller failedToGetShippingRates:(BUYCheckout *)checkout withError:(NSError *)error; - (void)controller:(PaymentViewController *)controller failedToGetShippingRates:(BUYCheckout *)checkout withError:(NSError *)error;
/** /**
* This failure occurs whenever completing a checkout fails. * This failure occurs whenever completing a checkout fails.
* This can occur if there is missing payment information or if the shop is improperly configured. * This can occur if there is missing payment information or if the shop is improperly configured.
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
* @param checkout The BUYCheckout * @param checkout The BUYCheckout
* @param error An NSError describing the failure * @param error An NSError describing the failure
*/ */
- (void)controller:(BUYViewController *)controller failedToCompleteCheckout:(BUYCheckout *)checkout withError:(NSError *)error; - (void)controller:(PaymentViewController *)controller failedToCompleteCheckout:(BUYCheckout *)checkout withError:(NSError *)error;
/** /**
* This is called whenever the checkout fully completes, success or failure. * This is called whenever the checkout fully completes, success or failure.
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
* @param checkout The BUYCheckout * @param checkout The BUYCheckout
* @param status Checkout status BUYStatus * @param status Checkout status BUYStatus
*/ */
- (void)controller:(BUYViewController *)controller didCompleteCheckout:(BUYCheckout *)checkout status:(BUYStatus)status; - (void)controller:(PaymentViewController *)controller didCompleteCheckout:(BUYCheckout *)checkout status:(BUYStatus)status;
/** /**
* This is called when the Apple Pay Authorization View Controller has been dismissed. * This is called when the Apple Pay Authorization View Controller has been dismissed.
...@@ -98,25 +98,25 @@ ...@@ -98,25 +98,25 @@
* Note: If the PKPaymentAuthorizationStatus is not PKPaymentAuthorizationStatusSuccess we will expire the checkout by * Note: If the PKPaymentAuthorizationStatus is not PKPaymentAuthorizationStatusSuccess we will expire the checkout by
* calling `expireCheckout:completion:` to release the hold on the product inventory. * calling `expireCheckout:completion:` to release the hold on the product inventory.
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
* @param checkout The BUYCheckout * @param checkout The BUYCheckout
*/ */
- (void)controller:(BUYViewController *)controller didDismissApplePayControllerWithStatus:(PKPaymentAuthorizationStatus)status forCheckout:(BUYCheckout *)checkout; - (void)controller:(PaymentViewController *)controller didDismissApplePayControllerWithStatus:(PKPaymentAuthorizationStatus)status forCheckout:(BUYCheckout *)checkout;
/** /**
* This is called when the SFSafariViewController has been dismissed before checkout completion. * This is called when the SFSafariViewController has been dismissed before checkout completion.
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
* @param checkout The BUYCheckout * @param checkout The BUYCheckout
*/ */
- (void)controller:(BUYViewController *)controller didDismissWebCheckout:(BUYCheckout *)checkout; - (void)controller:(PaymentViewController *)controller didDismissWebCheckout:(BUYCheckout *)checkout;
/** /**
* The view controller has been dismissed * The view controller has been dismissed
* *
* @param controller The BUYViewController * @param controller The PaymentViewController
*/ */
- (void)didDismissViewController:(BUYViewController *)controller; - (void)didDismissViewController:(PaymentViewController *)controller;
@optional @optional
...@@ -125,26 +125,26 @@ ...@@ -125,26 +125,26 @@
* *
* @param viewController the view controller * @param viewController the view controller
*/ */
- (void)controllerWillCheckoutViaWeb:(BUYViewController *)viewController; - (void)controllerWillCheckoutViaWeb:(PaymentViewController *)viewController;
/** /**
* Called when the user chooses to checkout via Apple Pay * Called when the user chooses to checkout via Apple Pay
* *
* @param viewController the view controller * @param viewController the view controller
*/ */
- (void)controllerWillCheckoutViaApplePay:(BUYViewController *)viewController; - (void)controllerWillCheckoutViaApplePay:(PaymentViewController *)viewController;
@end @end
/** /**
* This base class guides you through the entire Apple Pay process. * This base class guides you through the entire Apple Pay process.
*/ */
@interface BUYViewController : UIViewController <PKPaymentAuthorizationViewControllerDelegate> @interface PaymentViewController : UIViewController <BUYPaymentProviderDelegate>
/** /**
* Register yourself as a BUYViewControllerDelegate to handle all errors, and status changes. * Register yourself as a PaymentViewControllerDelegate to handle all errors, and status changes.
*/ */
@property (nonatomic, weak) id <BUYViewControllerDelegate> delegate; @property (nonatomic, weak) id <PaymentViewControllerDelegate> delegate;
/** /**
* Set the BUYClient using the provided initializer method `initWithClient:` or * Set the BUYClient using the provided initializer method `initWithClient:` or
...@@ -203,14 +203,15 @@ ...@@ -203,14 +203,15 @@
/** /**
* The current checkout object * The current checkout object
*/ */
@property (nonatomic, strong, readonly) BUYCheckout *checkout; @property (nonatomic, strong) BUYCheckout *checkout;
/** /**
* Loads the shop details * Loads the shop details
* *
* @param block callback block called on completion * @param block callback block called on completion
*/ */
- (void)loadShopWithCallback:(void (^)(BOOL, NSError *))block; - (void)loadShopWithCallback:(void (^)(BOOL, NSError *))block NS_DEPRECATED_IOS(8_0, 9_0);
#pragma mark - Apple Pay Overrides #pragma mark - Apple Pay Overrides
...@@ -235,11 +236,11 @@ ...@@ -235,11 +236,11 @@
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
/** /**
* Creates a BUYViewController using your * Creates a PaymentViewController using your
* *
* @param client A BUYClient configured to your shop * @param client A BUYClient configured to your shop
* *
* @return A BUYViewController * @return A PaymentViewController
*/ */
- (instancetype)initWithClient:(BUYClient *)client; - (instancetype)initWithClient:(BUYClient *)client;
...@@ -285,4 +286,6 @@ ...@@ -285,4 +286,6 @@
*/ */
+ (void)completeCheckoutFromLaunchURL:(NSURL *)url; + (void)completeCheckoutFromLaunchURL:(NSURL *)url;
@property (nonatomic, strong) BUYPaymentController *paymentController;
@end @end
//
// PaymentViewController.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import AddressBook;
@import UIKit;
@import Buy;
#import "PaymentViewController.h"
@implementation PaymentViewController
@synthesize client = _client;
- (instancetype)initWithClient:(BUYClient *)client
{
self = [super init];
if (self) {
self.client = client;
}
return self;
}
- (BUYClient*)client
{
if (_client == nil) {
NSLog(@"`BUYClient` has not been initialized. Please initialize PaymentViewController with `initWithClient:` or set a `BUYClient` after Storyboard initialization");
}
return _client;
}
- (void)loadShopWithCallback:(void (^)(BOOL, NSError *))block
{
// Deprecated
}
- (BOOL)canShowApplePaySetup
{
return self.applePayPaymentProvider.canShowApplePaySetup;
}
- (BOOL)isApplePayAvailable
{
return self.applePayPaymentProvider.available;
}
- (BOOL)shouldShowApplePayButton {
return self.applePayPaymentProvider.available || [self canShowApplePaySetup];
}
- (BOOL)shouldShowApplePaySetup
{
return self.applePayPaymentProvider.available == NO && [self canShowApplePaySetup];
}
#pragma mark - Payment
- (void)startApplePayCheckout:(BUYCheckout *)checkout
{
[self.paymentController startCheckout:checkout withProviderType:BUYApplePayPaymentProviderId];
}
- (void)startWebCheckout:(BUYCheckout *)checkout
{
[self.paymentController startCheckout:checkout withProviderType:BUYWebPaymentProviderId];
}
- (BUYPaymentController *)paymentController
{
if (_paymentController == nil) {
_paymentController = [[BUYPaymentController alloc] init];
BUYWebCheckoutPaymentProvider *webPaymentProvider = [[BUYWebCheckoutPaymentProvider alloc] initWithClient:self.client];
webPaymentProvider.delegate = self;
[_paymentController addPaymentProvider:webPaymentProvider];
if (self.merchantId.length) {
BUYApplePayPaymentProvider *applePayPaymentProvider = [[BUYApplePayPaymentProvider alloc] initWithClient:self.client merchantID:self.merchantId];
applePayPaymentProvider.delegate = self;
[_paymentController addPaymentProvider:applePayPaymentProvider];
}
}
return _paymentController;
}
- (BUYWebCheckoutPaymentProvider *)webPaymentProvider
{
return (BUYWebCheckoutPaymentProvider*)[self.paymentController providerForType:BUYWebPaymentProviderId];
}
- (BUYApplePayPaymentProvider *)applePayPaymentProvider
{
return (BUYApplePayPaymentProvider*)[self.paymentController providerForType:BUYApplePayPaymentProviderId];
}
#pragma mark - Creating a Checkout using a Cart Token
- (void)startCheckoutWithCartToken:(NSString *)token
{
[self.client createCheckoutWithCartToken:token completion:^(BUYCheckout *checkout, NSError *error) {
[self handleCheckoutCompletion:checkout error:error];
}];
}
- (void)handleCheckoutCompletion:(BUYCheckout *)checkout error:(NSError *)error
{
if (checkout && error == nil) {
self.checkout = checkout;
[self.applePayPaymentProvider startCheckout:checkout];
}
else {
if ([self.delegate respondsToSelector:@selector(controller:failedToCreateCheckout:)]) {
[self.delegate controller:self failedToCreateCheckout:error];
}
}
}
- (void)checkoutCompleted:(BUYCheckout *)checkout status:(BUYStatus)status
{
if ([self.delegate respondsToSelector:@selector(controller:didCompleteCheckout:status:)]) {
[self.delegate controller:self didCompleteCheckout:checkout status:status];
}
}
#pragma mark - Payment delegate methods
+ (void)completeCheckoutFromLaunchURL:(NSURL *)url
{
[[NSNotificationCenter defaultCenter] postNotificationName:BUYSafariCallbackURLNotification object:nil userInfo:@{BUYURLKey: url}];
}
- (void)paymentProviderWillStartCheckout:(id<BUYPaymentProvider>)provider
{
if ([self.delegate respondsToSelector:@selector(controllerWillCheckoutViaWeb:)]) {
[self.delegate controllerWillCheckoutViaWeb:self];
}
}
- (void)paymentProviderDidDismissCheckout:(id<BUYPaymentProvider>)provider
{
if ([self.delegate respondsToSelector:@selector(controller:didDismissWebCheckout:)]) {
[self.delegate controller:self didDismissWebCheckout:self.checkout];
}
}
- (void)paymentProvider:(id<BUYPaymentProvider>)provider didFailCheckoutWithError:(NSError *)error
{
if ([self.delegate respondsToSelector:@selector(controller:failedToCreateCheckout:)]) {
[self.delegate controller:self failedToCreateCheckout:error];
}
}
- (void)paymentProvider:(id<BUYPaymentProvider>)provider didCompleteCheckout:(BUYCheckout *)checkout withStatus:(BUYStatus)status
{
if ([self.delegate respondsToSelector:@selector(controller:didCompleteCheckout:status:)]) {
[self.delegate controller:self didCompleteCheckout:checkout status:status];
}
}
- (void)paymentProvider:(id<BUYPaymentProvider>)provider wantsControllerPresented:(UIViewController *)controller
{
[self presentViewController:controller animated:YES completion:nil];
}
- (void)paymentProviderWantsControllerDismissed:(id<BUYPaymentProvider>)provider
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(PKPaymentRequest *)paymentRequest
{
return nil;
}
@end
...@@ -27,10 +27,6 @@ ...@@ -27,10 +27,6 @@
@import UIKit; @import UIKit;
@import Buy; @import Buy;
#warning - Enter your merchant ID
// Adding a merchant ID will show Apple Pay in the BUYProductViewController (on supported devices)
#define MERCHANT_ID @""
@interface ProductListViewController : UITableViewController @interface ProductListViewController : UITableViewController
- (instancetype)initWithClient:(BUYClient *)client collection:(BUYCollection*)collection; - (instancetype)initWithClient:(BUYClient *)client collection:(BUYCollection*)collection;
......
...@@ -25,11 +25,19 @@ ...@@ -25,11 +25,19 @@
// //
#import "ProductListViewController.h" #import "ProductListViewController.h"
#import "ProductViewController.h"
#import "Theme.h"
#import "ShippingRatesTableViewController.h" #import "ShippingRatesTableViewController.h"
#import "ProductViewControllerToggleTableViewCell.h" #import "ProductViewControllerToggleTableViewCell.h"
#import "ProductViewControllerThemeStyleTableViewCell.h" #import "ProductViewControllerThemeStyleTableViewCell.h"
#import "ProductViewControllerThemeTintColorTableViewCell.h" #import "ProductViewControllerThemeTintColorTableViewCell.h"
#import <Buy/Buy.h>
//#warning - Enter your merchant ID
// Adding a merchant ID will show Apple Pay in the BUYProductViewController (on supported devices)
#define MERCHANT_ID @""
@interface ProductListViewController () <UIViewControllerPreviewingDelegate> @interface ProductListViewController () <UIViewControllerPreviewingDelegate>
@property (nonatomic, strong) BUYClient *client; @property (nonatomic, strong) BUYClient *client;
...@@ -39,7 +47,7 @@ ...@@ -39,7 +47,7 @@
@property (nonatomic, strong) NSURLSessionDataTask *checkoutCreationTask; @property (nonatomic, strong) NSURLSessionDataTask *checkoutCreationTask;
@property (nonatomic, assign) BOOL demoProductViewController; @property (nonatomic, assign) BOOL demoProductViewController;
@property (nonatomic, assign) BUYThemeStyle themeStyle; @property (nonatomic, assign) ThemeStyle themeStyle;
@property (nonatomic, strong) NSArray *themeTintColors; @property (nonatomic, strong) NSArray *themeTintColors;
@property (nonatomic, assign) NSInteger themeTintColorSelectedIndex; @property (nonatomic, assign) NSInteger themeTintColorSelectedIndex;
@property (nonatomic, assign) BOOL showsProductImageBackground; @property (nonatomic, assign) BOOL showsProductImageBackground;
...@@ -275,7 +283,7 @@ ...@@ -275,7 +283,7 @@
[self.checkoutCreationTask cancel]; [self.checkoutCreationTask cancel];
} }
BUYCart *cart = [[BUYCart alloc] init]; BUYCart *cart = [self.client.modelManager insertCartWithJSONDictionary:nil];
[cart addVariant:product.variants.firstObject]; [cart addVariant:product.variants.firstObject];
BUYCheckout *checkout = [[BUYCheckout alloc] initWithCart:cart]; BUYCheckout *checkout = [[BUYCheckout alloc] initWithCart:cart];
...@@ -304,7 +312,7 @@ ...@@ -304,7 +312,7 @@
- (void)demoProductViewControllerWithProduct:(BUYProduct*)product - (void)demoProductViewControllerWithProduct:(BUYProduct*)product
{ {
BUYProductViewController *productViewController = [self productViewController]; ProductViewController *productViewController = [self productViewController];
[productViewController loadWithProduct:product completion:^(BOOL success, NSError *error) { [productViewController loadWithProduct:product completion:^(BOOL success, NSError *error) {
if (error == nil) { if (error == nil) {
if (self.presentViewController) { if (self.presentViewController) {
...@@ -316,13 +324,13 @@ ...@@ -316,13 +324,13 @@
}]; }];
} }
-(BUYProductViewController*)productViewController -(ProductViewController*)productViewController
{ {
BUYTheme *theme = [BUYTheme new]; Theme *theme = [Theme new];
theme.style = self.themeStyle; theme.style = self.themeStyle;
theme.tintColor = self.themeTintColors[self.themeTintColorSelectedIndex]; theme.tintColor = self.themeTintColors[self.themeTintColorSelectedIndex];
theme.showsProductImageBackground = self.showsProductImageBackground; theme.showsProductImageBackground = self.showsProductImageBackground;
BUYProductViewController *productViewController = [[BUYProductViewController alloc] initWithClient:self.client theme:theme]; ProductViewController *productViewController = [[ProductViewController alloc] initWithClient:self.client theme:theme];
productViewController.merchantId = MERCHANT_ID; productViewController.merchantId = MERCHANT_ID;
return productViewController; return productViewController;
} }
...@@ -362,7 +370,7 @@ ...@@ -362,7 +370,7 @@
- (BUYAddress *)address - (BUYAddress *)address
{ {
BUYAddress *address = [[BUYAddress alloc] init]; BUYAddress *address = [self.client.modelManager insertAddressWithJSONDictionary:nil];
address.address1 = @"150 Elgin Street"; address.address1 = @"150 Elgin Street";
address.address2 = @"8th Floor"; address.address2 = @"8th Floor";
address.city = @"Ottawa"; address.city = @"Ottawa";
...@@ -387,7 +395,7 @@ ...@@ -387,7 +395,7 @@
} }
BUYProduct *product = self.products[indexPath.row]; BUYProduct *product = self.products[indexPath.row];
BUYProductViewController *productViewController = [self productViewController]; ProductViewController *productViewController = [self productViewController];
[productViewController loadWithProduct:product completion:NULL]; [productViewController loadWithProduct:product completion:NULL];
previewingContext.sourceRect = cell.frame; previewingContext.sourceRect = cell.frame;
......
// //
// BUYStoreViewController.h // UIColor+BUYAdditions.h
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -25,60 +25,35 @@ ...@@ -25,60 +25,35 @@
// //
@import UIKit; @import UIKit;
#import "BUYViewController.h"
#import "BUYError.h"
typedef NS_ENUM(NSUInteger, BUYCheckoutType){ #define BUY_RGB(r, g, b) BUY_RGBA(r, g, b, 1)
BUYCheckoutTypeNormal, #define BUY_RGBA(r, g, b, a) [UIColor colorWithRed:r/255.0f green:g/255.0f blue:b/255.0f alpha:a]
BUYCheckoutTypeApplePay,
BUYCheckoutTypeCancel
};
typedef void (^BUYCheckoutTypeBlock)(BUYCheckoutType type); @interface UIColor (BUYAdditions)
@class BUYStoreViewController;
/**
* The BUYStoreViewControllerDelegate protocol implements methods related to the checkout process
*/
@protocol BUYStoreViewControllerDelegate <BUYViewControllerDelegate>
/** /**
* Tells the delegate that the user has proceeded to checkout. Use this opportunity to present an interface to a * Check if the color is light (brightness of colors combined less than .5)
* user to choose between checking out with Apple Pay or standard webcheckout. * Supports RGB and grey space colors
* Note: Before presenting an option for Apple Pay, check if the device is setup to do so by calling `[PKPaymentAuthorizationViewController canMakePayments]`
* *
* @param controller The controller with an embedded WKWebView displaying the Shopify store * @return Returns true if the color is light
* @param completionHandler (^BUYCheckoutTypeBlock)(BUYCheckoutType type);
*/ */
- (void)controller:(BUYStoreViewController *)controller shouldProceedWithCheckoutType:(BUYCheckoutTypeBlock)completionHandler; - (BOOL)isLightColor;
@end
/** /**
* This controller shows you how to build a controller that embeds a WKWebView (iOS 8+) with your store in it. * Creates a UIColor from a hex value
* This means that you can show off your store and its sleek responsive design without having to build a full native app to showcase it.
*/
@interface BUYStoreViewController : BUYViewController
/**
* Creates a view controller with an embedded webview to show the shops storefront
* *
* @param client The client configured to your shop * @param hex the hex value to convert to a UIColor
* @param url The address where the shop can be viewed
* *
* @return the BUYStoreViewController instance * @return A UIColor object
*/
- (instancetype)initWithClient:(BUYClient *)client url:(NSURL *)url;
/**
* Jumps back to the shops home page
*/ */
- (void)reloadHomePage; + (UIColor*)colorWithHex:(NSInteger)hex;
+ (UIColor*)colorWithHexString:(NSString *)hexString;
/** /**
* The BUYStoreViewControllerDelegate * Creates a string representing the hex value for the color
*
* @return String representing the hex value
*/ */
@property (nonatomic, weak) id <BUYStoreViewControllerDelegate> delegate; - (NSString *)hexString;
@end @end
//
// UIColor+BUYAdditions.m
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#import "UIColor+BUYAdditions.h"
@implementation UIColor (BUYAdditions)
-(BOOL)isLightColor
{
CGFloat colorBrightness = 0;
CGColorSpaceRef colorSpace = CGColorGetColorSpace(self.CGColor);
CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);
if (colorSpaceModel == kCGColorSpaceModelRGB) {
const CGFloat *componentColors = CGColorGetComponents(self.CGColor);
colorBrightness = ((componentColors[0] * 299) + (componentColors[1] * 587) + (componentColors[2] * 114)) / 1000;
} else {
[self getWhite:&colorBrightness alpha:0];
}
return (colorBrightness >= .5f);
}
+ (UIColor*)colorWithHex:(NSInteger)hex
{
return [UIColor colorWithRed:(((hex & 0xFF0000) >> 16)) / 255.0f
green:(((hex & 0x00FF00) >> 8)) / 255.0f
blue:(((hex & 0x0000FF) >> 0)) / 255.0f
alpha:1.0];
}
+ (UIColor*)colorWithHexString:(NSString *)hexString
{
if ([hexString length] == 0) {
return nil;
}
NSScanner *scanner = [NSScanner scannerWithString:hexString];
unsigned hex;
[scanner scanHexInt:&hex];
return [UIColor colorWithHex:hex];
}
- (NSString *)hexString
{
CGFloat r;
CGFloat g;
CGFloat b;
[self getRed:&r green:&g blue:&b alpha:nil];
return [NSString stringWithFormat:@"#%02lX%02lX%02lX",
lround(r * 255),
lround(g * 255),
lround(b * 255)];
}
@end
//
// BUYFont.h
// Mobile Buy SDK
//
// Created by Shopify.
// Copyright (c) 2015 Shopify Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
@import UIKit;
@interface UIFont (BUYAdditions)
/**
* Class method to allow system fonts to have increased point sizes from the OS default.
*
* @param style The text style for the font.
* @param size A positive value to increase the default font's point size by.
*
* @return A system font with an optionally increased point size.
*/
+ (UIFont *)preferredFontForTextStyle:(NSString *)style increasedPointSize:(CGFloat)size;
@end
// //
// BUYFontAdditionsTests.m // BUYFont.m
// Mobile Buy SDK // Mobile Buy SDK
// //
// Created by Shopify. // Created by Shopify.
...@@ -24,27 +24,14 @@ ...@@ -24,27 +24,14 @@
// THE SOFTWARE. // THE SOFTWARE.
// //
#import <XCTest/XCTest.h>
#import "UIFont+BUYAdditions.h" #import "UIFont+BUYAdditions.h"
@interface BUYFontAdditionsTests : XCTestCase @implementation UIFont (BUYAdditions)
@end + (UIFont *)preferredFontForTextStyle:(NSString *)style increasedPointSize:(CGFloat)size
@implementation BUYFontAdditionsTests
- (void)testIncreasedFontSize
{ {
static CGFloat const increase = 2.0; UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:style];
UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody]; return [UIFont fontWithDescriptor:descriptor size:descriptor.pointSize + size];
CGFloat newSize = font.pointSize + increase;
NSMutableDictionary *attributes = [font.fontDescriptor.fontAttributes mutableCopy];
attributes[UIFontDescriptorSizeAttribute] = @(newSize);
UIFont *expected = [UIFont fontWithDescriptor:[UIFontDescriptor fontDescriptorWithFontAttributes:attributes] size:newSize];
UIFont *actual = [UIFont preferredFontForTextStyle:UIFontTextStyleBody increasedPointSize:increase];
XCTAssertEqualObjects(actual, expected);
} }
@end @end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment