diff --git a/GoogleMobileAds.framework/GoogleMobileAds b/GoogleMobileAds.framework/GoogleMobileAds new file mode 120000 index 0000000..95e839f --- /dev/null +++ b/GoogleMobileAds.framework/GoogleMobileAds @@ -0,0 +1 @@ +Versions/Current/GoogleMobileAds \ No newline at end of file diff --git a/GoogleMobileAds.framework/Headers b/GoogleMobileAds.framework/Headers new file mode 120000 index 0000000..a177d2a --- /dev/null +++ b/GoogleMobileAds.framework/Headers @@ -0,0 +1 @@ +Versions/Current/Headers \ No newline at end of file diff --git a/GoogleMobileAds.framework/Modules/module.modulemap b/GoogleMobileAds.framework/Modules/module.modulemap new file mode 100755 index 0000000..8eac697 --- /dev/null +++ b/GoogleMobileAds.framework/Modules/module.modulemap @@ -0,0 +1,20 @@ +framework module GoogleMobileAds { + umbrella header "GoogleMobileAds.h" + + export * + module * { export * } + + link framework "AdSupport" + link framework "AudioToolbox" + link framework "AVFoundation" + link framework "CoreGraphics" + link framework "CoreMedia" + link framework "CoreTelephony" + link framework "EventKit" + link framework "EventKitUI" + link framework "Foundation" + link framework "MessageUI" + link framework "StoreKit" + link framework "SystemConfiguration" + link framework "UIKit" +} diff --git a/GoogleMobileAds.framework/Versions/A/GoogleMobileAds b/GoogleMobileAds.framework/Versions/A/GoogleMobileAds new file mode 100755 index 0000000..5f61114 Binary files /dev/null and b/GoogleMobileAds.framework/Versions/A/GoogleMobileAds differ diff --git a/GoogleMobileAds.framework/Versions/A/Headers/DFPBannerView.h b/GoogleMobileAds.framework/Versions/A/Headers/DFPBannerView.h new file mode 100755 index 0000000..1e45302 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/DFPBannerView.h @@ -0,0 +1,83 @@ +// +// DFPBannerView.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import +#import +#import + +@protocol DFPCustomRenderedBannerViewDelegate; +@protocol GADAdSizeDelegate; +@protocol GADAppEventDelegate; + +/// The view that displays DoubleClick For Publishers banner ads. +@interface DFPBannerView : GADBannerView + +/// Required value created on the DFP website. Create a new ad unit for every unique placement of an +/// ad in your application. Set this to the ID assigned for this placement. Ad units are important +/// for targeting and statistics. +/// +/// Example DFP ad unit ID: @"/6499/example/banner" +@property(nonatomic, copy) NSString *adUnitID; + +/// Optional delegate that is notified when creatives send app events. +@property(nonatomic, weak) IBOutlet id appEventDelegate; + +/// Optional delegate that is notified when creatives cause the banner to change size. To avoid +/// crashing the app, remember to nil this property before releasing the object that implements the +/// GADAdSizeDelegate protocol. +@property(nonatomic, weak) IBOutlet id adSizeDelegate; + +/// Optional array of NSValue encoded GADAdSize structs, specifying all valid sizes that are +/// appropriate for this slot. Never create your own GADAdSize directly. Use one of the predefined +/// standard ad sizes (such as kGADAdSizeBanner), or create one using the GADAdSizeFromCGSize +/// method. +/// +/// \see setValidAdSizesWithSizes: +/// +/// Example: +/// \code +/// NSArray *validSizes = @[ +/// NSValueFromGADAdSize(kGADAdSizeBanner), +/// NSValueFromGADAdSize(kGADAdSizeLargeBanner) +/// ]; +/// +/// bannerView.validAdSizes = validSizes; +/// \endcode +@property(nonatomic, copy) NSArray *validAdSizes; + +/// Correlator object for correlating this object to other ad objects. +@property(nonatomic, strong) GADCorrelator *correlator; + +/// Indicates that the publisher will record impressions manually when the ad becomes visible to the +/// user. +@property(nonatomic, assign) BOOL enableManualImpressions; + +/// Optional delegate object for custom rendered ads. +@property(nonatomic, weak) + IBOutlet id customRenderedBannerViewDelegate; + +/// If you've set enableManualImpressions to YES, call this method when the ad is visible. +- (void)recordImpression; + +/// Use this function to resize the banner view without launching a new ad request. +- (void)resize:(GADAdSize)size; + +#pragma mark Deprecated + +/// Sets the receiver's valid ad sizes to the values pointed to by the provided NULL terminated list +/// of GADAdSize pointers. +/// +/// Example: +/// \code +/// GADAdSize size1 = kGADAdSizeBanner; +/// GADAdSize size2 = kGADAdSizeLargeBanner; +/// [bannerView setValidAdSizesWithSizes:&size1, &size2, NULL]; +/// \endcode +- (void)setValidAdSizesWithSizes:(GADAdSize *)firstSize, ... NS_REQUIRES_NIL_TERMINATION + GAD_DEPRECATED_MSG_ATTRIBUTE("Use validAdSizes property."); + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedAd.h b/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedAd.h new file mode 100755 index 0000000..c4543cc --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedAd.h @@ -0,0 +1,28 @@ +// +// DFPCustomRenderedAd.h +// Google Mobile Ads SDK +// +// Copyright 2014 Google Inc. All rights reserved. +// + +#import + +/// Custom rendered ad. Your application renders the ad. +@interface DFPCustomRenderedAd : NSObject + +/// The ad's HTML. +@property(nonatomic, copy, readonly) NSString *adHTML; + +/// The base URL of the ad's HTML. +@property(nonatomic, copy, readonly) NSURL *adBaseURL; + +/// Call this method when the user clicks the ad. +- (void)recordClick; + +/// Call this method when the ad is visible to the user. +- (void)recordImpression; + +/// Call this method after the ad has been rendered in a UIView object. +- (void)finishedRenderingAdView:(UIView *)view; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedBannerViewDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedBannerViewDelegate.h new file mode 100755 index 0000000..59f1965 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedBannerViewDelegate.h @@ -0,0 +1,20 @@ +// +// DFPCustomRenderedBannerViewDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2014 Google Inc. All rights reserved. +// + +#import + +@class DFPBannerView; +@class DFPCustomRenderedAd; + +@protocol DFPCustomRenderedBannerViewDelegate + +/// Called after ad data has been received. You must construct a banner from |customRenderedAd| and +/// call the |customRenderedAd| object's finishedRenderingAdView: when the ad is rendered. +- (void)bannerView:(DFPBannerView *)bannerView + didReceiveCustomRenderedAd:(DFPCustomRenderedAd *)customRenderedAd; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedInterstitialDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedInterstitialDelegate.h new file mode 100755 index 0000000..fda75a2 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/DFPCustomRenderedInterstitialDelegate.h @@ -0,0 +1,21 @@ +// +// DFPCustomRenderedInterstitialDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2014 Google Inc. All rights reserved. +// + +#import + +@class DFPCustomRenderedAd; +@class DFPInterstitial; + +@protocol DFPCustomRenderedInterstitialDelegate + +/// Called after ad data has been received. You must construct an interstitial from +/// |customRenderedAd| and call the |customRenderedAd| object's finishedRenderingAdView: method when +/// the ad has been rendered. +- (void)interstitial:(DFPInterstitial *)interstitial + didReceiveCustomRenderedAd:(DFPCustomRenderedAd *)customRenderedAd; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/DFPInterstitial.h b/GoogleMobileAds.framework/Versions/A/Headers/DFPInterstitial.h new file mode 100755 index 0000000..27112eb --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/DFPInterstitial.h @@ -0,0 +1,33 @@ +// +// DFPInterstitial.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import +#import + +@protocol DFPCustomRenderedInterstitialDelegate; +@protocol GADAppEventDelegate; + +@interface DFPInterstitial : GADInterstitial + +/// Required value created on the DFP website. Create a new ad unit for every unique placement of an +/// ad in your application. Set this to the ID assigned for this placement. Ad units are important +/// for targeting and stats. +/// +/// Example DFP ad unit ID: @"/6499/example/interstitial" +@property(nonatomic, readonly, copy) NSString *adUnitID; + +/// Correlator object for correlating this object to other ad objects. +@property(nonatomic, strong) GADCorrelator *correlator; + +/// Optional delegate that is notified when creatives send app events. +@property(nonatomic, weak) id appEventDelegate; + +/// Optional delegate object for custom rendered ads. +@property(nonatomic, weak) + id customRenderedInterstitialDelegate; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/DFPRequest.h b/GoogleMobileAds.framework/Versions/A/Headers/DFPRequest.h new file mode 100755 index 0000000..5d41aea --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/DFPRequest.h @@ -0,0 +1,33 @@ +// +// DFPRequest.h +// Google Mobile Ads SDK +// +// Copyright 2014 Google Inc. All rights reserved. +// + +#import + +#import +#import + +/// Add this constant to the testDevices property's array to receive test ads on the simulator. +GAD_EXTERN const id kDFPSimulatorID; + +/// Specifies optional parameters for ad requests. +@interface DFPRequest : GADRequest + +/// Publisher provided user ID. +@property(nonatomic, copy) NSString *publisherProvidedID; + +/// Array of strings used to exclude specified categories in ad results. +@property(nonatomic, copy) NSArray *categoryExclusions; + +/// Key-value pairs used for custom targeting. +@property(nonatomic, copy) NSDictionary *customTargeting; + +/// This API is deprecated and a no-op, use an instance of GADCorrelator set on DFPInterstitial or +/// DFPBannerView objects to correlate requests. ++ (void)updateCorrelator GAD_DEPRECATED_MSG_ATTRIBUTE( + "Set GADCorrelator objects on your ads instead. This method longer affects ad correlation."); + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoader.h b/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoader.h new file mode 100755 index 0000000..673fce9 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoader.h @@ -0,0 +1,42 @@ +// +// GADAdLoader.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +#import "GADAdLoaderDelegate.h" +#import "GADRequest.h" +#import "GADRequestError.h" + +/// Loads ads. See GADAdLoaderAdTypes.h for available ad types. +@interface GADAdLoader : NSObject + +/// Object notified when an ad request succeeds or fails. Must conform to requested ad types' +/// delegate protocols. +@property(nonatomic, weak) id delegate; + +/// Returns an initialized ad loader configured to load the specified ad types. +/// +/// @param rootViewController The root view controller is used to present ad click actions. Cannot +/// be nil. +/// @param adTypes An array of ad types. See GADAdLoaderAdTypes.h for available ad types. +/// @param options An array of GADAdLoaderOptions objects to configure how ads are loaded, or nil to +/// use default options. See each ad type's header for available GADAdLoaderOptions subclasses. +- (instancetype)initWithAdUnitID:(NSString *)adUnitID + rootViewController:(UIViewController *)rootViewController + adTypes:(NSArray *)adTypes + options:(NSArray *)options; + +/// Loads the ad and informs the delegate of the outcome. +- (void)loadRequest:(GADRequest *)request; + +@end + +/// Ad loader options base class. See each ad type's header for available GADAdLoaderOptions +/// subclasses. +@interface GADAdLoaderOptions : NSObject +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoaderAdTypes.h b/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoaderAdTypes.h new file mode 100755 index 0000000..1b1eaeb --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoaderAdTypes.h @@ -0,0 +1,22 @@ +// +// GADAdLoaderAdTypes.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +#import + +// For use with GADAdLoader's creation methods. See the constants' respective headers for each ad +// type's delegate requirements. + +/// Native app install ad type. \see GADNativeAppInstallAd.h. +GAD_EXTERN NSString *const kGADAdLoaderAdTypeNativeAppInstall; + +/// Native content ad type. \see GADNativeContentAd.h. +GAD_EXTERN NSString *const kGADAdLoaderAdTypeNativeContent; + +/// Native custom template ad type. \see GADNativeCustomTemplateAd.h. +GAD_EXTERN NSString *const kGADAdLoaderAdTypeNativeCustomTemplate; diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoaderDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoaderDelegate.h new file mode 100755 index 0000000..1be8884 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADAdLoaderDelegate.h @@ -0,0 +1,21 @@ +// +// GADAdLoaderDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +#import "GADRequestError.h" + +@class GADAdLoader; + +/// Base ad loader delegate protocol. Ad types provide extended protocols that declare methods to +/// handle successful ad loads. +@protocol GADAdLoaderDelegate + +/// Called when adLoader fails to load an ad. +- (void)adLoader:(GADAdLoader *)adLoader didFailToReceiveAdWithError:(GADRequestError *)error; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADAdNetworkExtras.h b/GoogleMobileAds.framework/Versions/A/Headers/GADAdNetworkExtras.h new file mode 100755 index 0000000..d33de74 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADAdNetworkExtras.h @@ -0,0 +1,16 @@ +// +// GADAdNetworkExtras.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +/// An object implementing this protocol contains information set by the publisher on the client +/// device for a particular ad network. +/// +/// Ad networks should create an 'extras' object implementing this protocol for their publishers to +/// use. +@protocol GADAdNetworkExtras +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADAdReward.h b/GoogleMobileAds.framework/Versions/A/Headers/GADAdReward.h new file mode 100755 index 0000000..5657a21 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADAdReward.h @@ -0,0 +1,23 @@ +// +// GADAdReward.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +@interface GADAdReward : NSObject + +/// Type of the reward. +@property(nonatomic, readonly, copy) NSString *type; + +/// Amount rewarded to the user. +@property(nonatomic, readonly, copy) NSDecimalNumber *amount; + +/// Returns an initialized GADAdReward with the provided reward type and reward amount. rewardType +/// and rewardAmount must not be nil. +- (instancetype)initWithRewardType:(NSString *)rewardType + rewardAmount:(NSDecimalNumber *)rewardAmount NS_DESIGNATED_INITIALIZER; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADAdSize.h b/GoogleMobileAds.framework/Versions/A/Headers/GADAdSize.h new file mode 100755 index 0000000..32efccd --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADAdSize.h @@ -0,0 +1,105 @@ +// +// GADAdSize.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import +#import + +#import + +/// A valid GADAdSize is considered to be one of the predefined GADAdSize constants or a GADAdSize +/// constructed by GADAdSizeFromCGSize, GADAdSizeFullWidthPortraitWithHeight, +/// GADAdSizeFullWidthLandscapeWithHeight. +/// +/// Do not create a GADAdSize manually. Use one of the kGADAdSize constants. Treat GADAdSize as an +/// opaque type. Do not access any fields directly. To obtain a concrete CGSize, use the function +/// CGSizeFromGADAdSize(). +typedef struct GADAdSize { + CGSize size; + NSUInteger flags; +} GADAdSize; + +#pragma mark Standard Sizes + +/// iPhone and iPod Touch ad size. Typically 320x50. +GAD_EXTERN GADAdSize const kGADAdSizeBanner; + +/// Taller version of kGADAdSizeBanner. Typically 320x100. +GAD_EXTERN GADAdSize const kGADAdSizeLargeBanner; + +/// Medium Rectangle size for the iPad (especially in a UISplitView's left pane). Typically 300x250. +GAD_EXTERN GADAdSize const kGADAdSizeMediumRectangle; + +/// Full Banner size for the iPad (especially in a UIPopoverController or in +/// UIModalPresentationFormSheet). Typically 468x60. +GAD_EXTERN GADAdSize const kGADAdSizeFullBanner; + +/// Leaderboard size for the iPad. Typically 728x90. +GAD_EXTERN GADAdSize const kGADAdSizeLeaderboard; + +/// Skyscraper size for the iPad. Mediation only. AdMob/Google does not offer this size. Typically +/// 120x600. +GAD_EXTERN GADAdSize const kGADAdSizeSkyscraper; + +/// An ad size that spans the full width of the application in portrait orientation. The height is +/// typically 50 pixels on an iPhone/iPod UI, and 90 pixels tall on an iPad UI. +GAD_EXTERN GADAdSize const kGADAdSizeSmartBannerPortrait; + +/// An ad size that spans the full width of the application in landscape orientation. The height is +/// typically 32 pixels on an iPhone/iPod UI, and 90 pixels tall on an iPad UI. +GAD_EXTERN GADAdSize const kGADAdSizeSmartBannerLandscape; + +/// An ad size that spans the full width of its container, with a height dynamically determined by +/// the ad. +GAD_EXTERN GADAdSize const kGADAdSizeFluid; + +/// Invalid ad size marker. +GAD_EXTERN GADAdSize const kGADAdSizeInvalid; + +#pragma mark Custom Sizes + +/// Returns a custom GADAdSize for the provided CGSize. Use this only if you require a non-standard +/// size. Otherwise, use one of the standard size constants above. +GADAdSize GADAdSizeFromCGSize(CGSize size); + +/// Returns a custom GADAdSize that spans the full width of the application in portrait orientation +/// with the height provided. +GADAdSize GADAdSizeFullWidthPortraitWithHeight(CGFloat height); + +/// Returns a custom GADAdSize that spans the full width of the application in landscape orientation +/// with the height provided. +GADAdSize GADAdSizeFullWidthLandscapeWithHeight(CGFloat height); + +#pragma mark Convenience Functions + +/// Returns YES if the two GADAdSizes are equal, otherwise returns NO. +BOOL GADAdSizeEqualToSize(GADAdSize size1, GADAdSize size2); + +/// Returns a CGSize for the provided a GADAdSize constant. If the GADAdSize is unknown, returns +/// CGSizeZero. +CGSize CGSizeFromGADAdSize(GADAdSize size); + +/// Returns YES if |size| is one of the predefined constants or is a custom GADAdSize generated by +/// GADAdSizeFromCGSize. +BOOL IsGADAdSizeValid(GADAdSize size); + +/// Returns a NSString describing the provided GADAdSize. +NSString *NSStringFromGADAdSize(GADAdSize size); + +/// Returns an NSValue representing the GADAdSize. +NSValue *NSValueFromGADAdSize(GADAdSize size); + +/// Returns a GADAdSize from an NSValue. Returns kGADAdSizeInvalid if the value is not a GADAdSize. +GADAdSize GADAdSizeFromNSValue(NSValue *value); + +#pragma mark Deprecated Macros + +#define GAD_SIZE_320x50 CGSizeFromGADAdSize(kGADAdSizeBanner) +#define GAD_SIZE_320x100 CGSizeFromGADAdSize(kGADAdSizeLargeBanner) +#define GAD_SIZE_300x250 CGSizeFromGADAdSize(kGADAdSizeMediumRectangle) +#define GAD_SIZE_468x60 CGSizeFromGADAdSize(kGADAdSizeFullBanner) +#define GAD_SIZE_728x90 CGSizeFromGADAdSize(kGADAdSizeLeaderboard) +#define GAD_SIZE_120x600 CGSizeFromGADAdSize(kGADAdSizeSkyscraper) diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADAdSizeDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADAdSizeDelegate.h new file mode 100755 index 0000000..324a571 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADAdSizeDelegate.h @@ -0,0 +1,21 @@ +// +// GADAdSizeDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +#import + +@class GADBannerView; + +/// The class implementing this protocol will be notified when the DFPBannerView changes ad size. +/// Any views that may be affected by the banner size change will have time to adjust. +@protocol GADAdSizeDelegate + +/// Called before the ad view changes to the new size. +- (void)adView:(GADBannerView *)bannerView willChangeAdSizeTo:(GADAdSize)size; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADAppEventDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADAppEventDelegate.h new file mode 100755 index 0000000..34fbcce --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADAppEventDelegate.h @@ -0,0 +1,29 @@ +// +// GADAppEventDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +@class GADBannerView; +@class GADInterstitial; + +/// Implement your app event within these methods. The delegate will be notified when the SDK +/// receives an app event message from the ad. +@protocol GADAppEventDelegate + +@optional + +/// Called when the banner receives an app event. +- (void)adView:(GADBannerView *)banner + didReceiveAppEvent:(NSString *)name + withInfo:(NSString *)info; + +/// Called when the interstitial receives an app event. +- (void)interstitial:(GADInterstitial *)interstitial + didReceiveAppEvent:(NSString *)name + withInfo:(NSString *)info; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADBannerView.h b/GoogleMobileAds.framework/Versions/A/Headers/GADBannerView.h new file mode 100755 index 0000000..eace109 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADBannerView.h @@ -0,0 +1,106 @@ +// +// GADBannerView.h +// Google Mobile Ads SDK +// +// Copyright 2011 Google Inc. All rights reserved. +// + +#import + +#import +#import +#import +#import +#import +#import + +#ifndef IBInspectable +#define IBInspectable +#endif + +/// The view that displays banner ads. A minimum implementation to get an ad from within a +/// UIViewController class is: +/// +/// \code +/// // Create and setup the ad view, specifying the size and origin at {0, 0}. +/// GADBannerView *adView = [[GADBannerView alloc] initWithAdSize:kGADAdSizeBanner]; +/// adView.rootViewController = self; +/// adView.adUnitID = @"ID created when registering your app"; +/// +/// // Place the ad view onto the screen. +/// [self.view addSubview:adView]; +/// +/// // Request an ad without any additional targeting information. +/// [adView loadRequest:[GADRequest request]]; +/// \endcode +/// +@interface GADBannerView : UIView + +#pragma mark Initialization + +/// Initializes a GADBannerView and sets it to the specified size, and specifies its placement +/// within its superview bounds. Returns nil if |adSize| is an invalid ad size. +- (instancetype)initWithAdSize:(GADAdSize)adSize origin:(CGPoint)origin; + +/// Initializes a GADBannerView and sets it to the specified size, and specifies its placement at +/// the top left of its superview. Returns nil if |adSize| is an invalid ad size. +- (instancetype)initWithAdSize:(GADAdSize)adSize; + +#pragma mark Pre-Request + +/// Required value created on the AdMob website. Create a new ad unit for every unique placement of +/// an ad in your application. Set this to the ID assigned for this placement. Ad units are +/// important for targeting and statistics. +/// +/// Example AdMob ad unit ID: @"ca-app-pub-0123456789012345/0123456789" +@property(nonatomic, copy) IBInspectable NSString *adUnitID; + +/// Required reference to the current root view controller. For example the root view controller in +/// tab-based application would be the UITabViewController. +@property(nonatomic, weak) IBOutlet UIViewController *rootViewController; + +/// Required to set this banner view to a proper size. Never create your own GADAdSize directly. Use +/// one of the predefined standard ad sizes (such as kGADAdSizeBanner), or create one using the +/// GADAdSizeFromCGSize method. If not using mediation, then changing the adSize after an ad has +/// been shown will cause a new request (for an ad of the new size) to be sent. If using mediation, +/// then a new request may not be sent. +@property(nonatomic, assign) GADAdSize adSize; + +/// Optional delegate object that receives state change notifications from this GADBannerView. +/// Typically this is a UIViewController. +@property(nonatomic, weak) IBOutlet id delegate; + +/// Optional delegate object that receives in-app purchase notifications from this ad. Required for +/// the custom in-app purchase flow, but ignored when using the default in-app purchase flow. +@property(nonatomic, weak) IBOutlet id inAppPurchaseDelegate; + +#pragma mark Making an Ad Request + +/// Makes an ad request. The request object supplies targeting information. +- (void)loadRequest:(GADRequest *)request; + +/// A Boolean value that determines whether autoloading of ads in the receiver is enabled. If +/// enabled, you do not need to call the loadRequest: method to load ads. +@property(nonatomic, assign, getter=isAutoloadEnabled) IBInspectable BOOL autoloadEnabled; + +#pragma mark Mediation + +/// The ad network class name that fetched the current ad. Returns nil while the latest ad request +/// is in progress or if the latest ad request failed. For both standard and mediated Google AdMob +/// ads, this method returns @"GADMAdapterGoogleAdMobAds". For ads fetched via mediation custom +/// events, this method returns @"GADMAdapterCustomEvents". +@property(nonatomic, readonly, copy) NSString *adNetworkClassName; + +#pragma mark Deprecated + +/// Indicates if the currently displayed ad (or most recent failure) was a result of auto refreshing +/// as specified on server. This property is set to NO after each loadRequest: method. +@property(nonatomic, readonly, assign) BOOL hasAutoRefreshed GAD_DEPRECATED_ATTRIBUTE; + +/// The mediated ad network's underlying ad view. You may use this property to read the ad's actual +/// size and adjust this banner view's frame origin. However, modifying the banner view's frame size +/// triggers the Mobile Ads SDK to request a new ad. Only update the banner view's frame origin. +@property(nonatomic, readonly, weak) + UIView *mediatedAdView GAD_DEPRECATED_MSG_ATTRIBUTE("Use adNetworkClassName."); + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADBannerViewDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADBannerViewDelegate.h new file mode 100755 index 0000000..bd377bb --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADBannerViewDelegate.h @@ -0,0 +1,47 @@ +// +// GADBannerViewDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2011 Google Inc. All rights reserved. +// + +#import + +@class GADBannerView; +@class GADRequestError; + +/// Delegate methods for receiving GADBannerView state change messages such as ad request status +/// and ad click lifecycle. +@protocol GADBannerViewDelegate + +@optional + +#pragma mark Ad Request Lifecycle Notifications + +/// Tells the delegate that an ad request successfully received an ad. The delegate may want to add +/// the banner view to the view hierarchy if it hasn't been added yet. +- (void)adViewDidReceiveAd:(GADBannerView *)bannerView; + +/// Tells the delegate that an ad request failed. The failure is normally due to network +/// connectivity or ad availablility (i.e., no fill). +- (void)adView:(GADBannerView *)bannerView didFailToReceiveAdWithError:(GADRequestError *)error; + +#pragma mark Click-Time Lifecycle Notifications + +/// Tells the delegate that a full screen view will be presented in response to the user clicking on +/// an ad. The delegate may want to pause animations and time sensitive interactions. +- (void)adViewWillPresentScreen:(GADBannerView *)bannerView; + +/// Tells the delegate that the full screen view will be dismissed. +- (void)adViewWillDismissScreen:(GADBannerView *)bannerView; + +/// Tells the delegate that the full screen view has been dismissed. The delegate should restart +/// anything paused while handling adViewWillPresentScreen:. +- (void)adViewDidDismissScreen:(GADBannerView *)bannerView; + +/// Tells the delegate that the user click will open another app, backgrounding the current +/// application. The standard UIApplicationDelegate methods, like applicationDidEnterBackground:, +/// are called immediately before this method is called. +- (void)adViewWillLeaveApplication:(GADBannerView *)bannerView; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCorrelator.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCorrelator.h new file mode 100755 index 0000000..268c2ae --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCorrelator.h @@ -0,0 +1,17 @@ +// +// GADCorrelator.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +/// Represents a correlation between multiple ads. Set an instance of this object on multiple ads to +/// indicate they are being used in a common context. +@interface GADCorrelator : NSObject + +/// Resets the correlator to force a new set of correlated ads. +- (void)reset; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCorrelatorAdLoaderOptions.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCorrelatorAdLoaderOptions.h new file mode 100755 index 0000000..65df4e8 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCorrelatorAdLoaderOptions.h @@ -0,0 +1,17 @@ +// +// GADCorrelatorAdLoaderOptions.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +/// Ad loader options for adding a correlator to a native ad request. +@interface GADCorrelatorAdLoaderOptions : GADAdLoaderOptions + +/// Correlator object for correlating ads loaded by an ad loader to other ad objects. +@property(nonatomic, strong) GADCorrelator *correlator; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventBanner.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventBanner.h new file mode 100755 index 0000000..e0e28c2 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventBanner.h @@ -0,0 +1,38 @@ +// +// GADCustomEventBanner.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +#import +#import +#import + +/// The protocol for a Custom Event of the banner type. Your Custom Event handler object for banners +/// must implement this protocol. The requestBannerAd method will be called when mediation schedules +/// your Custom Event to be executed. +@protocol GADCustomEventBanner + +/// This method is called by mediation when your Custom Event is scheduled to be executed. Results +/// of the execution should be reported back via the delegate. |adSize| is the size of the ad as +/// configured in the mediation UI for the mediation placement. |serverParameter| and |serverLabel| +/// are the parameter and label configured in the mediation UI for the Custom Event. |request| +/// contains information about the ad request, some of those are from GADRequest. +- (void)requestBannerAd:(GADAdSize)adSize + parameter:(NSString *)serverParameter + label:(NSString *)serverLabel + request:(GADCustomEventRequest *)request; + +/// You should call back to the |delegate| with the results of the execution to ensure mediation +/// behaves correctly. The delegate is weakly referenced to prevent memory leaks caused by circular +/// retention. +/// +/// Define the -delegate and -setDelegate: methods in your class. +/// +/// In your class's -dealloc method, remember to nil out the delegate. +@property(nonatomic, weak) id delegate; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventBannerDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventBannerDelegate.h new file mode 100755 index 0000000..271489f --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventBannerDelegate.h @@ -0,0 +1,62 @@ +// +// GADCustomEventBannerDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import +#import + +#import + +@protocol GADCustomEventBanner; + +/// Call back to this delegate in your custom event. You must call customEventBanner:didReceiveAd: +/// when there is an ad to show, or customEventBanner:didFailAd: when there is no ad to show. +/// Otherwise, if enough time passed (several seconds) after the SDK called the requestBannerAd: +/// method of your custom event, the mediation SDK will consider the request timed out, and move on +/// to the next ad network. +@protocol GADCustomEventBannerDelegate + +/// Your Custom Event object must call this when it receives or creates an ad view. +- (void)customEventBanner:(id)customEvent didReceiveAd:(UIView *)view; + +/// Your Custom Event object must call this when it fails to receive or create the ad view. Pass +/// along any error object sent from the ad network's SDK, or an NSError describing the error. Pass +/// nil if not available. +- (void)customEventBanner:(id)customEvent didFailAd:(NSError *)error; + +/// Your Custom Event object should call this when the user touches or "clicks" the ad to initiate +/// an action. When the SDK receives this callback, it reports the click back to the mediation +/// server. +- (void)customEventBannerWasClicked:(id)customEvent; + +/// The rootViewController that you set in GADBannerView. Use this UIViewController to show a modal +/// view when a user taps on the ad. +@property(nonatomic, readonly) UIViewController *viewControllerForPresentingModalView; + +/// When you call the following methods, the call will be propagated back to the +/// GADBannerViewDelegate that you implemented and passed to GADBannerView. + +/// Your Custom Event should call this when the user taps an ad and a modal view appears. +- (void)customEventBannerWillPresentModal:(id)customEvent; + +/// Your Custom Event should call this when the user dismisses the modal view and the modal view is +/// about to go away. +- (void)customEventBannerWillDismissModal:(id)customEvent; + +/// Your Custom Event should call this when the user dismisses the modal view and the modal view has +/// gone away. +- (void)customEventBannerDidDismissModal:(id)customEvent; + +/// Your Custom Event should call this method when a user action will result in App switching. +- (void)customEventBannerWillLeaveApplication:(id)customEvent; + +#pragma mark Deprecated + +- (void)customEventBanner:(id)customEvent + clickDidOccurInAd:(UIView *)view + GAD_DEPRECATED_MSG_ATTRIBUTE("Use customEventBannerWasClicked:."); + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventExtras.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventExtras.h new file mode 100755 index 0000000..3ebe81e --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventExtras.h @@ -0,0 +1,30 @@ +// +// GADCustomEventExtras.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +#import + +/// Create an instance of this class to set additional parameters for each custom event object. The +/// additional parameters for a custom event are keyed by the custom event label. These extras are +/// passed to your implementation of GADCustomEventBanner or GADCustomEventInterstitial. +@interface GADCustomEventExtras : NSObject + +/// Set additional parameters for the custom event with label |label|. To remove additional +/// parameters associated with |label|, pass in nil for |extras|. +- (void)setExtras:(NSDictionary *)extras forLabel:(NSString *)label; + +/// Retrieve the extras for |label|. +- (NSDictionary *)extrasForLabel:(NSString *)label; + +/// Removes all the extras set on this instance. +- (void)removeAllExtras; + +/// Returns all the extras set on this instance. +- (NSDictionary *)allExtras; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventInterstitial.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventInterstitial.h new file mode 100755 index 0000000..9e2a860 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventInterstitial.h @@ -0,0 +1,44 @@ +// +// GADCustomEventInterstitial.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +#import +#import + +/// The protocol for a Custom Event of the interstitial type. Your Custom Event handler object for +/// interstitial must implement this protocol. The requestInterstitialAd method will be called when +/// mediation schedules your Custom Event to be executed. +@protocol GADCustomEventInterstitial + +/// You should call back to the |delegate| with the results of the execution to ensure mediation +/// behaves correctly. The delegate is assigned, not retained, to prevent memory leak caused by +/// circular retention. +/// +/// Define the -delegate and -setDelegate: methods in your class. +/// +/// In your class's -dealloc method, remember to nil out the delegate. +@property(nonatomic, weak) id delegate; + +/// This method is called by mediation when your Custom Event is scheduled to be executed. Your +/// implementation should begin retrieval of the interstitial ad, usually from a backend server, or +/// from an ad network SDK. Results of the execution should be reported back via the delegate. Note +/// that you should wait until -presentFromRootViewController is called before displaying the +/// interstitial ad. Do not automatically display the ad when you receive the ad. Instead, retain +/// the ad and display it when presentFromRootViewController is called. |serverParameter| and +/// |serverLabel| are the parameter and label configured in the AdMob mediation UI for the Custom +/// Event. |request| contains information about the ad request, some of those are from GADRequest. +- (void)requestInterstitialAdWithParameter:(NSString *)serverParameter + label:(NSString *)serverLabel + request:(GADCustomEventRequest *)request; + +/// Present the interstitial ad as a modal view using the provided view controller. This is called +/// only after your Custom Event calls back to the delegate with the message +/// -customEvent:didReceiveAd: . +- (void)presentFromRootViewController:(UIViewController *)rootViewController; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventInterstitialDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventInterstitialDelegate.h new file mode 100755 index 0000000..bb6feba --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventInterstitialDelegate.h @@ -0,0 +1,56 @@ +// +// GADCustomEventInterstitialDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +#import + +@protocol GADCustomEventInterstitial; + +/// Call back to this delegate in your custom event. You must call +/// customEventInterstitial:didReceiveAd: when there is an ad to show, or +/// customEventInterstitial:didFailAd: when there is no ad to show. Otherwise, if enough time passed +/// (several seconds) after the SDK called the requestInterstitialAdWithParameter: method of your +/// custom event, the mediation SDK will consider the request timed out, and move on to the next ad +/// network. +@protocol GADCustomEventInterstitialDelegate + +/// Your Custom Event object must call this when it receives or creates an interstitial ad. +- (void)customEventInterstitialDidReceiveAd:(id)customEvent; + +/// Your Custom Event object must call this when it fails to receive or create the ad. Pass along +/// any error object sent from the ad network's SDK, or an NSError describing the error. Pass nil if +/// not available. +- (void)customEventInterstitial:(id)customEvent + didFailAd:(NSError *)error; + +/// Your Custom Event object should call this when the user touches or "clicks" the ad to initiate +/// an action. When the SDK receives this callback, it reports the click back to the mediation +/// server. +- (void)customEventInterstitialWasClicked:(id)customEvent; + +/// When you call any of the the following methods, the call will be propagated back to the +/// GADInterstitialDelegate that you implemented and passed to GADInterstitial. + +/// Your Custom Event should call this when the interstitial is being displayed. +- (void)customEventInterstitialWillPresent:(id)customEvent; + +/// Your Custom Event should call this when the interstitial is about to be dismissed. +- (void)customEventInterstitialWillDismiss:(id)customEvent; + +/// Your Custom Event should call this when the interstitial has been dismissed. +- (void)customEventInterstitialDidDismiss:(id)customEvent; + +/// Your Custom Event should call this method when a user action will result in app switching. +- (void)customEventInterstitialWillLeaveApplication:(id)customEvent; + +#pragma mark Deprecated +- (void)customEventInterstitial:(id)customEvent + didReceiveAd:(NSObject *)ad + GAD_DEPRECATED_MSG_ATTRIBUTE("Use customEventInterstitialDidReceiveAd:."); + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventNativeAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventNativeAd.h new file mode 100755 index 0000000..7f4c83a --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventNativeAd.h @@ -0,0 +1,36 @@ +// +// GADCustomEventNativeAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +#import "GADCustomEventRequest.h" + +@protocol GADCustomEventNativeAdDelegate; + +/// The protocol for a custom event for a native ad. Your custom event handler object for native ads +/// must implement this protocol. The +/// requestNativeAdWithParameter:request:adTypes:options:rootViewController: method will be called +/// when mediation schedules your custom event to be executed. +@protocol GADCustomEventNativeAd + +/// This method is called by mediation when your custom event is scheduled to be executed. +/// |serverParameter| is the parameter configured in the mediation UI for the custom event. +/// |request| contains ad targeting information. |adTypes| contains the list of native ad types +/// requested. See GADAdLoaderAdTypes.h for available ad types. |options| are any additional options +/// configured by the publisher for requesting a native ad. See GADNativeAdImageAdLoaderOptions.h +/// for available image options. |rootViewController| is the view controller provided by the +/// publisher. +- (void)requestNativeAdWithParameter:(NSString *)serverParameter + request:(GADCustomEventRequest *)request + adTypes:(NSArray *)adTypes + options:(NSArray *)options + rootViewController:(UIViewController *)rootViewController; + +/// The delegate object, used for receiving custom native ad load request progress. +@property(nonatomic, weak) id delegate; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventNativeAdDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventNativeAdDelegate.h new file mode 100755 index 0000000..4f1f79a --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventNativeAdDelegate.h @@ -0,0 +1,26 @@ +// +// GADCustomEventNativeAdDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +#import +#import + +/// The delegate of the GADCustomEventNativeAd object must adopt the GADCustomEventNativeAdDelegate +/// protocol. Methods in this protocol are used for native ad's custom event communication with the +/// Google Mobile Ads SDK. +@protocol GADCustomEventNativeAdDelegate + +/// Tells the delegate that the custom event ad request succeeded and loaded a native ad. +- (void)customEventNativeAd:(id)customEventNativeAd + didReceiveMediatedNativeAd:(id)mediatedNativeAd; + +/// Tells the delegate that the custom event ad request failed. +- (void)customEventNativeAd:(id)customEventNativeAd + didFailToLoadWithError:(NSError *)error; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventRequest.h b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventRequest.h new file mode 100755 index 0000000..75fe69b --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADCustomEventRequest.h @@ -0,0 +1,47 @@ +// +// GADCustomEventRequest.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +#import + +@class GADCustomEventExtras; + +@interface GADCustomEventRequest : NSObject + +/// The end user's gender set in GADRequest. If not specified, returns kGADGenderUnknown. +@property(nonatomic, readonly, assign) GADGender userGender; + +/// The end user's birthday set in GADRequest. If not specified, returns nil. +@property(nonatomic, readonly, copy) NSDate *userBirthday; + +/// The end user's latitude, longitude, and accuracy, set in GADRequest. If not specified, +/// userHasLocation returns NO, and userLatitude, userLongitude and userLocationAccuracyInMeters +/// will all return 0. +@property(nonatomic, readonly, assign) BOOL userHasLocation; +@property(nonatomic, readonly, assign) CGFloat userLatitude; +@property(nonatomic, readonly, assign) CGFloat userLongitude; +@property(nonatomic, readonly, assign) CGFloat userLocationAccuracyInMeters; + +/// Description of the user's location, in free form text, set in GADRequest. If not available, +/// returns nil. This may be set even if userHasLocation is NO. +@property(nonatomic, readonly, copy) NSString *userLocationDescription; + +/// Keywords set in GADRequest. Returns nil if no keywords are set. +@property(nonatomic, readonly, copy) NSArray *userKeywords; + +/// The additional parameters set by the application. This property allows you to pass additional +/// information from your application to your Custom Event object. To do so, create an instance of +/// GADCustomEventExtras to pass to GADRequest -registerAdNetworkExtras:. The instance should have +/// an NSDictionary set for a particular custom event label. That NSDictionary becomes the +/// additionalParameters here. +@property(nonatomic, readonly, copy) NSDictionary *additionalParameters; + +/// Indicates if the testing property has been set in GADRequest. +@property(nonatomic, readonly, assign) BOOL isTesting; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADExtras.h b/GoogleMobileAds.framework/Versions/A/Headers/GADExtras.h new file mode 100755 index 0000000..80a354a --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADExtras.h @@ -0,0 +1,17 @@ +// +// GADExtras.h +// Google Mobile Ads SDK +// +// Copyright 2012 Google Inc. All rights reserved. +// + +#import + +#import + +@interface GADExtras : NSObject + +/// Additional parameters to be sent to Google networks. +@property(nonatomic, copy) NSDictionary *additionalParameters; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADInAppPurchase.h b/GoogleMobileAds.framework/Versions/A/Headers/GADInAppPurchase.h new file mode 100755 index 0000000..bd8d922 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADInAppPurchase.h @@ -0,0 +1,80 @@ +// +// GADInAppPurchase.h +// Google Mobile Ads SDK +// +// Copyright 2013 Google Inc. All rights reserved. +// + +#import +#import + +@protocol GADDefaultInAppPurchaseDelegate; + +#pragma mark - Default Purchase Flow + +/// The consumable in-app purchase item that has been purchased by the user. The purchase flow is +/// handled by the Google Mobile Ads SDK. +/// Instances of this class are created and passed to your in-app purchase delegate after the user +/// has successfully paid for a product. Your code must correctly deliver the product to the user +/// and then call the didCompletePurchase method to finish the transaction. +@interface GADDefaultInAppPurchase : NSObject + +/// Enables the default consumable product in-app purchase flow handled by the Google Mobile Ads +/// SDK. The GADDefaultInAppPurchaseDelegate object is retained while the default purchase flow is +/// enabled. This method adds a SKPaymentTransactionObserver to the default SKPaymentQueue. +/// +/// Call this method early in your application to handle unfinished transactions from previous +/// application sessions. For example, call this method in your application delegate's +/// application:didFinishLaunchingWithOptions: method. ++ (void)enableDefaultPurchaseFlowWithDelegate:(id)delegate; + +/// Disables the default in-app purchase flow handled by the Google Mobile Ads SDK and releases the +/// associated GADDefaultInAppPurchaseDelegate object. ++ (void)disableDefaultPurchaseFlow; + +/// The in-app purchase product ID. +@property(nonatomic, readonly, copy) NSString *productID; + +/// The product quantity. +@property(nonatomic, readonly, assign) NSInteger quantity; + +/// The purchased item's completed payment transaction. Your application can use this property's +/// data to save a permanent record of the completed payment. The default purchase flow will finish +/// the transaction on your behalf. Do not finish the transaction yourself. +@property(nonatomic, readonly, strong) SKPaymentTransaction *paymentTransaction; + +/// The in-app purchase delegate object must first deliver the user's item and then call this +/// method. Failure to call this method will result in duplicate purchase notifications. +- (void)finishTransaction; + +@end + +#pragma mark - Custom Purchase Flow + +/// Enum of the different statuses resulting from processing a purchase. +typedef NS_ENUM(NSInteger, GADInAppPurchaseStatus) { + kGADInAppPurchaseStatusError = 0, ///< Error occured while processing the purchase. + kGADInAppPurchaseStatusSuccessful = 1, ///< Purchase was completed successfully. + kGADInAppPurchaseStatusCancel = 2, ///< Purchase was cancelled by the user. + kGADInAppPurchaseStatusInvalidProduct = 3 ///< Error occured while looking up the product. +}; + +/// The in-app purchase item to be purchased with the purchase flow handled by you, the +/// application developer. +/// Instances of this class are created and passed to your GADInAppPurchaseDelegate object when +/// users click a buy button. It is important to report the result of the purchase back to the SDK +/// in order to track metrics about the transaction. +@interface GADInAppPurchase : NSObject + +/// The in-app purchase product ID. +@property(nonatomic, readonly, copy) NSString *productID; + +/// The product quantity. +@property(nonatomic, readonly, assign) NSInteger quantity; + +/// The GADInAppPurchaseDelegate object must call this method after handling the in-app purchase for +/// both successful and unsuccessful purchase attempts. This method reports ad conversion and +/// purchase status information to Google. +- (void)reportPurchaseStatus:(GADInAppPurchaseStatus)purchaseStatus; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADInAppPurchaseDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADInAppPurchaseDelegate.h new file mode 100755 index 0000000..6dee244 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADInAppPurchaseDelegate.h @@ -0,0 +1,42 @@ +// +// GADInAppPurchaseDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2013 Google Inc. All rights reserved. +// + +#import + +@class GADDefaultInAppPurchase; +@class GADInAppPurchase; + +#pragma mark - Default Purchase Flow + +/// In-app purchase delegate protocol for default purchase handling. The delegate must deliver +/// the purchased item then call the GADDefaultInAppPurchase object's finishTransaction method. +@protocol GADDefaultInAppPurchaseDelegate + +/// Called when the user successfully paid for a purchase. You must first deliver the purchased +/// item to the user, then call defaultInAppPurchase's finishTransaction method. +- (void)userDidPayForPurchase:(GADDefaultInAppPurchase *)defaultInAppPurchase; + +@optional + +/// Called when the user clicks on the buy button of an in-app purchase ad. Return YES if the +/// default purchase flow should be started to purchase the item, otherwise return NO. If not +/// implemented, defaults to YES. +- (BOOL)shouldStartPurchaseForProductID:(NSString *)productID quantity:(NSInteger)quantity; + +@end + +#pragma mark - Custom Purchase Flow + +/// In-app purchase delegate protocol for custom purchase handling. The delegate must handle the +/// product purchase flow then call the GADInAppPurchase object's reportPurchaseStatus: method. +@protocol GADInAppPurchaseDelegate + +/// Called when the user clicks on the buy button of an in-app purchase ad. After the receiver +/// handles the purchase, it must call the GADInAppPurchase object's reportPurchaseStatus: method. +- (void)didReceiveInAppPurchase:(GADInAppPurchase *)purchase; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADInterstitial.h b/GoogleMobileAds.framework/Versions/A/Headers/GADInterstitial.h new file mode 100755 index 0000000..7e17627 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADInterstitial.h @@ -0,0 +1,86 @@ +// +// GADInterstitial.h +// Google Mobile Ads SDK +// +// Copyright 2011 Google Inc. All rights reserved. +// + +#import + +#import +#import +#import +#import +#import + +/// An interstitial ad. This is a full-screen advertisement shown at natural transition points in +/// your application such as between game levels or news stories. +@interface GADInterstitial : NSObject + +/// Initializes an interstitial with an ad unit created on the AdMob website. Create a new ad unit +/// for every unique placement of an ad in your application. Set this to the ID assigned for this +/// placement. Ad units are important for targeting and statistics. +/// +/// Example AdMob ad unit ID: @"ca-app-pub-0123456789012345/0123456789" +- (instancetype)initWithAdUnitID:(NSString *)adUnitID NS_DESIGNATED_INITIALIZER; + +#pragma mark Pre-Request + +/// Required value passed in with initWithAdUnitID:. +@property(nonatomic, readonly, copy) NSString *adUnitID; + +/// Optional delegate object that receives state change notifications from this GADInterstitalAd. +/// Remember to nil this property before deallocating the delegate. +@property(nonatomic, weak) id delegate; + +/// Optional delegate object that receives in-app purchase notifications from this ad. Required for +/// the custom in-app purchase flow, but ignored when using the default in-app purchase flow. +/// Remember to nil this property before deallocating the delegate. +@property(nonatomic, weak) id inAppPurchaseDelegate; + +#pragma mark Making an Ad Request + +/// Makes an interstitial ad request. Additional targeting options can be supplied with a request +/// object. Only one interstitial request is allowed at a time. +/// +/// This is best to do several seconds before the interstitial is needed to preload its content. +/// Then when transitioning between view controllers show the interstital with +/// presentFromViewController. +- (void)loadRequest:(GADRequest *)request; + +#pragma mark Post-Request + +/// Returns YES if the interstitial is ready to be displayed. The delegate's +/// interstitialAdDidReceiveAd: will be called after this property switches from NO to YES. +@property(nonatomic, readonly, assign) BOOL isReady; + +/// Returns YES if this object has already been presented. Interstitial objects can only be used +/// once even with different requests. +@property(nonatomic, readonly, assign) BOOL hasBeenUsed; + +/// Returns the ad network class name that fetched the current ad. Returns nil while the latest ad +/// request is in progress or if the latest ad request failed. For both standard and mediated Google +/// AdMob ads, this method returns @"GADMAdapterGoogleAdMobAds". For ads fetched via mediation +/// custom events, this method returns @"GADMAdapterCustomEvents". +@property(nonatomic, readonly, copy) NSString *adNetworkClassName; + +/// Presents the interstitial ad which takes over the entire screen until the user dismisses it. +/// This has no effect unless isReady returns YES and/or the delegate's interstitialDidReceiveAd: +/// has been received. +/// +/// Set rootViewController to the current view controller at the time this method is called. If your +/// application does not use view controllers pass in nil and your views will be removed from the +/// window to show the interstitial and restored when done. After the interstitial has been removed, +/// the delegate's interstitialDidDismissScreen: will be called. +- (void)presentFromRootViewController:(UIViewController *)rootViewController; + +#pragma mark Deprecated + +/// Deprecated intializer. Use initWithAdUnitID: instead. +- (instancetype)init GAD_DEPRECATED_MSG_ATTRIBUTE("Use initWithAdUnitID:."); + +/// Deprecated setter, use initWithAdUnitID: instead. +- (void)setAdUnitID:(NSString *)adUnitID + GAD_DEPRECATED_MSG_ATTRIBUTE("Use initWithAdUnitID: instead of setting the ad unit ID."); + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADInterstitialDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADInterstitialDelegate.h new file mode 100755 index 0000000..fe436aa --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADInterstitialDelegate.h @@ -0,0 +1,49 @@ +// +// GADInterstitialDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2011 Google Inc. All rights reserved. +// + +#import + +@class GADInterstitial; +@class GADRequestError; + +/// Delegate for receiving state change messages from a GADInterstitial such as interstitial ad +/// requests succeeding/failing. +@protocol GADInterstitialDelegate + +@optional + +#pragma mark Ad Request Lifecycle Notifications + +/// Called when an interstitial ad request succeeded. Show it at the next transition point in your +/// application such as when transitioning between view controllers. +- (void)interstitialDidReceiveAd:(GADInterstitial *)ad; + +/// Called when an interstitial ad request completed without an interstitial to +/// show. This is common since interstitials are shown sparingly to users. +- (void)interstitial:(GADInterstitial *)ad didFailToReceiveAdWithError:(GADRequestError *)error; + +#pragma mark Display-Time Lifecycle Notifications + +/// Called just before presenting an interstitial. After this method finishes the interstitial will +/// animate onto the screen. Use this opportunity to stop animations and save the state of your +/// application in case the user leaves while the interstitial is on screen (e.g. to visit the App +/// Store from a link on the interstitial). +- (void)interstitialWillPresentScreen:(GADInterstitial *)ad; + +/// Called before the interstitial is to be animated off the screen. +- (void)interstitialWillDismissScreen:(GADInterstitial *)ad; + +/// Called just after dismissing an interstitial and it has animated off the screen. +- (void)interstitialDidDismissScreen:(GADInterstitial *)ad; + +/// Called just before the application will background or terminate because the user clicked on an +/// ad that will launch another application (such as the App Store). The normal +/// UIApplicationDelegate methods, like applicationDidEnterBackground:, will be called immediately +/// before this. +- (void)interstitialWillLeaveApplication:(GADInterstitial *)ad; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAd.h new file mode 100755 index 0000000..868b640 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAd.h @@ -0,0 +1,21 @@ +// +// GADMediatedNativeAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +/// Base protocol for mediated native ads. +@protocol GADMediatedNativeAd + +/// Returns a delegate object that receives state change notifications. +- (id)mediatedNativeAdDelegate; + +/// Returns a dictionary of asset names and object pairs for assets that are not handled by +/// properties of the GADMediatedNativeAd subclass. +- (NSDictionary *)extraAssets; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAdDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAdDelegate.h new file mode 100755 index 0000000..716e283 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAdDelegate.h @@ -0,0 +1,32 @@ +// +// GADMediatedNativeAdDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +@protocol GADMediatedNativeAd; + +/// GADMediatedNativeAdDelegate objects handle mediated native ad events. +@protocol GADMediatedNativeAdDelegate + +@optional + +/// Tells the delegate that the mediated native ad has rendered in |view|. +- (void)mediatedNativeAd:(id)mediatedNativeAd didRenderInView:(UIView *)view; + +/// Tells the delegate that the mediated native ad has recorded an impression. This method is called +/// only once per mediated native ad. +- (void)mediatedNativeAdDidRecordImpression:(id)mediatedNativeAd; + +/// Tells the delegate that the mediated native ad has recorded a user click on the asset named +/// |assetName|. Full screen actions should be presented from |viewController|. +- (void)mediatedNativeAd:(id)mediatedNativeAd + didRecordClickOnAssetWithName:(NSString *)assetName + view:(UIView *)view + viewController:(UIViewController *)viewController; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAdNotificationSource.h b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAdNotificationSource.h new file mode 100755 index 0000000..6f1aed5 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAdNotificationSource.h @@ -0,0 +1,31 @@ +// +// GADMediatedNativeAdNotificationSource.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +/// Notifies the Google Mobile Ads SDK about the events performed by adapters. Adapters may perform +/// some action (e.g. opening an in app browser or open the iTunes store) when handling callbacks +/// from GADMediatedNativeAdDelegate. Adapters in such case should notify the Google Mobile Ads SDK +/// by calling the relevant methods from this class. +@interface GADMediatedNativeAdNotificationSource : NSObject + +/// Must be called by the adapter just before mediatedNativeAd has opened an in app modal screen. ++ (void)mediatedNativeAdWillPresentScreen:(id)mediatedNativeAd; + +/// Must be called by the adapter just before the in app modal screen opened by mediatedNativeAd is +/// dismissed. ++ (void)mediatedNativeAdWillDismissScreen:(id)mediatedNativeAd; + +/// Must be called by the adapter after the in app modal screen opened by mediatedNativeAd is +/// dismissed. ++ (void)mediatedNativeAdDidDismissScreen:(id)mediatedNativeAd; + +/// Must be called by the adapter just before mediatedNativeAd has left the application. ++ (void)mediatedNativeAdWillLeaveApplication:(id)mediatedNativeAd; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAppInstallAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAppInstallAd.h new file mode 100755 index 0000000..8269c87 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeAppInstallAd.h @@ -0,0 +1,40 @@ +// +// GADMediatedNativeAppInstallAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import +#import + +/// Provides methods used for constructing native app install ads. The adapter must return an object +/// conforming to this protocol for native app install ad requests. +@protocol GADMediatedNativeAppInstallAd + +/// App title. +- (NSString *)headline; + +/// Array of GADNativeAdImage objects related to the advertised application. +- (NSArray *)images; + +/// App description. +- (NSString *)body; + +/// Application icon. +- (GADNativeAdImage *)icon; + +/// Text that encourages user to take some action with the ad. For example "Install". +- (NSString *)callToAction; + +/// App store rating (0 to 5). +- (NSDecimalNumber *)starRating; + +/// The app store name. For example, "App Store". +- (NSString *)store; + +/// String representation of the app's price. +- (NSString *)price; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeContentAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeContentAd.h new file mode 100755 index 0000000..d560063 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADMediatedNativeContentAd.h @@ -0,0 +1,33 @@ +// +// GADMediatedNativeContentAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import +#import + +/// Provides methods used for constructing native content ads. +@protocol GADMediatedNativeContentAd + +/// Primary text headline. +- (NSString *)headline; + +/// Secondary text. +- (NSString *)body; + +/// List of large images. Each object is an instance of GADNativeAdImage. +- (NSArray *)images; + +/// Small logo image. +- (GADNativeAdImage *)logo; + +/// Text that encourages user to take some action with the ad. +- (NSString *)callToAction; + +/// Identifies the advertiser. For example, the advertiser’s name or visible URL. +- (NSString *)advertiser; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADMobileAds.h b/GoogleMobileAds.framework/Versions/A/Headers/GADMobileAds.h new file mode 100755 index 0000000..c8c2875 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADMobileAds.h @@ -0,0 +1,22 @@ +// +// GADMobileAds.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +@interface GADMobileAds : NSObject + +/// Disables automated in app purchase (IAP) reporting. Must be called before any IAP transaction is +/// initiated. IAP reporting is used to track IAP ad conversions. Do not disable reporting if you +/// use IAP ads. ++ (void)disableAutomatedInAppPurchaseReporting; + +/// Disables automated SDK crash reporting. If not called, the SDK records the original exception +/// handler if available and registers a new exception handler. The new exception handler only +/// reports SDK related exceptions and calls the recorded original exception handler. ++ (void)disableSDKCrashReporting; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAd.h new file mode 100755 index 0000000..b493721 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAd.h @@ -0,0 +1,30 @@ +// +// GADNativeAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +@protocol GADNativeAdDelegate; + +/// Native ad base class. All native ad types are subclasses of this class. +@interface GADNativeAd : NSObject + +/// Optional delegate to receive state change notifications. +@property(nonatomic, weak) id delegate; + +/// Root view controller for handling ad actions. +@property(nonatomic, weak) UIViewController *rootViewController; + +/// Dictionary of assets which aren't processed by the receiver. +@property(nonatomic, readonly, copy) NSDictionary *extraAssets; + +/// The ad network class name that fetched the current ad. For both standard and mediated Google +/// AdMob ads, this method returns @"GADMAdapterGoogleAdMobAds". For ads fetched via mediation +/// custom events, this method returns @"GADMAdapterCustomEvents". +@property(nonatomic, readonly, copy) NSString *adNetworkClassName; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdDelegate.h new file mode 100755 index 0000000..fa39d85 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdDelegate.h @@ -0,0 +1,41 @@ +// +// GADNativeAdDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +@class GADNativeAd; + +/// Identifies native ad assets. +@protocol GADNativeAdDelegate + +@optional + +#pragma mark Click-Time Lifecycle Notifications + +/// Called just before presenting the user a full screen view, such as a browser, in response to +/// clicking on an ad. Use this opportunity to stop animations, time sensitive interactions, etc. +/// +/// Normally the user looks at the ad, dismisses it, and control returns to your application with +/// the nativeAdDidDismissScreen: message. However, if the user hits the Home button or clicks on an +/// App Store link, your application will end. The next method called will be the +/// applicationWillResignActive: of your UIApplicationDelegate object.Immediately after that, +/// nativeAdWillLeaveApplication: is called. +- (void)nativeAdWillPresentScreen:(GADNativeAd *)nativeAd; + +/// Called just before dismissing a full screen view. +- (void)nativeAdWillDismissScreen:(GADNativeAd *)nativeAd; + +/// Called just after dismissing a full screen view. Use this opportunity to restart anything you +/// may have stopped as part of nativeAdWillPresentScreen:. +- (void)nativeAdDidDismissScreen:(GADNativeAd *)nativeAd; + +/// Called just before the application will go to the background or terminate due to an ad action +/// that will launch another application (such as the App Store). The normal UIApplicationDelegate +/// methods, like applicationDidEnterBackground:, will be called immediately before this. +- (void)nativeAdWillLeaveApplication:(GADNativeAd *)nativeAd; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImage+Mediation.h b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImage+Mediation.h new file mode 100755 index 0000000..b448c4f --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImage+Mediation.h @@ -0,0 +1,18 @@ +// +// GADNativeAdImage+Mediation.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google. All rights reserved. +// + +#import "GADNativeAdImage.h" + +@interface GADNativeAdImage (MediationAdditions) + +/// Initializes and returns a native ad image object with the provided image. +- (instancetype)initWithImage:(UIImage *)image; + +/// Initializes and returns a native ad image object with the provided image URL and image scale. +- (instancetype)initWithURL:(NSURL *)URL scale:(CGFloat)scale; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImage.h b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImage.h new file mode 100755 index 0000000..58da290 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImage.h @@ -0,0 +1,23 @@ +// +// GADNativeAdImage.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +/// Native ad image. +@interface GADNativeAdImage : NSObject + +/// The image. If image autoloading is disabled, this property will be nil. +@property(nonatomic, readonly, strong) UIImage *image; + +/// The image's URL. +@property(nonatomic, readonly, strong) NSURL *imageURL; + +/// The image's scale. +@property(nonatomic, readonly, assign) CGFloat scale; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImageAdLoaderOptions.h b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImageAdLoaderOptions.h new file mode 100755 index 0000000..c847e73 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAdImageAdLoaderOptions.h @@ -0,0 +1,31 @@ +// +// GADNativeAdImageAdLoaderOptions.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +/// Native ad image orientation preference. +typedef NS_ENUM(NSInteger, GADNativeAdImageAdLoaderOptionsOrientation) { + GADNativeAdImageAdLoaderOptionsOrientationAny, ///< No orientation preference. + GADNativeAdImageAdLoaderOptionsOrientationPortrait, ///< Prefer portrait images. + GADNativeAdImageAdLoaderOptionsOrientationLandscape ///< Prefer landscape images. +}; + +@interface GADNativeAdImageAdLoaderOptions : GADAdLoaderOptions + +/// Indicates if image asset content should be loaded by the SDK. If set to YES, the SDK will not +/// load image asset content and native ad image URLs can be used to fetch content. Defaults to NO, +/// image assets are loaded by the SDK. +@property(nonatomic, assign) BOOL disableImageLoading; + +/// Indicates if multiple images should be loaded for each asset. Defaults to NO. +@property(nonatomic, assign) BOOL shouldRequestMultipleImages; + +/// Indicates preferred image orientation. Defaults to +/// GADNativeAdImageAdLoaderOptionsOrientationAny. +@property(nonatomic, assign) GADNativeAdImageAdLoaderOptionsOrientation preferredImageOrientation; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAppInstallAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAppInstallAd.h new file mode 100755 index 0000000..6512627 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeAppInstallAd.h @@ -0,0 +1,77 @@ +// +// GADNativeAppInstallAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +#import +#import +#import +#import + +/// For use with GADAdLoader's creation methods. If you request this ad type, your delegate must +/// conform to the GADNativeAppInstallAdRequestDelegate protocol. +/// +/// See GADNativeAdImageAdLoaderOptions.h for ad loader image options. +GAD_EXTERN NSString *const kGADAdLoaderAdTypeNativeAppInstall; + +/// Native app install ad. +@interface GADNativeAppInstallAd : GADNativeAd + +#pragma mark - Must be displayed + +/// App title. +@property(nonatomic, readonly, copy) NSString *headline; +/// Text that encourages user to take some action with the ad. For example "Install". +@property(nonatomic, readonly, copy) NSString *callToAction; +/// Application icon. +@property(nonatomic, readonly, strong) GADNativeAdImage *icon; + +#pragma mark - Recommended to display + +/// App description. +@property(nonatomic, readonly, copy) NSString *body; +/// The app store name. For example, "App Store". +@property(nonatomic, readonly, copy) NSString *store; +/// String representation of the app's price. +@property(nonatomic, readonly, copy) NSString *price; +/// Array of GADNativeAdImage objects related to the advertised application. +@property(nonatomic, readonly, strong) NSArray *images; +/// App store rating (0 to 5). +@property(nonatomic, readonly, copy) NSDecimalNumber *starRating; +@end + +#pragma mark - Protocol and constants + +/// The delegate of a GADAdLoader object implements this protocol to receive GADNativeAppInstallAd +/// ads. +@protocol GADNativeAppInstallAdLoaderDelegate +/// Called when a native app install ad is received. +- (void)adLoader:(GADAdLoader *)adLoader + didReceiveNativeAppInstallAd:(GADNativeAppInstallAd *)nativeAppInstallAd; +@end + +#pragma mark - Native App Install Ad View + +/// Base class for app install ad views. Your app install ad view must be a subclass of this class +/// and must call superclass methods for all overriden methods. +@interface GADNativeAppInstallAdView : UIView + +/// This property must point to the native app install ad object rendered by this ad view. +@property(nonatomic, strong) GADNativeAppInstallAd *nativeAppInstallAd; + +// Weak references to your ad view's asset views. +@property(nonatomic, weak) IBOutlet UIView *headlineView; +@property(nonatomic, weak) IBOutlet UIView *callToActionView; +@property(nonatomic, weak) IBOutlet UIView *iconView; +@property(nonatomic, weak) IBOutlet UIView *bodyView; +@property(nonatomic, weak) IBOutlet UIView *storeView; +@property(nonatomic, weak) IBOutlet UIView *priceView; +@property(nonatomic, weak) IBOutlet UIView *imageView; +@property(nonatomic, weak) IBOutlet UIView *starRatingView; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADNativeContentAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeContentAd.h new file mode 100755 index 0000000..0e5d999 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeContentAd.h @@ -0,0 +1,72 @@ +// +// GADNativeContentAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +#import +#import +#import +#import + +#pragma mark - Native Content Ad Assets + +/// For use with GADAdLoader's creation methods. If you request this ad type, your delegate must +/// conform to the GADNativeContentAdRequestDelegate protocol. +/// +/// See GADNativeAdImageAdLoaderOptions.h for ad loader image options. +GAD_EXTERN NSString *const kGADAdLoaderAdTypeNativeContent; + +/// Native content ad. +@interface GADNativeContentAd : GADNativeAd + +#pragma mark - Must be displayed + +/// Primary text headline. +@property(nonatomic, readonly, copy) NSString *headline; +/// Secondary text. +@property(nonatomic, readonly, copy) NSString *body; + +#pragma mark - Recommended to display + +/// Large images. +@property(nonatomic, readonly, copy) NSArray *images; +/// Small logo image. +@property(nonatomic, readonly, strong) GADNativeAdImage *logo; +/// Text that encourages user to take some action with the ad. +@property(nonatomic, readonly, copy) NSString *callToAction; +/// Identifies the advertiser. For example, the advertiser’s name or visible URL. +@property(nonatomic, readonly, copy) NSString *advertiser; +@end + +#pragma mark - Protocol and constants + +/// The delegate of a GADAdLoader object implements this protocol to receive GADNativeContentAd ads. +@protocol GADNativeContentAdLoaderDelegate +/// Called when native content is received. +- (void)adLoader:(GADAdLoader *)adLoader + didReceiveNativeContentAd:(GADNativeContentAd *)nativeContentAd; +@end + +#pragma mark - Native Content Ad View + +/// Base class for content ad views. Your content ad view must be a subclass of this class and must +/// call superclass methods for all overriden methods. +@interface GADNativeContentAdView : UIView + +/// This property must point to the native content ad object rendered by this ad view. +@property(nonatomic, strong) GADNativeContentAd *nativeContentAd; + +// Weak references to your ad view's asset views. +@property(nonatomic, weak) IBOutlet UIView *headlineView; +@property(nonatomic, weak) IBOutlet UIView *bodyView; +@property(nonatomic, weak) IBOutlet UIView *imageView; +@property(nonatomic, weak) IBOutlet UIView *logoView; +@property(nonatomic, weak) IBOutlet UIView *callToActionView; +@property(nonatomic, weak) IBOutlet UIView *advertiserView; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADNativeCustomTemplateAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeCustomTemplateAd.h new file mode 100755 index 0000000..b7dbd23 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADNativeCustomTemplateAd.h @@ -0,0 +1,62 @@ +// +// GADNativeCustomTemplateAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +#import +#import +#import +#import + +/// For use with GADAdLoader's creation methods. If you request this ad type, your delegate must +/// conform to the GADNativeCustomTemplateAdLoaderDelegate protocol. +GAD_EXTERN NSString *const kGADAdLoaderAdTypeNativeCustomTemplate; + +/// Native custom template ad. +@interface GADNativeCustomTemplateAd : GADNativeAd + +/// The ad's custom template ID. +@property(nonatomic, readonly) NSString *templateID; + +/// Array of available asset keys. +@property(nonatomic, readonly) NSArray *availableAssetKeys; + +/// Returns the native ad image corresponding to the specified key or nil if the image is not +/// available. +- (GADNativeAdImage *)imageForKey:(NSString *)key; + +/// Returns the string corresponding to the specified key or nil if the string is not available. +- (NSString *)stringForKey:(NSString *)key; + +/// Call when the user clicks on the ad. Provide the asset key that best matches the asset the user +/// interacted with. Provide |customClickHandler| only if this template is configured with a custom +/// click action, otherwise pass in nil. If a block is provided, the ad's built-in click actions are +/// ignored and |customClickHandler| is executed after recording the click. +- (void)performClickOnAssetWithKey:(NSString *)assetKey + customClickHandler:(dispatch_block_t)customClickHandler; + +/// Call when the ad is displayed on screen to the user. Can be called multiple times. Only the +/// first impression is recorded. +- (void)recordImpression; + +@end + +#pragma mark - Loading Protocol + +/// The delegate of a GADAdLoader object implements this protocol to receive +/// GADNativeCustomTemplateAd ads. +@protocol GADNativeCustomTemplateAdLoaderDelegate + +/// Called when requesting an ad. Asks the delgate for an array of custom template ID strings. +- (NSArray *)nativeCustomTemplateIDsForAdLoader:(GADAdLoader *)adLoader; + +/// Tells the delegate that a native custom template ad was received. +- (void)adLoader:(GADAdLoader *)adLoader + didReceiveNativeCustomTemplateAd:(GADNativeCustomTemplateAd *)nativeCustomTemplateAd; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADRequest.h b/GoogleMobileAds.framework/Versions/A/Headers/GADRequest.h new file mode 100755 index 0000000..68060d5 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADRequest.h @@ -0,0 +1,124 @@ +// +// GADRequest.h +// Google Mobile Ads SDK +// +// Copyright 2011 Google Inc. All rights reserved. +// + +#import +#import + +#import + +/// Add this constant to the testDevices property's array to receive test ads on the simulator. +GAD_EXTERN const id kGADSimulatorID; + +@protocol GADAdNetworkExtras; + +/// Genders to help deliver more relevant ads. +typedef NS_ENUM(NSInteger, GADGender) { + kGADGenderUnknown, ///< Unknown gender. + kGADGenderMale, ///< Male gender. + kGADGenderFemale ///< Female gender. +}; + +/// Specifies optional parameters for ad requests. +@interface GADRequest : NSObject + +/// Returns a default request. ++ (instancetype)request; + +#pragma mark Additional Parameters For Ad Networks + +/// Ad networks may have additional parameters they accept. To pass these parameters to them, create +/// the ad network extras object for that network, fill in the parameters, and register it here. The +/// ad network should have a header defining the interface for the 'extras' object to create. All +/// networks will have access to the basic settings you've set in this GADRequest (gender, birthday, +/// testing mode, etc.). If you register an extras object that is the same class as one you have +/// registered before, the previous extras will be overwritten. +- (void)registerAdNetworkExtras:(id)extras; + +/// Returns the network extras defined for an ad network. +- (id)adNetworkExtrasFor:(Class)aClass; + +/// Removes the extras for an ad network. |aClass| is the class which represents that network's +/// extras type. +- (void)removeAdNetworkExtrasFor:(Class)aClass; + +#pragma mark Collecting SDK Information + +/// Returns the version of the SDK. ++ (NSString *)sdkVersion; + +#pragma mark Testing + +/// Test ads will be returned for devices with device IDs specified in this array. +@property(nonatomic, copy) NSArray *testDevices; + +#pragma mark User Information + +/// Provide the user's gender to increase ad relevancy. +@property(nonatomic, assign) GADGender gender; + +/// Provide the user's birthday to increase ad relevancy. +@property(nonatomic, copy) NSDate *birthday; + +/// The user's current location may be used to deliver more relevant ads. However do not use Core +/// Location just for advertising, make sure it is used for more beneficial reasons as well. It is +/// both a good idea and part of Apple's guidelines. +- (void)setLocationWithLatitude:(CGFloat)latitude + longitude:(CGFloat)longitude + accuracy:(CGFloat)accuracyInMeters; + +/// [Optional] This method allows you to specify whether you would like your app to be treated as +/// child-directed for purposes of the Children’s Online Privacy Protection Act (COPPA), +/// http:///business.ftc.gov/privacy-and-security/childrens-privacy. +/// +/// If you call this method with YES, you are indicating that your app should be treated as +/// child-directed for purposes of the Children’s Online Privacy Protection Act (COPPA). If you call +/// this method with NO, you are indicating that your app should not be treated as child-directed +/// for purposes of the Children’s Online Privacy Protection Act (COPPA). If you do not call this +/// method, ad requests will include no indication of how you would like your app treated with +/// respect to COPPA. +/// +/// By setting this method, you certify that this notification is accurate and you are authorized to +/// act on behalf of the owner of the app. You understand that abuse of this setting may result in +/// termination of your Google account. +/// +/// It may take some time for this designation to be fully implemented in applicable Google +/// services. This designation will only apply to ad requests for which you have set this method. +- (void)tagForChildDirectedTreatment:(BOOL)childDirectedTreatment; + +#pragma mark Contextual Information + +/// Array of keyword strings. Keywords are words or phrases describing the current user activity +/// such as @"Sports Scores" or @"Football". Set this property to nil to clear the keywords. +@property(nonatomic, copy) NSArray *keywords; + +/// URL string for a webpage whose content matches the app content. This webpage content is used for +/// targeting purposes. +@property(nonatomic, copy) NSString *contentURL; + +#pragma mark Request Agent Information + +/// String that identifies the ad request's origin. Third party libraries that reference the Mobile +/// Ads SDK should set this property to denote the platform from which the ad request originated. +/// For example, a third party ad network called "CoolAds network" that is mediating requests to the +/// Mobile Ads SDK should set this property as "CoolAds". +@property(nonatomic, copy) NSString *requestAgent; + +#pragma mark Deprecated Methods + +/// Provide the user's birthday to increase ad relevancy. +- (void)setBirthdayWithMonth:(NSInteger)month + day:(NSInteger)day + year:(NSInteger)year + GAD_DEPRECATED_MSG_ATTRIBUTE(" use the birthday property."); + +/// When Core Location isn't available but the user's location is known supplying it here may +/// deliver more relevant ads. It can be any free-form text such as @"Champs-Elysees Paris" or +/// @"94041 US". +- (void)setLocationWithDescription:(NSString *)locationDescription + GAD_DEPRECATED_MSG_ATTRIBUTE(" use setLocationWithLatitude:longitude:accuracy:."); + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADRequestError.h b/GoogleMobileAds.framework/Versions/A/Headers/GADRequestError.h new file mode 100755 index 0000000..31175e4 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADRequestError.h @@ -0,0 +1,66 @@ +// +// GADRequestError.h +// Google Mobile Ads SDK +// +// Copyright 2011 Google Inc. All rights reserved. +// + +#import + +#import + +@class GADRequest; + +/// Google AdMob Ads error domain. +GAD_EXTERN NSString *const kGADErrorDomain; + +/// NSError codes for GAD error domain. +typedef NS_ENUM(NSInteger, GADErrorCode) { + /// The ad request is invalid. The localizedFailureReason error description will have more + /// details. Typically this is because the ad did not have the ad unit ID or root view + /// controller set. + kGADErrorInvalidRequest, + + /// The ad request was successful, but no ad was returned. + kGADErrorNoFill, + + /// There was an error loading data from the network. + kGADErrorNetworkError, + + /// The ad server experienced a failure processing the request. + kGADErrorServerError, + + /// The current device's OS is below the minimum required version. + kGADErrorOSVersionTooLow, + + /// The request was unable to be loaded before being timed out. + kGADErrorTimeout, + + /// Will not send request because the interstitial object has already been used. + kGADErrorInterstitialAlreadyUsed, + + /// The mediation response was invalid. + kGADErrorMediationDataError, + + /// Error finding or creating a mediation ad network adapter. + kGADErrorMediationAdapterError, + + /// The mediation request was successful, but no ad was returned from any ad networks. + kGADErrorMediationNoFill, + + /// Attempting to pass an invalid ad size to an adapter. + kGADErrorMediationInvalidAdSize, + + /// Internal error. + kGADErrorInternalError, + + /// Invalid argument error. + kGADErrorInvalidArgument, + + /// Received invalid response. + kGADErrorReceivedInvalidResponse +}; + +/// Represents the error generated due to invalid request parameters. +@interface GADRequestError : NSError +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADRewardBasedVideoAd.h b/GoogleMobileAds.framework/Versions/A/Headers/GADRewardBasedVideoAd.h new file mode 100755 index 0000000..b70cd24 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADRewardBasedVideoAd.h @@ -0,0 +1,34 @@ +// +// GADRewardBasedVideoAd.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import +#import + +@class GADRequest; + +@protocol GADRewardBasedVideoAdDelegate; + +@interface GADRewardBasedVideoAd : NSObject + +/// Delegate for receiving video notifications. +@property(nonatomic, weak) id delegate; + +/// Indicates if the receiver is ready to be presented full screen. +@property(nonatomic, readonly, assign, getter=isReady) BOOL ready; + +/// Singleton instance. ++ (GADRewardBasedVideoAd *)sharedInstance; + +/// Initiate the request to fetch the reward based video ad. +- (void)loadRequest:(GADRequest *)request + withAdUnitID:(NSString *)adUnitID + userID:(NSString *)userID; + +/// Present the reward based video ad with provided view controller. +- (void)presentFromRootViewController:(UIViewController *)viewController; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADRewardBasedVideoAdDelegate.h b/GoogleMobileAds.framework/Versions/A/Headers/GADRewardBasedVideoAdDelegate.h new file mode 100755 index 0000000..eb0f9d1 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADRewardBasedVideoAdDelegate.h @@ -0,0 +1,41 @@ +// +// GADRewardBasedVideoAdDelegate.h +// Google Mobile Ads SDK +// +// Copyright 2015 Google Inc. All rights reserved. +// + +#import + +#import + +/// Delegate for receiving state change messages from a GADRewardBasedVideoAd such as ad requests +/// succeeding/failing. +@protocol GADRewardBasedVideoAdDelegate + +@optional + +/// Tells the delegate that the reward based video ad has been received. +- (void)rewardBasedVideoAdDidReceiveAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd; + +/// Tells the delegate that the reward based video ad is opened. +- (void)rewardBasedVideoAdDidOpen:(GADRewardBasedVideoAd *)rewardBasedVideoAd; + +/// Tells the delegate that the reward based video ad has started playing. +- (void)rewardBasedVideoAdDidStartPlaying:(GADRewardBasedVideoAd *)rewardBasedVideoAd; + +/// Tells the delegate that the reward based video ad is closed. +- (void)rewardBasedVideoAdDidClose:(GADRewardBasedVideoAd *)rewardBasedVideoAd; + +/// Tells the delegate that the reward based video ad will leave the application. +- (void)rewardBasedVideoAdWillLeaveApplication:(GADRewardBasedVideoAd *)rewardBasedVideoAd; + +/// Tells the delegate that the reward based video ad has rewarded the user. +- (void)rewardBasedVideoAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd + didRewardUserWithReward:(GADAdReward *)reward; + +/// Tells the delegate that the reward based video ad has failed to load. +- (void)rewardBasedVideoAd:(GADRewardBasedVideoAd *)rewardBasedVideoAd + didFailToLoadwithError:(NSError *)error; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADSearchBannerView.h b/GoogleMobileAds.framework/Versions/A/Headers/GADSearchBannerView.h new file mode 100755 index 0000000..363ad24 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADSearchBannerView.h @@ -0,0 +1,16 @@ +// +// GADSearchBannerView.h +// Google Mobile Ads SDK +// +// Copyright 2011 Google Inc. All rights reserved. +// + +#import + +// A view that displays search ads. +// To show search ads: +// 1) Create a GADSearchBannerView and add it to your view controller's view hierarchy. +// 2) Create a GADSearchRequest ad request object to hold the search query and other search data. +// 3) Call GADSearchBannerView's -loadRequest: method with the GADSearchRequest object. +@interface GADSearchBannerView : GADBannerView +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GADSearchRequest.h b/GoogleMobileAds.framework/Versions/A/Headers/GADSearchRequest.h new file mode 100755 index 0000000..461852a --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GADSearchRequest.h @@ -0,0 +1,57 @@ +// +// GADSearchRequest.h +// Google Mobile Ads SDK +// +// Copyright 2011 Google Inc. All rights reserved. +// + +#import +#import + +#import + +/// Search ad border types. +typedef NS_ENUM(NSUInteger, GADSearchBorderType) { + kGADSearchBorderTypeNone, + kGADSearchBorderTypeDashed, + kGADSearchBorderTypeDotted, + kGADSearchBorderTypeSolid +}; + +typedef NS_ENUM(NSUInteger, GADSearchCallButtonColor) { + kGADSearchCallButtonLight, + kGADSearchCallButtonMedium, + kGADSearchCallButtonDark +}; + +// Specifies parameters for search ads. +@interface GADSearchRequest : GADRequest + +@property(nonatomic, copy) NSString *query; +@property(nonatomic, copy, readonly) UIColor *backgroundColor; +@property(nonatomic, copy, readonly) UIColor *gradientFrom; +@property(nonatomic, copy, readonly) UIColor *gradientTo; +@property(nonatomic, copy) UIColor *headerColor; +@property(nonatomic, copy) UIColor *descriptionTextColor; +@property(nonatomic, copy) UIColor *anchorTextColor; +@property(nonatomic, copy) NSString *fontFamily; +@property(nonatomic, assign) NSUInteger headerTextSize; +@property(nonatomic, copy) UIColor *borderColor; +@property(nonatomic, assign) GADSearchBorderType borderType; +@property(nonatomic, assign) NSUInteger borderThickness; +@property(nonatomic, copy) NSString *customChannels; +@property(nonatomic, assign) GADSearchCallButtonColor callButtonColor; + +// A solid background color for rendering the ad. The background of the ad +// can either be a solid color, or a gradient, which can be specified through +// setBackgroundGradientFrom:toColor: method. If both solid and gradient +// background is requested, only the latter is considered. +- (void)setBackgroundSolid:(UIColor *)color; + +// A linear gradient background color for rendering the ad. The background of +// the ad can either be a linear gradient, or a solid color, which can be +// specified through setBackgroundSolid method. If both solid and gradient +// background is requested, only the latter is considered. +- (void)setBackgroundGradientFrom:(UIColor *)from toColor:(UIColor *)toColor; + +@end diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GoogleMobileAds.h b/GoogleMobileAds.framework/Versions/A/Headers/GoogleMobileAds.h new file mode 100755 index 0000000..18962ef --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GoogleMobileAds.h @@ -0,0 +1,76 @@ +// +// GoogleMobileAds.h +// Google Mobile Ads SDK +// +// Copyright 2014 Google Inc. All rights reserved. + +#import +#import + +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0 +#error The Google Mobile Ads SDK requires a deployment target of iOS 6.0 or later. +#endif + +//! Project version string for GoogleMobileAds. +FOUNDATION_EXPORT const unsigned char GoogleMobileAdsVersionString[]; + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import +#import + +#import +#import +#import diff --git a/GoogleMobileAds.framework/Versions/A/Headers/GoogleMobileAdsDefines.h b/GoogleMobileAds.framework/Versions/A/Headers/GoogleMobileAdsDefines.h new file mode 100755 index 0000000..ad036c9 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/A/Headers/GoogleMobileAdsDefines.h @@ -0,0 +1,30 @@ +// +// GoogleMobileAdsDefines.h +// Google Mobile Ads SDK +// +// Copyright (c) 2015 Google Inc. All rights reserved. +// + +#if defined(__cplusplus) +#define GAD_EXTERN extern "C" __attribute__((visibility("default"))) +#else +#define GAD_EXTERN extern __attribute__((visibility("default"))) +#endif // defined(__cplusplus) + +#if defined(__has_feature) && defined(__has_attribute) +#if __has_feature(attribute_GAD_DEPRECATED_with_message) +#define GAD_DEPRECATED_MSG_ATTRIBUTE(s) __attribute__((deprecated(s))) +#elif __has_attribute(deprecated) +#define GAD_DEPRECATED_MSG_ATTRIBUTE(s) __attribute__((deprecated)) +#else +#define GAD_DEPRECATED_MSG_ATTRIBUTE(s) +#endif // __has_feature(attribute_GAD_DEPRECATED_with_message) +#if __has_attribute(deprecated) +#define GAD_DEPRECATED_ATTRIBUTE __attribute__((deprecated)) +#else +#define GAD_DEPRECATED_ATTRIBUTE +#endif // __has_attribute(deprecated) +#else +#define GAD_DEPRECATED_ATTRIBUTE +#define GAD_DEPRECATED_MSG_ATTRIBUTE(s) +#endif // defined(__has_feature) && defined(__has_attribute) diff --git a/GoogleMobileAds.framework/Versions/Current b/GoogleMobileAds.framework/Versions/Current new file mode 120000 index 0000000..8c7e5a6 --- /dev/null +++ b/GoogleMobileAds.framework/Versions/Current @@ -0,0 +1 @@ +A \ No newline at end of file diff --git a/ListApp.xcodeproj/project.pbxproj b/ListApp.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index 85063cc..89b2de6 --- a/ListApp.xcodeproj/project.pbxproj +++ b/ListApp.xcodeproj/project.pbxproj @@ -8,6 +8,15 @@ /* Begin PBXBuildFile section */ 9B3452FF1BF8AC9000FCE722 /* ListDetailVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B3452FE1BF8AC9000FCE722 /* ListDetailVC.swift */; }; + 9B71ED941BFCAD7900DE4DFB /* GoogleMobileAds.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B71ED931BFCAD7900DE4DFB /* GoogleMobileAds.framework */; }; + 9B71ED971BFD8DF500DE4DFB /* MenuVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B71ED961BFD8DF500DE4DFB /* MenuVC.swift */; }; + 9B71EDA31BFD905A00DE4DFB /* MMDrawerBarButtonItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B71ED9B1BFD905A00DE4DFB /* MMDrawerBarButtonItem.m */; }; + 9B71EDA41BFD905A00DE4DFB /* MMDrawerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B71ED9D1BFD905A00DE4DFB /* MMDrawerController.m */; }; + 9B71EDA51BFD905A00DE4DFB /* MMDrawerVisualState.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B71EDA01BFD905A00DE4DFB /* MMDrawerVisualState.m */; }; + 9B71EDA61BFD905A00DE4DFB /* UIViewController+MMDrawerController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9B71EDA21BFD905A00DE4DFB /* UIViewController+MMDrawerController.m */; }; + 9B71EDA81BFDADB700DE4DFB /* menuCustomTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B71EDA71BFDADB700DE4DFB /* menuCustomTableViewCell.swift */; }; + 9B71EDAA1BFDC0DE00DE4DFB /* AboutVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B71EDA91BFDC0DE00DE4DFB /* AboutVC.swift */; }; + 9B71EDAC1BFDC38800DE4DFB /* PrivacyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B71EDAB1BFDC38800DE4DFB /* PrivacyVC.swift */; }; 9B8B39171BF9FA7200FDC1E6 /* DataHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8B39161BF9FA7200FDC1E6 /* DataHelper.swift */; }; 9B8B39241BFA1FC800FDC1E6 /* ShopList+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8B39201BFA1FC800FDC1E6 /* ShopList+CoreDataProperties.swift */; }; 9B8B39251BFA1FC800FDC1E6 /* ShopList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B8B39211BFA1FC800FDC1E6 /* ShopList.swift */; }; @@ -22,6 +31,8 @@ 9BB1712E1BF744DC00FC8104 /* ListAppTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BB1712D1BF744DC00FC8104 /* ListAppTests.swift */; }; 9BB171391BF744DC00FC8104 /* ListAppUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BB171381BF744DC00FC8104 /* ListAppUITests.swift */; }; 9BB171581BF7482000FC8104 /* TableVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BB171571BF7482000FC8104 /* TableVC.swift */; }; + F9BCB1C81C01749500F1675F /* reminderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9BCB1C71C01749500F1675F /* reminderViewController.swift */; }; + F9BCB1CA1C01F0E500F1675F /* LocationReminderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9BCB1C91C01F0E500F1675F /* LocationReminderViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -43,6 +54,21 @@ /* Begin PBXFileReference section */ 9B3452FE1BF8AC9000FCE722 /* ListDetailVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListDetailVC.swift; sourceTree = ""; }; + 9B71ED931BFCAD7900DE4DFB /* GoogleMobileAds.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleMobileAds.framework; path = ../GoogleMobileAds.framework; sourceTree = ""; }; + 9B71ED961BFD8DF500DE4DFB /* MenuVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuVC.swift; sourceTree = ""; }; + 9B71ED991BFD905900DE4DFB /* ListApp-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ListApp-Bridging-Header.h"; sourceTree = ""; }; + 9B71ED9A1BFD905A00DE4DFB /* MMDrawerBarButtonItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDrawerBarButtonItem.h; sourceTree = ""; }; + 9B71ED9B1BFD905A00DE4DFB /* MMDrawerBarButtonItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMDrawerBarButtonItem.m; sourceTree = ""; }; + 9B71ED9C1BFD905A00DE4DFB /* MMDrawerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDrawerController.h; sourceTree = ""; }; + 9B71ED9D1BFD905A00DE4DFB /* MMDrawerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMDrawerController.m; sourceTree = ""; }; + 9B71ED9E1BFD905A00DE4DFB /* MMDrawerController+Subclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MMDrawerController+Subclass.h"; sourceTree = ""; }; + 9B71ED9F1BFD905A00DE4DFB /* MMDrawerVisualState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MMDrawerVisualState.h; sourceTree = ""; }; + 9B71EDA01BFD905A00DE4DFB /* MMDrawerVisualState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MMDrawerVisualState.m; sourceTree = ""; }; + 9B71EDA11BFD905A00DE4DFB /* UIViewController+MMDrawerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+MMDrawerController.h"; sourceTree = ""; }; + 9B71EDA21BFD905A00DE4DFB /* UIViewController+MMDrawerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIViewController+MMDrawerController.m"; sourceTree = ""; }; + 9B71EDA71BFDADB700DE4DFB /* menuCustomTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = menuCustomTableViewCell.swift; sourceTree = ""; }; + 9B71EDA91BFDC0DE00DE4DFB /* AboutVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutVC.swift; sourceTree = ""; }; + 9B71EDAB1BFDC38800DE4DFB /* PrivacyVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivacyVC.swift; sourceTree = ""; }; 9B8B39161BF9FA7200FDC1E6 /* DataHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataHelper.swift; sourceTree = ""; }; 9B8B39201BFA1FC800FDC1E6 /* ShopList+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ShopList+CoreDataProperties.swift"; sourceTree = ""; }; 9B8B39211BFA1FC800FDC1E6 /* ShopList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShopList.swift; sourceTree = ""; }; @@ -64,6 +90,8 @@ 9BB171381BF744DC00FC8104 /* ListAppUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAppUITests.swift; sourceTree = ""; }; 9BB1713A1BF744DC00FC8104 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9BB171571BF7482000FC8104 /* TableVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TableVC.swift; path = ../TableVC.swift; sourceTree = ""; }; + F9BCB1C71C01749500F1675F /* reminderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = reminderViewController.swift; path = ../reminderViewController.swift; sourceTree = ""; }; + F9BCB1C91C01F0E500F1675F /* LocationReminderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationReminderViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -71,6 +99,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9B71ED941BFCAD7900DE4DFB /* GoogleMobileAds.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -112,14 +141,45 @@ 9B3453021BF9020C00FCE722 /* Controllers */ = { isa = PBXGroup; children = ( + 9B71EDAB1BFDC38800DE4DFB /* PrivacyVC.swift */, + 9B71EDA91BFDC0DE00DE4DFB /* AboutVC.swift */, + 9B71EDA71BFDADB700DE4DFB /* menuCustomTableViewCell.swift */, + 9B71ED961BFD8DF500DE4DFB /* MenuVC.swift */, 9BB171151BF744DB00FC8104 /* AppDelegate.swift */, 9B3452FE1BF8AC9000FCE722 /* ListDetailVC.swift */, + F9BCB1C71C01749500F1675F /* reminderViewController.swift */, + F9BCB1C91C01F0E500F1675F /* LocationReminderViewController.swift */, 9BB171171BF744DB00FC8104 /* MainVC.swift */, 9BB171571BF7482000FC8104 /* TableVC.swift */, ); name = Controllers; sourceTree = ""; }; + 9B71ED951BFCB3B600DE4DFB /* Frameworks */ = { + isa = PBXGroup; + children = ( + 9B71ED931BFCAD7900DE4DFB /* GoogleMobileAds.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9B71ED981BFD8F9E00DE4DFB /* MenuView */ = { + isa = PBXGroup; + children = ( + 9B71ED9A1BFD905A00DE4DFB /* MMDrawerBarButtonItem.h */, + 9B71ED9B1BFD905A00DE4DFB /* MMDrawerBarButtonItem.m */, + 9B71ED9C1BFD905A00DE4DFB /* MMDrawerController.h */, + 9B71ED9D1BFD905A00DE4DFB /* MMDrawerController.m */, + 9B71ED9E1BFD905A00DE4DFB /* MMDrawerController+Subclass.h */, + 9B71ED9F1BFD905A00DE4DFB /* MMDrawerVisualState.h */, + 9B71EDA01BFD905A00DE4DFB /* MMDrawerVisualState.m */, + 9B71EDA11BFD905A00DE4DFB /* UIViewController+MMDrawerController.h */, + 9B71EDA21BFD905A00DE4DFB /* UIViewController+MMDrawerController.m */, + 9B71ED991BFD905900DE4DFB /* ListApp-Bridging-Header.h */, + ); + name = MenuView; + sourceTree = ""; + }; 9B8B39291BFA2C8100FDC1E6 /* Helper */ = { isa = PBXGroup; children = ( @@ -151,6 +211,8 @@ 9BB171141BF744DB00FC8104 /* ListApp */ = { isa = PBXGroup; children = ( + 9B71ED981BFD8F9E00DE4DFB /* MenuView */, + 9B71ED951BFCB3B600DE4DFB /* Frameworks */, 9B8B39291BFA2C8100FDC1E6 /* Helper */, 9B3453021BF9020C00FCE722 /* Controllers */, 9B3453011BF8FC1F00FCE722 /* Assets */, @@ -252,6 +314,7 @@ 9BB1710A1BF744DB00FC8104 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 0700; LastUpgradeCheck = 0700; ORGANIZATIONNAME = BAPAT; TargetAttributes = { @@ -324,12 +387,22 @@ buildActionMask = 2147483647; files = ( 9BB1711E1BF744DB00FC8104 /* ListApp.xcdatamodeld in Sources */, + 9B71EDA31BFD905A00DE4DFB /* MMDrawerBarButtonItem.m in Sources */, 9BB171581BF7482000FC8104 /* TableVC.swift in Sources */, + 9B71EDA61BFD905A00DE4DFB /* UIViewController+MMDrawerController.m in Sources */, 9B8B39241BFA1FC800FDC1E6 /* ShopList+CoreDataProperties.swift in Sources */, + F9BCB1CA1C01F0E500F1675F /* LocationReminderViewController.swift in Sources */, 9BB171181BF744DB00FC8104 /* MainVC.swift in Sources */, + 9B71EDA41BFD905A00DE4DFB /* MMDrawerController.m in Sources */, + F9BCB1C81C01749500F1675F /* reminderViewController.swift in Sources */, + 9B71EDAC1BFDC38800DE4DFB /* PrivacyVC.swift in Sources */, + 9B71EDA51BFD905A00DE4DFB /* MMDrawerVisualState.m in Sources */, 9BB171161BF744DB00FC8104 /* AppDelegate.swift in Sources */, 9B3452FF1BF8AC9000FCE722 /* ListDetailVC.swift in Sources */, + 9B71ED971BFD8DF500DE4DFB /* MenuVC.swift in Sources */, + 9B71EDA81BFDADB700DE4DFB /* menuCustomTableViewCell.swift in Sources */, 9B8B39171BF9FA7200FDC1E6 /* DataHelper.swift in Sources */, + 9B71EDAA1BFDC0DE00DE4DFB /* AboutVC.swift in Sources */, 9B8B39271BFA1FC800FDC1E6 /* BuyItems.swift in Sources */, 9B8B39251BFA1FC800FDC1E6 /* ShopList.swift in Sources */, 9B8B39261BFA1FC800FDC1E6 /* BuyItems+CoreDataProperties.swift in Sources */, @@ -471,29 +544,59 @@ 9BB1713E1BF744DC00FC8104 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + "FRAMEWORK_SEARCH_PATHS[arch=*]" = "$(inherited)"; INFOPLIST_FILE = ListApp/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/GoogleMobileAds.framework/Versions/A", + ); PRODUCT_BUNDLE_IDENTIFIER = SB.ListApp; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "ListApp/ListApp-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; 9BB1713F1BF744DC00FC8104 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + "FRAMEWORK_SEARCH_PATHS[arch=*]" = "$(inherited)"; INFOPLIST_FILE = ListApp/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/GoogleMobileAds.framework/Versions/A", + ); PRODUCT_BUNDLE_IDENTIFIER = SB.ListApp; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "ListApp/ListApp-Bridging-Header.h"; }; name = Release; }; 9BB171411BF744DC00FC8104 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); INFOPLIST_FILE = ListAppTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = SB.ListAppTests; @@ -505,7 +608,12 @@ 9BB171421BF744DC00FC8104 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_SEARCH_USER_PATHS = YES; BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); INFOPLIST_FILE = ListAppTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = SB.ListAppTests; diff --git a/ListApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ListApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata old mode 100644 new mode 100755 diff --git a/ListApp.xcodeproj/project.xcworkspace/xcuserdata/Connie.xcuserdatad/UserInterfaceState.xcuserstate b/ListApp.xcodeproj/project.xcworkspace/xcuserdata/Connie.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..a6486b6 Binary files /dev/null and b/ListApp.xcodeproj/project.xcworkspace/xcuserdata/Connie.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ListApp.xcodeproj/project.xcworkspace/xcuserdata/Suchi.xcuserdatad/UserInterfaceState.xcuserstate b/ListApp.xcodeproj/project.xcworkspace/xcuserdata/Suchi.xcuserdatad/UserInterfaceState.xcuserstate old mode 100644 new mode 100755 index b2be297..ae03e90 Binary files a/ListApp.xcodeproj/project.xcworkspace/xcuserdata/Suchi.xcuserdatad/UserInterfaceState.xcuserstate and b/ListApp.xcodeproj/project.xcworkspace/xcuserdata/Suchi.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..fe2b454 --- /dev/null +++ b/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcschemes/ListApp.xcscheme b/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcschemes/ListApp.xcscheme new file mode 100644 index 0000000..1bf73ca --- /dev/null +++ b/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcschemes/ListApp.xcscheme @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcschemes/xcschememanagement.plist b/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..c659cc7 --- /dev/null +++ b/ListApp.xcodeproj/xcuserdata/Connie.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,32 @@ + + + + + SchemeUserState + + ListApp.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 9BB171111BF744DB00FC8104 + + primary + + + 9BB171281BF744DC00FC8104 + + primary + + + 9BB171331BF744DC00FC8104 + + primary + + + + + diff --git a/ListApp.xcodeproj/xcuserdata/Suchi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/ListApp.xcodeproj/xcuserdata/Suchi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist old mode 100644 new mode 100755 index 264c3a8..dedfb03 --- a/ListApp.xcodeproj/xcuserdata/Suchi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/ListApp.xcodeproj/xcuserdata/Suchi.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -42,11 +42,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TableVC.swift" - timestampString = "469198273.964704" + timestampString = "469608753.652479" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "47" - endingLineNumber = "47" + startingLineNumber = "53" + endingLineNumber = "53" landmarkName = "viewDidLoad()" landmarkType = "5"> @@ -147,5 +147,67 @@ landmarkType = "5"> + + + + + + + + + + + + + + + + diff --git a/ListApp.xcodeproj/xcuserdata/Suchi.xcuserdatad/xcschemes/ListApp.xcscheme b/ListApp.xcodeproj/xcuserdata/Suchi.xcuserdatad/xcschemes/ListApp.xcscheme old mode 100644 new mode 100755 diff --git a/ListApp.xcodeproj/xcuserdata/Suchi.xcuserdatad/xcschemes/xcschememanagement.plist b/ListApp.xcodeproj/xcuserdata/Suchi.xcuserdatad/xcschemes/xcschememanagement.plist old mode 100644 new mode 100755 diff --git a/ListApp/AboutVC.swift b/ListApp/AboutVC.swift new file mode 100755 index 0000000..9838277 --- /dev/null +++ b/ListApp/AboutVC.swift @@ -0,0 +1,35 @@ +// +// AboutVC.swift +// ListApp +// +// Created by Suruchi on 19/11/2015. +// Copyright © 2015 BAPAT. All rights reserved. +// + +import UIKit + +class AboutVC: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + // Get the new view controller using segue.destinationViewController. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/ListApp/AppDelegate.swift b/ListApp/AppDelegate.swift old mode 100644 new mode 100755 index 09fc70e..f261db2 --- a/ListApp/AppDelegate.swift +++ b/ListApp/AppDelegate.swift @@ -8,13 +8,17 @@ import UIKit import CoreData +import EventKit + @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? + var eventStore: EKEventStore? - + var centerContainer:MMDrawerController? + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // Override point for customization after application launch. @@ -24,6 +28,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate { //dataHelper.seedDataStore() //dataHelper.printAllLists() //dataHelper.printAllBuyItems() + + //****** MMDrawerController + + var rootViewController = self.window!.rootViewController + + let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) + + var centerViewController = mainStoryboard.instantiateViewControllerWithIdentifier("TableVC") as! TableVC + var menuViewController = mainStoryboard.instantiateViewControllerWithIdentifier("MenuVC") as! MenuVC + + var menuNav = UINavigationController(rootViewController: menuViewController) + var centerNav = UINavigationController(rootViewController: centerViewController) + + centerContainer = MMDrawerController(centerViewController: centerNav, leftDrawerViewController: menuNav) + + centerContainer!.openDrawerGestureModeMask = MMOpenDrawerGestureMode.PanningCenterView; + centerContainer!.closeDrawerGestureModeMask = MMCloseDrawerGestureMode.PanningCenterView; + centerContainer!.maximumLeftDrawerWidth = 130 + + window!.rootViewController = centerContainer + window!.makeKeyAndVisible() return true } diff --git a/ListApp/Assets.xcassets/80x80_Logo.png b/ListApp/Assets.xcassets/80x80_Logo.png new file mode 100755 index 0000000..c0046ff Binary files /dev/null and b/ListApp/Assets.xcassets/80x80_Logo.png differ diff --git a/ListApp/Assets.xcassets/AppIcon.appiconset/80x80_Logo-1.png b/ListApp/Assets.xcassets/AppIcon.appiconset/80x80_Logo-1.png new file mode 100755 index 0000000..c0046ff Binary files /dev/null and b/ListApp/Assets.xcassets/AppIcon.appiconset/80x80_Logo-1.png differ diff --git a/ListApp/Assets.xcassets/AppIcon.appiconset/80x80_Logo.png b/ListApp/Assets.xcassets/AppIcon.appiconset/80x80_Logo.png new file mode 100755 index 0000000..c0046ff Binary files /dev/null and b/ListApp/Assets.xcassets/AppIcon.appiconset/80x80_Logo.png differ diff --git a/ListApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/ListApp/Assets.xcassets/AppIcon.appiconset/Contents.json old mode 100644 new mode 100755 index 118c98f..1a7c082 --- a/ListApp/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/ListApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -11,8 +11,9 @@ "scale" : "3x" }, { - "idiom" : "iphone", "size" : "40x40", + "idiom" : "iphone", + "filename" : "80x80_Logo-1.png", "scale" : "2x" }, { @@ -21,8 +22,9 @@ "scale" : "3x" }, { - "idiom" : "iphone", "size" : "60x60", + "idiom" : "iphone", + "filename" : "80x80_Logo.png", "scale" : "2x" }, { diff --git a/ListApp/Assets.xcassets/Contents.json b/ListApp/Assets.xcassets/Contents.json old mode 100644 new mode 100755 diff --git a/ListApp/Assets.xcassets/LaunchImage.imageset/Contents.json b/ListApp/Assets.xcassets/LaunchImage.imageset/Contents.json old mode 100644 new mode 100755 diff --git a/ListApp/Assets.xcassets/LaunchImage.imageset/Screen Shot 2015-09-15 at 3.57.24 pm.png b/ListApp/Assets.xcassets/LaunchImage.imageset/Screen Shot 2015-09-15 at 3.57.24 pm.png old mode 100644 new mode 100755 diff --git a/ListApp/Assets.xcassets/Menu Image.png b/ListApp/Assets.xcassets/Menu Image.png new file mode 100755 index 0000000..2a2a491 Binary files /dev/null and b/ListApp/Assets.xcassets/Menu Image.png differ diff --git a/ListApp/Assets.xcassets/Menu.imageset/Contents.json b/ListApp/Assets.xcassets/Menu.imageset/Contents.json new file mode 100755 index 0000000..9c77dea --- /dev/null +++ b/ListApp/Assets.xcassets/Menu.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Menu Image.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ListApp/Assets.xcassets/Menu.imageset/Menu Image.png b/ListApp/Assets.xcassets/Menu.imageset/Menu Image.png new file mode 100755 index 0000000..2a2a491 Binary files /dev/null and b/ListApp/Assets.xcassets/Menu.imageset/Menu Image.png differ diff --git a/ListApp/Assets.xcassets/Tick.imageset/Contents.json b/ListApp/Assets.xcassets/Tick.imageset/Contents.json new file mode 100755 index 0000000..e060d48 --- /dev/null +++ b/ListApp/Assets.xcassets/Tick.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Tick.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ListApp/Assets.xcassets/Tick.imageset/Tick.png b/ListApp/Assets.xcassets/Tick.imageset/Tick.png new file mode 100755 index 0000000..8b97bb7 Binary files /dev/null and b/ListApp/Assets.xcassets/Tick.imageset/Tick.png differ diff --git a/ListApp/Assets.xcassets/Tick.png b/ListApp/Assets.xcassets/Tick.png new file mode 100755 index 0000000..8b97bb7 Binary files /dev/null and b/ListApp/Assets.xcassets/Tick.png differ diff --git a/ListApp/Base.lproj/LaunchScreen.storyboard b/ListApp/Base.lproj/LaunchScreen.storyboard old mode 100644 new mode 100755 diff --git a/ListApp/Base.lproj/Main.storyboard b/ListApp/Base.lproj/Main.storyboard old mode 100644 new mode 100755 index c755139..14907b8 --- a/ListApp/Base.lproj/Main.storyboard +++ b/ListApp/Base.lproj/Main.storyboard @@ -1,25 +1,155 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + - - + + - + @@ -52,17 +182,21 @@ - - + + - + + + + + - + @@ -76,7 +210,7 @@ - + @@ -85,25 +219,25 @@ - + - + - + - - + - - + + - + - + - + - + - + - - - - - - + + + + + + @@ -259,12 +393,12 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + diff --git a/ListApp/BuyItems+CoreDataProperties.swift b/ListApp/BuyItems+CoreDataProperties.swift old mode 100644 new mode 100755 diff --git a/ListApp/BuyItems.swift b/ListApp/BuyItems.swift old mode 100644 new mode 100755 diff --git a/ListApp/DataHelper.swift b/ListApp/DataHelper.swift old mode 100644 new mode 100755 diff --git a/ListApp/Info.plist b/ListApp/Info.plist old mode 100644 new mode 100755 diff --git a/ListApp/ListApp-Bridging-Header.h b/ListApp/ListApp-Bridging-Header.h new file mode 100755 index 0000000..adb39e4 --- /dev/null +++ b/ListApp/ListApp-Bridging-Header.h @@ -0,0 +1,6 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + +#import "MMDrawerController.h" + diff --git a/ListApp/ListApp.xcdatamodeld/.xccurrentversion b/ListApp/ListApp.xcdatamodeld/.xccurrentversion old mode 100644 new mode 100755 diff --git a/ListApp/ListApp.xcdatamodeld/ListApp 2.xcdatamodel/contents b/ListApp/ListApp.xcdatamodeld/ListApp 2.xcdatamodel/contents old mode 100644 new mode 100755 diff --git a/ListApp/ListApp.xcdatamodeld/ListApp.xcdatamodel/contents b/ListApp/ListApp.xcdatamodeld/ListApp.xcdatamodel/contents old mode 100644 new mode 100755 diff --git a/ListApp/ListDetailVC.swift b/ListApp/ListDetailVC.swift index ec35828..8254ccd 100755 --- a/ListApp/ListDetailVC.swift +++ b/ListApp/ListDetailVC.swift @@ -250,7 +250,7 @@ class ListDetailVC: UIViewController,UITableViewDataSource , UIPickerViewDelegat @IBAction func cancleDetailView(sender: UIBarButtonItem) { dismissVC() - print("cancel") + // print("cancel") } func dismissVC (){ diff --git a/ListApp/LocationReminderViewController.swift b/ListApp/LocationReminderViewController.swift new file mode 100644 index 0000000..62cbc09 --- /dev/null +++ b/ListApp/LocationReminderViewController.swift @@ -0,0 +1,107 @@ +// +// LocationReminderViewController.swift +// ListApp +// +// Created by Guanghui Ji on 22/11/2015. +// Copyright © 2015 BAPAT. All rights reserved. +// + +import UIKit + +import EventKit +import CoreLocation + + +class LocationReminderViewController: UIViewController, CLLocationManagerDelegate{ + + @IBOutlet var locationText: UITextField! + var appDelegate: AppDelegate? + var locationManager: CLLocationManager = CLLocationManager() + //var locationManager: CLLocationManager! + + + override func viewDidLoad() { + super.viewDidLoad() + + locationManager.requestWhenInUseAuthorization() + locationManager.delegate = self + locationManager.distanceFilter = kCLDistanceFilterNone + locationManager.desiredAccuracy = kCLLocationAccuracyBest + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + @IBAction func setLocationReminder(sender: AnyObject) { + + appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate + + if appDelegate!.eventStore == nil { + appDelegate!.eventStore = EKEventStore() + appDelegate!.eventStore!.requestAccessToEntityType(EKEntityType.Reminder, completion: + {(granted, error) in + if !granted + { + print("Access to store not granted") + print(error!.localizedDescription) + } + else + { + print("Access granted") + } + }) + + } + + if (appDelegate!.eventStore != nil) { + //locationManager.startUpdatingLocation() + locationManager.requestLocation() + } + } + + + + + func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){ + + locationManager.stopUpdatingLocation() + + let reminder = EKReminder(eventStore: appDelegate!.eventStore!) + reminder.title = locationText.text! + reminder.calendar = + appDelegate!.eventStore!.defaultCalendarForNewReminders() + + let location = EKStructuredLocation(title: "Current Location") + location.geoLocation = locations.last + + let alarm = EKAlarm() + + alarm.structuredLocation = location + alarm.proximity = EKAlarmProximity.Leave + + reminder.addAlarm(alarm) + +// var error: NSError? +// +// appDelegate!.eventStore?.saveReminder(reminder, +// commit: true, error: &error) +// +// if error != nil { +// print("Reminder failed with error \(error?.localizedDescription)") +// } + do { + try appDelegate?.eventStore?.saveReminder(reminder, commit: true) + print(3) + } catch let error as NSError{ + + print("Reminder failed with error \(error.localizedDescription)") + } + + } + + func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { + print(" Failed to get location \(error.localizedDescription)") + } +} diff --git a/ListApp/MMDrawerBarButtonItem.h b/ListApp/MMDrawerBarButtonItem.h new file mode 100755 index 0000000..f0ee364 --- /dev/null +++ b/ListApp/MMDrawerBarButtonItem.h @@ -0,0 +1,77 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 + +/** + `MMDrawerBarButtonItem` provides convenience methods to create `UIBarButtonItems` with a default hamburger-menu asset. + */ + +@interface MMDrawerBarButtonItem : UIBarButtonItem + +///--------------------------------------- +/// @name Initializing a `MMDrawerBarButtonItem` +///--------------------------------------- + +/** + Creates and initializes an `MMDrawerBarButtonItem` without a border. + + @param target The target to forward the `action` to when the button is pressed. + @param action The action to call when the button is pressed. + + @return The newly-initialized bar button item. + */ +-(instancetype)initWithTarget:(id)target action:(SEL)action; + +/** + Returns the current color of the menu button for the state requested. This property is deprecated in iOS 7.0. Use `tintColor` instead. + + @param state The UIControl state that the color is being requested for. + + @return The menu button color for the requested state. + */ +-(UIColor *)menuButtonColorForState:(UIControlState)state __attribute__((deprecated("Use tintColor instead"))); + +/** + Sets the color of the menu button for the specified state. For this control, only set colors for `UIControlStateNormal` and `UIControlStateHighlighted`. This property is deprecated in iOS 7.0. Use `tintColor` instead. + + @param color The color to set. + @param state The state to set the color for. + */ +-(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state __attribute__((deprecated("Use tintColor instead"))); + +/** + Returns the current color of the shadow for the state requested. This property is deprecated in iOS 7.0. The menu button no longer supports a shadow. + + @param state The UIControl state that the color is being requested for. + + @return The menu button color for the requested state. + */ +-(UIColor *)shadowColorForState:(UIControlState)state __attribute__((deprecated("Shadow is no longer supported"))); + +/** + Sets the color of the shadow for the specified state. For this control, only set colors for `UIControlStateNormal` and `UIControlStateHighlighted`. This property is deprecated in iOS 7.0. The menu button no longer supports a shadow. + + @param color The color to set. + @param state The state to set the color for. + */ +-(void)setShadowColor:(UIColor *)color forState:(UIControlState)state __attribute__((deprecated("Shadow is no longer supported"))); + +@end diff --git a/ListApp/MMDrawerBarButtonItem.m b/ListApp/MMDrawerBarButtonItem.m new file mode 100755 index 0000000..d29609c --- /dev/null +++ b/ListApp/MMDrawerBarButtonItem.m @@ -0,0 +1,302 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "MMDrawerBarButtonItem.h" + +@interface MMDrawerMenuButtonView : UIButton +@property (nonatomic,strong) UIColor * menuButtonNormalColor; +@property (nonatomic,strong) UIColor * menuButtonHighlightedColor; + +@property (nonatomic,strong) UIColor * shadowNormalColor; +@property (nonatomic,strong) UIColor * shadowHighlightedColor; + +-(UIColor *)menuButtonColorForState:(UIControlState)state; +-(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state; + +-(UIColor *)shadowColorForState:(UIControlState)state; +-(void)setShadowColor:(UIColor *)color forState:(UIControlState)state; + +@end + +@implementation MMDrawerMenuButtonView + +-(instancetype)initWithFrame:(CGRect)frame{ + self = [super initWithFrame:frame]; + if(self){ + [self setMenuButtonNormalColor:[[UIColor whiteColor] colorWithAlphaComponent:0.9f]]; + [self setMenuButtonHighlightedColor:[UIColor colorWithRed:139.0/255.0 + green:135.0/255.0 + blue:136.0/255.0 + alpha:0.9f]]; + + [self setShadowNormalColor:[[UIColor blackColor] colorWithAlphaComponent:0.5f]]; + [self setShadowHighlightedColor:[[UIColor blackColor] colorWithAlphaComponent:0.2f]]; + } + return self; +} + +-(UIColor *)menuButtonColorForState:(UIControlState)state{ + UIColor * color; + switch (state) { + case UIControlStateNormal: + color = self.menuButtonNormalColor; + break; + case UIControlStateHighlighted: + color = self.menuButtonHighlightedColor; + break; + default: + break; + } + return color; +} + +-(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state{ + switch (state) { + case UIControlStateNormal: + [self setMenuButtonNormalColor:color]; + break; + case UIControlStateHighlighted: + [self setMenuButtonHighlightedColor:color]; + break; + default: + break; + } + [self setNeedsDisplay]; +} + +-(UIColor *)shadowColorForState:(UIControlState)state{ + UIColor * color; + switch (state) { + case UIControlStateNormal: + color = self.shadowNormalColor; + break; + case UIControlStateHighlighted: + color = self.shadowHighlightedColor; + break; + default: + break; + } + return color; +} + +-(void)setShadowColor:(UIColor *)color forState:(UIControlState)state{ + switch (state) { + case UIControlStateNormal: + [self setShadowNormalColor:color]; + break; + case UIControlStateHighlighted: + [self setShadowHighlightedColor:color]; + break; + default: + break; + } + [self setNeedsDisplay]; +} + +-(void)drawRect:(CGRect)rect{ + //// General Declarations + CGContextRef context = UIGraphicsGetCurrentContext(); + + //Sizes + CGFloat buttonWidth = CGRectGetWidth(self.bounds)*.80; + CGFloat buttonHeight = CGRectGetHeight(self.bounds)*.16; + CGFloat xOffset = CGRectGetWidth(self.bounds)*.10; + CGFloat yOffset = CGRectGetHeight(self.bounds)*.12; + CGFloat cornerRadius = 1.0; + + //// Color Declarations + UIColor* buttonColor = [self menuButtonColorForState:self.state]; + UIColor* shadowColor = [self shadowColorForState:self.state]; + + + //// Shadow Declarations + UIColor* shadow = shadowColor; + CGSize shadowOffset = CGSizeMake(0.0, 1.0); + CGFloat shadowBlurRadius = 0; + + //// Top Bun Drawing + UIBezierPath* topBunPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); + [buttonColor setFill]; + [topBunPath fill]; + CGContextRestoreGState(context); + + //// Meat Drawing + UIBezierPath* meatPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset*2 + buttonHeight, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); + [buttonColor setFill]; + [meatPath fill]; + CGContextRestoreGState(context); + + //// Bottom Bun Drawing + UIBezierPath* bottomBunPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset*3 + buttonHeight*2, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); + [buttonColor setFill]; + [bottomBunPath fill]; + CGContextRestoreGState(context); +} + +-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ + [super touchesBegan:touches withEvent:event]; + [self setNeedsDisplay]; +} + +-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ + [super touchesEnded:touches withEvent:event]; + [self setNeedsDisplay]; +} + +-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{ + [super touchesCancelled:touches withEvent:event]; + [self setNeedsDisplay]; +} + +-(void)setSelected:(BOOL)selected{ + [super setSelected:selected]; + [self setNeedsDisplay]; +} + +-(void)setHighlighted:(BOOL)highlighted{ + [super setHighlighted:highlighted]; + [self setNeedsDisplay]; +} + +-(void)setTintColor:(UIColor *)tintColor{ + if([super respondsToSelector:@selector(setTintColor:)]){ + [super setTintColor:tintColor]; + } +} + +-(void)tintColorDidChange{ + [self setNeedsDisplay]; +} + +@end + +@interface MMDrawerBarButtonItem () +@property (nonatomic,strong) MMDrawerMenuButtonView * buttonView; + +@end + +@implementation MMDrawerBarButtonItem + ++(UIImage*)drawerButtonItemImage{ + + static UIImage *drawerButtonImage = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + UIGraphicsBeginImageContextWithOptions( CGSizeMake(26, 26), NO, 0 ); + + //// Color Declarations + UIColor* fillColor = [UIColor whiteColor]; + + //// Frames + CGRect frame = CGRectMake(0, 0, 26, 26); + + //// Bottom Bar Drawing + UIBezierPath* bottomBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.72000 + 0.5), 16, 1)]; + [fillColor setFill]; + [bottomBarPath fill]; + + + //// Middle Bar Drawing + UIBezierPath* middleBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.48000 + 0.5), 16, 1)]; + [fillColor setFill]; + [middleBarPath fill]; + + + //// Top Bar Drawing + UIBezierPath* topBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.24000 + 0.5), 16, 1)]; + [fillColor setFill]; + [topBarPath fill]; + + drawerButtonImage = UIGraphicsGetImageFromCurrentImageContext(); + }); + + return drawerButtonImage; +} + +-(instancetype)initWithTarget:(id)target action:(SEL)action{ + + if((floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1)){ + return [self initWithImage:[self.class drawerButtonItemImage] + style:UIBarButtonItemStylePlain + target:target + action:action]; + } + else { + MMDrawerMenuButtonView * buttonView = [[MMDrawerMenuButtonView alloc] initWithFrame:CGRectMake(0, 0, 26, 26)]; + [buttonView addTarget:self action:@selector(touchUpInside:) forControlEvents:UIControlEventTouchUpInside]; + self = [self initWithCustomView:buttonView]; + if(self){ + [self setButtonView:buttonView]; + } + self.action = action; + self.target = target; + return self; + } +} + +-(instancetype)initWithCoder:(NSCoder *)aDecoder{ + // non-ideal way to get the target/action, but it works + UIBarButtonItem* barButtonItem = [[UIBarButtonItem alloc] initWithCoder: aDecoder]; + return [self initWithTarget:barButtonItem.target action:barButtonItem.action]; +} + +-(void)touchUpInside:(id)sender{ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [self.target performSelector:self.action withObject:sender]; +#pragma clang diagnostic pop; + +} + +-(UIColor *)menuButtonColorForState:(UIControlState)state{ + return [self.buttonView menuButtonColorForState:state]; +} + +-(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state{ + [self.buttonView setMenuButtonColor:color forState:state]; +} + +-(UIColor *)shadowColorForState:(UIControlState)state{ + return [self.buttonView shadowColorForState:state]; +} + +-(void)setShadowColor:(UIColor *)color forState:(UIControlState)state{ + [self.buttonView setShadowColor:color forState:state]; +} + +-(void)setTintColor:(UIColor *)tintColor{ + if([super respondsToSelector:@selector(setTintColor:)]){ + [super setTintColor:tintColor]; + } + if([self.buttonView respondsToSelector:@selector(setTintColor:)]){ + [self.buttonView setTintColor:tintColor]; + } +} + +@end diff --git a/ListApp/MMDrawerController+Subclass.h b/ListApp/MMDrawerController+Subclass.h new file mode 100755 index 0000000..419ff68 --- /dev/null +++ b/ListApp/MMDrawerController+Subclass.h @@ -0,0 +1,111 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "MMDrawerController.h" + +/** + This class extension is designed for use by subclasses of `MMDrawerController` to customize the functionality to support a specific use-case by a developer. When importing this file, there is no need to also call `#import MMDrawerController.h`. + + None of these methods are meant to be called by non-subclasses of `MMDrawerController`. + */ + +@interface MMDrawerController (Subclass) +///--------------------------------------- +/// @name Gesture Interaction +///--------------------------------------- +/** + `MMDrawerController`'s single-tap gesture recognizer callback. This method is called every time the `UITapGestureRecognizer` is triggered. + + @param tapGesture The single-tap gesture recognizer instance that triggered the callback + */ +-(void)tapGestureCallback:(UITapGestureRecognizer *)tapGesture __attribute((objc_requires_super)); + +/** + `MMDrawerController`'s pan gesture recognizer callback. This method is called every time the `UIPanGestureRecognizer` is updated. + + @warning This method do the minimal amount of work to keep the pan gesture responsive. + + @param panGesture The pan gesture recognizer instance that triggered the callback + */ +-(void)panGestureCallback:(UIPanGestureRecognizer *)panGesture __attribute((objc_requires_super)); + +/** + A `UIGestureRecognizerDelegate` method that is queried by `MMDrawerController`'s gestures to determine if it should receive the touch. + + @param gestureRecognizer The gesture recognizer that is asking if it should recieve a touch + @param touch The touch in question in gestureRecognizer.view's coordinate space + */ +-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch __attribute((objc_requires_super)); + +///--------------------------------------- +/// @name Drawer Presentation +///--------------------------------------- +/** + Sets the initial conditions for `MMDrawerController` and its child view controllers to prepare the drawer for a transition. If a drawer is open and the opposite drawer is being presented, it prepares that drawer to be hidden and vice-versa for the closing drawer. + + @param drawer The drawer side that will be presented + @param animated A boolean that indicates whether the presentation is being animated or not + */ +-(void)prepareToPresentDrawer:(MMDrawerSide)drawer animated:(BOOL)animated __attribute((objc_requires_super)); + +///--------------------------------------- +/// @name Opening/Closing Drawer +///--------------------------------------- +/** + The method that handles closing the drawer. You can subclass this method to get a callback every time the drawer is about to be closed. You can inspect the current open side to determine what side is about to be closed. + + @param animated A boolean that indicates whether the drawer should close with animation + @param velocity A float indicating how fast the drawer should close + @param animationOptions A mask defining the animation options of the animation + @param completion A completion block to be called when the drawer is finished closing + */ +-(void)closeDrawerAnimated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL))completion __attribute((objc_requires_super)); + +/** + The method that handles opening the drawer. You can subclass this method to get a callback every time the drawer is about to be opened. + + @param drawerSide The drawer side that will be opened + @param animated A boolean that indicates whether the drawer should open with animation + @param velocity A float indicating how fast the drawer should open + @param animationOptions A mask defining the animation options of the animation + @param completion A completion block to be called when the drawer is finished opening + */ +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL))completion __attribute((objc_requires_super)); + +///--------------------------------------- +/// @name `UIViewController` Subclass Methods +///--------------------------------------- +/** + Included here to ensure subclasses call `super`. + + @param toInterfaceOrientation The interface orientation that the interface is moving to + @param duration The duration of the interface orientation animation + */ +-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration __attribute((objc_requires_super)); + +/** + Included here to ensure subclasses call `super`. + + @param toInterfaceOrientation The interface orientation that the interface is moving to + @param duration The duration of the interface orientation animation + */ +-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration __attribute((objc_requires_super)); + +@end diff --git a/ListApp/MMDrawerController.h b/ListApp/MMDrawerController.h new file mode 100755 index 0000000..92833a6 --- /dev/null +++ b/ListApp/MMDrawerController.h @@ -0,0 +1,455 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 + +/** + `MMDrawerController` is a side drawer navigation container view controller designed to support the growing number of applications that leverage the side drawer paradigm. This library is designed to exclusively support side drawer navigation in light-weight, focused approach. + + ## Creating a MMDrawerController + `MMDrawerController` is a container view controller, similiar to `UINavigationController` or `UITabBarController`, with up to three child view controllers - Center, LeftDrawer, and RightDrawer. To create a `MMDrawerController`, you must first instantiate the drawer view controllers and the initial center controller, then call one of the init methods listed in this class. + + ## Handling a UINavigationController as the centerViewController + `MMDrawerController` automatically supports handling a `UINavigationController` as the `centerViewController`, and will correctly handle the proper gestures on each view (the navigation bar view as well as the content view for the visible view controller). Note that while this library does support other container view controllers, the open/close gestures are not customized to support them. + + ## Accessing MMDrawerController from the Child View Controller + You can leverage the category class (UIViewController+MMDrawerController) included with this library to access information about the parent `MMDrawerController`. Note that if you are contained within a UINavigationController, the `drawerContainerViewController` will still return the proper reference to the `drawerContainerViewController` parent, even though it is not the direct parent. Refer to the documentation included with the category for more information. + + ## How MMDrawerOpenCenterInteractionMode is handled + `MMDrawerOpenCenterInteractionMode` controls how the user should be able to interact with the center view controller when either drawer is open. By default, this is set to `MMDrawerOpenCenterInteractionModeNavigationBarOnly`, which allows the user to interact with UINavigationBarItems while either drawer is open (typicaly used to click the menu button to close). If you set the interaction mode to `MMDrawerOpenCenterInteractionModeNone`, no items within the center view will be able to be interacted with while a drawer is open. Note that this setting has no effect at all on the `MMCloseDrawerGestureMode`. + + ## How Open/Close Gestures are handled + Two gestures are added to every instance of a drawer controller, one for pan and one for touch. `MMDrawerController` is the delegate for each of the gesture recoginzers, and determines if a touch should be sent to the appropriate gesture when a touch is detected compared with the masks set for open and close gestures and the state of the drawer controller. + + ## Integrating with State Restoration + In order to opt in to state restoration for `MMDrawerController`, you must set the `restorationIdentifier` of your drawer controller. Instances of your centerViewController, leftDrawerViewController and rightDrawerViewController must also be configured with their own `restorationIdentifier` (and optionally a restorationClass) if you intend for those to be restored as well. If your MMDrawerController had an open drawer when your app was sent to the background, that state will also be restored. + + ## What this library doesn't do. + This library is not meant for: + - Top or bottom drawer views + - Displaying both drawers at one time + - Displaying a minimum drawer width + - Support container view controllers other than `UINavigationController` as the center view controller. + */ + +typedef NS_ENUM(NSInteger,MMDrawerSide){ + MMDrawerSideNone = 0, + MMDrawerSideLeft, + MMDrawerSideRight, +}; + +typedef NS_OPTIONS(NSInteger, MMOpenDrawerGestureMode) { + MMOpenDrawerGestureModeNone = 0, + MMOpenDrawerGestureModePanningNavigationBar = 1 << 1, + MMOpenDrawerGestureModePanningCenterView = 1 << 2, + MMOpenDrawerGestureModeBezelPanningCenterView = 1 << 3, + MMOpenDrawerGestureModeCustom = 1 << 4, + MMOpenDrawerGestureModeAll = MMOpenDrawerGestureModePanningNavigationBar | + MMOpenDrawerGestureModePanningCenterView | + MMOpenDrawerGestureModeBezelPanningCenterView | + MMOpenDrawerGestureModeCustom, +}; + +typedef NS_OPTIONS(NSInteger, MMCloseDrawerGestureMode) { + MMCloseDrawerGestureModeNone = 0, + MMCloseDrawerGestureModePanningNavigationBar = 1 << 1, + MMCloseDrawerGestureModePanningCenterView = 1 << 2, + MMCloseDrawerGestureModeBezelPanningCenterView = 1 << 3, + MMCloseDrawerGestureModeTapNavigationBar = 1 << 4, + MMCloseDrawerGestureModeTapCenterView = 1 << 5, + MMCloseDrawerGestureModePanningDrawerView = 1 << 6, + MMCloseDrawerGestureModeCustom = 1 << 7, + MMCloseDrawerGestureModeAll = MMCloseDrawerGestureModePanningNavigationBar | + MMCloseDrawerGestureModePanningCenterView | + MMCloseDrawerGestureModeBezelPanningCenterView | + MMCloseDrawerGestureModeTapNavigationBar | + MMCloseDrawerGestureModeTapCenterView | + MMCloseDrawerGestureModePanningDrawerView | + MMCloseDrawerGestureModeCustom, +}; + +typedef NS_ENUM(NSInteger, MMDrawerOpenCenterInteractionMode) { + MMDrawerOpenCenterInteractionModeNone, + MMDrawerOpenCenterInteractionModeFull, + MMDrawerOpenCenterInteractionModeNavigationBarOnly, +}; + +@class MMDrawerController; +typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible); + +@interface MMDrawerController : UIViewController + +///--------------------------------------- +/// @name Accessing Drawer Container View Controller Properties +///--------------------------------------- + +/** + The center view controller. + + This can only be set via the init methods, as well as the `setNewCenterViewController:...` methods. The size of this view controller will automatically be set to the size of the drawer container view controller, and it's position is modified from within this class. Do not modify the frame externally. + */ +@property (nonatomic, strong) UIViewController * centerViewController; + +/** + The left drawer view controller. + + The size of this view controller is managed within this class, and is automatically set to the appropriate size based on the `maximumLeftDrawerWidth`. Do not modify the frame externally. + */ +@property (nonatomic, strong) UIViewController * leftDrawerViewController; + +/** + The right drawer view controller. + + The size of this view controller is managed within this class, and is automatically set to the appropriate size based on the `maximumRightDrawerWidth`. Do not modify the frame externally. + */ +@property (nonatomic, strong) UIViewController * rightDrawerViewController; + +/** + The maximum width of the `leftDrawerViewController`. + + By default, this is set to 280. If the `leftDrawerViewController` is nil, this property will return 0.0; + */ +@property (nonatomic, assign) CGFloat maximumLeftDrawerWidth; + +/** + The maximum width of the `rightDrawerViewController`. + + By default, this is set to 280. If the `rightDrawerViewController` is nil, this property will return 0.0; + + */ +@property (nonatomic, assign) CGFloat maximumRightDrawerWidth; + +/** + The visible width of the `leftDrawerViewController`. + + Note this value can be greater than `maximumLeftDrawerWidth` during the full close animation when setting a new center view controller; + */ +@property (nonatomic, assign, readonly) CGFloat visibleLeftDrawerWidth; + +/** + The visible width of the `rightDrawerViewController`. + + Note this value can be greater than `maximumRightDrawerWidth` during the full close animation when setting a new center view controller; + */ +@property (nonatomic, assign, readonly) CGFloat visibleRightDrawerWidth; + +/** + The animation velocity of the open and close methods, measured in points per second. + + By default, this is set to 840 points per second (three times the default drawer width), meaning it takes 1/3 of a second for the `centerViewController` to open/close across the default drawer width. Note that there is a minimum .1 second duration for built in animations, to account for small distance animations. + */ +@property (nonatomic, assign) CGFloat animationVelocity; + +/** + A boolean that determines whether or not the panning gesture will "hard-stop" at the maximum width for a given drawer side. + + By default, this value is set to YES. Enabling `shouldStretchDrawer` will give the pan a gradual asymptotic stopping point much like `UIScrollView` behaves. Note that if this value is set to YES, the `drawerVisualStateBlock` can be passed a `percentVisible` greater than 1.0, so be sure to handle that case appropriately. + */ +@property (nonatomic, assign) BOOL shouldStretchDrawer; + +/** + The current open side of the drawer. + + Note this value will change as soon as a pan gesture opens a drawer, or when a open/close animation is finished. + */ +@property (nonatomic, assign, readonly) MMDrawerSide openSide; + +/** + How a user is allowed to open a drawer using gestures. + + By default, this is set to `MMOpenDrawerGestureModeNone`. Note these gestures may affect user interaction with the `centerViewController`, so be sure to use appropriately. + */ +@property (nonatomic, assign) MMOpenDrawerGestureMode openDrawerGestureModeMask; + +/** + How a user is allowed to close a drawer. + + By default, this is set to `MMCloseDrawerGestureModeNone`. Note these gestures may affect user interaction with the `centerViewController`, so be sure to use appropriately. + */ +@property (nonatomic, assign) MMCloseDrawerGestureMode closeDrawerGestureModeMask; + +/** + The value determining if the user can interact with the `centerViewController` when a side drawer is open. + + By default, it is `MMDrawerOpenCenterInteractionModeNavigationBarOnly`, meaning that the user can only interact with the buttons on the `UINavigationBar`, if the center view controller is a `UINavigationController`. Otherwise, the user cannot interact with any other center view controller elements. + */ +@property (nonatomic, assign) MMDrawerOpenCenterInteractionMode centerHiddenInteractionMode; + +/** + The flag determining if a shadow should be drawn off of `centerViewController` when a drawer is open. + + By default, this is set to YES. + */ +@property (nonatomic, assign) BOOL showsShadow; + +/** + The shadow radius of `centerViewController` when a drawer is open. + + By default, this is set to 10.0f; + */ +@property (nonatomic, assign) CGFloat shadowRadius; + +/** + The shadow opacity of `centerViewController` when a drawer is open. + + By default, this is set to 0.8f; + */ +@property (nonatomic, assign) CGFloat shadowOpacity; + +/** + The shadow offset of `centerViewController` when a drawer is open. + + By default, this is set to (0, -3); + */ +@property (nonatomic, assign) CGSize shadowOffset; + +/** + The color of the shadow drawn off of 'centerViewController` when a drawer is open. + + By default, this is set to the systme default (opaque black). + */ +@property (nonatomic, strong) UIColor * shadowColor; + +/** + The flag determining if a custom background view should appear beneath the status bar, forcing the child content to be drawn lower than the status bar. + + By default, this is set to NO. + */ +@property (nonatomic, assign) BOOL showsStatusBarBackgroundView; + +/** + The color of the status bar background view if `showsStatusBarBackgroundView` is set to YES. + + By default, this is set `[UIColor blackColor]`. + */ +@property (nonatomic, strong) UIColor * statusBarViewBackgroundColor; + +/** + The value determining panning range of centerView's bezel if the user can open drawer with 'MMOpenDrawerGestureModeBezelPanningCenterView' or close drawer with 'MMCloseDrawerGestureModeBezelPanningCenterView' . + + By default, this is set 20.0f. + */ +@property (nonatomic, assign) CGFloat bezelPanningCenterViewRange; + +/** + The value determining if the user can open or close drawer with panGesture velocity. + + By default, this is set 200.0f. + */ +@property (nonatomic, assign) CGFloat panVelocityXAnimationThreshold; + +///--------------------------------------- +/// @name Initializing a `MMDrawerController` +///--------------------------------------- + +/** + Creates and initializes an `MMDrawerController` object with the specified center view controller, left drawer view controller, and right drawer view controller. + + @param centerViewController The center view controller. This argument must not be `nil`. + @param leftDrawerViewController The left drawer view controller. + @param rightDrawerViewController The right drawer controller. + + @return The newly-initialized drawer container view controller. + */ +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController; + +/** + Creates and initializes an `MMDrawerController` object with the specified center view controller, left drawer view controller. + + @param centerViewController The center view controller. This argument must not be `nil`. + @param leftDrawerViewController The left drawer view controller. + + @return The newly-initialized drawer container view controller. + */ +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController; + +/** + Creates and initializes an `MMDrawerController` object with the specified center view controller, right drawer view controller. + + @param centerViewController The center view controller. This argument must not be `nil`. + @param rightDrawerViewController The right drawer controller. + + @return The newly-initialized drawer container view controller. + */ +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController; + +///--------------------------------------- +/// @name Opening and Closing a Drawer +///--------------------------------------- + +/** + Toggles the drawer open/closed based on the `drawer` passed in. + + Note that if you attempt to toggle a drawer closed while the other is open, nothing will happen. For example, if you pass in MMDrawerSideLeft, but the right drawer is open, nothing will happen. In addition, the completion block will be called with the finished flag set to NO. + + @param drawerSide The `MMDrawerSide` to toggle. This value cannot be `MMDrawerSideNone`. + @param animated Determines whether the `drawer` should be toggle animated. + @param completion The block that is called when the toggle is complete, or if no toggle took place at all. + + */ +-(void)toggleDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +/** + Closes the open drawer. + + @param animated Determines whether the drawer side should be closed animated + @param completion The block that is called when the close is complete + + */ +-(void)closeDrawerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +/** + Opens the `drawer` passed in. + + @param drawerSide The `MMDrawerSide` to open. This value cannot be `MMDrawerSideNone`. + @param animated Determines whether the `drawer` should be open animated. + @param completion The block that is called when the toggle is open. + + */ +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +///--------------------------------------- +/// @name Setting a new Center View Controller +///--------------------------------------- + +/** + Sets the new `centerViewController`. + + This sets the view controller and will automatically adjust the frame based on the current state of the drawer controller. If `closeAnimated` is YES, it will immediately change the center view controller, and close the drawer from its current position. + + @param centerViewController The new `centerViewController`. + @param closeAnimated Determines whether the drawer should be closed with an animation. + @param completion The block called when the animation is finsihed. + + */ +-(void)setCenterViewController:(UIViewController *)centerViewController withCloseAnimation:(BOOL)closeAnimated completion:(void(^)(BOOL finished))completion; + +/** + Sets the new `centerViewController`. + + This sets the view controller and will automatically adjust the frame based on the current state of the drawer controller. If `closeFullAnimated` is YES, the current center view controller will animate off the screen, the new center view controller will then be set, followed by the drawer closing across the full width of the screen. + + @param newCenterViewController The new `centerViewController`. + @param fullCloseAnimated Determines whether the drawer should be closed with an animation. + @param completion The block called when the animation is finsihed. + + */ +-(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)fullCloseAnimated completion:(void(^)(BOOL finished))completion; + +///--------------------------------------- +/// @name Animating the Width of a Drawer +///--------------------------------------- + +/** + Sets the maximum width of the left drawer view controller. + + If the drawer is open, and `animated` is YES, it will animate the drawer frame as well as adjust the center view controller. If the drawer is not open, this change will take place immediately. + + @param width The new width of left drawer view controller. This must be greater than zero. + @param animated Determines whether the drawer should be adjusted with an animation. + @param completion The block called when the animation is finished. + + */ +-(void)setMaximumLeftDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +/** + Sets the maximum width of the right drawer view controller. + + If the drawer is open, and `animated` is YES, it will animate the drawer frame as well as adjust the center view controller. If the drawer is not open, this change will take place immediately. + + @param width The new width of right drawer view controller. This must be greater than zero. + @param animated Determines whether the drawer should be adjusted with an animation. + @param completion The block called when the animation is finished. + + */ +-(void)setMaximumRightDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +///--------------------------------------- +/// @name Previewing a Drawer +///--------------------------------------- + +/** + Bounce preview for the specified `drawerSide` a distance of 40 points. + + @param drawerSide The drawer to preview. This value cannot be `MMDrawerSideNone`. + @param completion The block called when the animation is finsihed. + + */ +-(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide completion:(void(^)(BOOL finished))completion; + +/** + Bounce preview for the specified `drawerSide`. + + @param drawerSide The drawer side to preview. This value cannot be `MMDrawerSideNone`. + @param distance The distance to bounce. + @param completion The block called when the animation is finsihed. + + */ +-(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide distance:(CGFloat)distance completion:(void(^)(BOOL finished))completion; + +///--------------------------------------- +/// @name Custom Drawer Animations +///--------------------------------------- + +/** + Sets a callback to be called when a drawer visual state needs to be updated. + + This block is responsible for updating the drawer's view state, and the drawer controller will handle animating to that state from the current state. This block will be called when the drawer is opened or closed, as well when the user is panning the drawer. This block is not responsible for doing animations directly, but instead just updating the state of the properies (such as alpha, anchor point, transform, etc). Note that if `shouldStretchDrawer` is set to YES, it is possible for `percentVisible` to be greater than 1.0. If `shouldStretchDrawer` is set to NO, `percentVisible` will never be greater than 1.0. + + Note that when the drawer is finished opening or closing, the side drawer controller view will be reset with the following properies: + + - alpha: 1.0 + - transform: CATransform3DIdentity + - anchorPoint: (0.5,0.5) + + @param drawerVisualStateBlock A block object to be called that allows the implementer to update visual state properties on the drawer. `percentVisible` represents the amount of the drawer space that is current visible, with drawer space being defined as the edge of the screen to the maxmimum drawer width. Note that you do have access to the drawerController, which will allow you to update things like the anchor point of the side drawer layer. + */ +-(void)setDrawerVisualStateBlock:(void(^)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible))drawerVisualStateBlock; + +///--------------------------------------- +/// @name Gesture Completion Handling +///--------------------------------------- + +/** + Sets a callback to be called when a gesture has been completed. + + This block is called when a gesture action has been completed. You can query the `openSide` of the `drawerController` to determine what the new state of the drawer is. + + @param gestureCompletionBlock A block object to be called that allows the implementer be notified when a gesture action has been completed. + */ +-(void)setGestureCompletionBlock:(void(^)(MMDrawerController * drawerController, UIGestureRecognizer * gesture))gestureCompletionBlock; + +///--------------------------------------- +/// @name Custom Gesture Handler +///--------------------------------------- + +/** + Sets a callback to be called to determine if a UIGestureRecognizer should recieve the given UITouch. + + This block provides a way to allow a gesture to be recognized with custom logic. For example, you may have a certain part of your view that should accept a pan gesture recognizer to open the drawer, but not another a part. If you return YES, the gesture is recognized and the appropriate action is taken. This provides similar support to how Facebook allows you to pan on the background view of the main table view, but not the content itself. You can inspect the `openSide` property of the `drawerController` to determine the current state of the drawer, and apply the appropriate logic within your block. + + Note that either `openDrawerGestureModeMask` must contain `MMOpenDrawerGestureModeCustom`, or `closeDrawerGestureModeMask` must contain `MMCloseDrawerGestureModeCustom` for this block to be consulted. + + @param gestureShouldRecognizeTouchBlock A block object to be called to determine if the given `touch` should be recognized by the given gesture. + */ +-(void)setGestureShouldRecognizeTouchBlock:(BOOL(^)(MMDrawerController * drawerController, UIGestureRecognizer * gesture, UITouch * touch))gestureShouldRecognizeTouchBlock; + +@end diff --git a/ListApp/MMDrawerController.m b/ListApp/MMDrawerController.m new file mode 100755 index 0000000..4aec238 --- /dev/null +++ b/ListApp/MMDrawerController.m @@ -0,0 +1,1479 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "MMDrawerController.h" +#import "UIViewController+MMDrawerController.h" + +#import + +CGFloat const MMDrawerDefaultWidth = 280.0f; +CGFloat const MMDrawerDefaultAnimationVelocity = 840.0f; + +NSTimeInterval const MMDrawerDefaultFullAnimationDelay = 0.10f; + +CGFloat const MMDrawerDefaultBounceDistance = 50.0f; + +NSTimeInterval const MMDrawerDefaultBounceAnimationDuration = 0.2f; +CGFloat const MMDrawerDefaultSecondBounceDistancePercentage = .25f; + +CGFloat const MMDrawerDefaultShadowRadius = 10.0f; +CGFloat const MMDrawerDefaultShadowOpacity = 0.8; + +NSTimeInterval const MMDrawerMinimumAnimationDuration = 0.15f; + +CGFloat const MMDrawerBezelRange = 20.0f; + +CGFloat const MMDrawerPanVelocityXAnimationThreshold = 200.0f; + +/** The amount of overshoot that is panned linearly. The remaining percentage nonlinearly asymptotes to the max percentage. */ +CGFloat const MMDrawerOvershootLinearRangePercentage = 0.75f; + +/** The percent of the possible overshoot width to use as the actual overshoot percentage. */ +CGFloat const MMDrawerOvershootPercentage = 0.1f; + +typedef BOOL (^MMDrawerGestureShouldRecognizeTouchBlock)(MMDrawerController * drawerController, UIGestureRecognizer * gesture, UITouch * touch); +typedef void (^MMDrawerGestureCompletionBlock)(MMDrawerController * drawerController, UIGestureRecognizer * gesture); + +static CAKeyframeAnimation * bounceKeyFrameAnimationForDistanceOnView(CGFloat distance, UIView * view) { + CGFloat factors[32] = {0, 32, 60, 83, 100, 114, 124, 128, 128, 124, 114, 100, 83, 60, 32, + 0, 24, 42, 54, 62, 64, 62, 54, 42, 24, 0, 18, 28, 32, 28, 18, 0}; + + NSMutableArray *values = [NSMutableArray array]; + + for (int i=0; i<32; i++) + { + CGFloat positionOffset = factors[i]/128.0f * distance + CGRectGetMidX(view.bounds); + [values addObject:@(positionOffset)]; + } + + CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"]; + animation.repeatCount = 1; + animation.duration = .8; + animation.fillMode = kCAFillModeForwards; + animation.values = values; + animation.removedOnCompletion = YES; + animation.autoreverses = NO; + + return animation; +} + +static NSString *MMDrawerLeftDrawerKey = @"MMDrawerLeftDrawer"; +static NSString *MMDrawerRightDrawerKey = @"MMDrawerRightDrawer"; +static NSString *MMDrawerCenterKey = @"MMDrawerCenter"; +static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide"; + +@interface MMDrawerCenterContainerView : UIView +@property (nonatomic,assign) MMDrawerOpenCenterInteractionMode centerInteractionMode; +@property (nonatomic,assign) MMDrawerSide openSide; +@end + +@implementation MMDrawerCenterContainerView + +-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ + UIView *hitView = [super hitTest:point withEvent:event]; + if(hitView && + self.openSide != MMDrawerSideNone){ + UINavigationBar * navBar = [self navigationBarContainedWithinSubviewsOfView:self]; + CGRect navBarFrame = [navBar convertRect:navBar.bounds toView:self]; + if((self.centerInteractionMode == MMDrawerOpenCenterInteractionModeNavigationBarOnly && + CGRectContainsPoint(navBarFrame, point) == NO) || + self.centerInteractionMode == MMDrawerOpenCenterInteractionModeNone){ + hitView = nil; + } + } + return hitView; +} + +-(UINavigationBar*)navigationBarContainedWithinSubviewsOfView:(UIView*)view{ + UINavigationBar * navBar = nil; + for(UIView * subview in [view subviews]){ + if([view isKindOfClass:[UINavigationBar class]]){ + navBar = (UINavigationBar*)view; + break; + } + else { + navBar = [self navigationBarContainedWithinSubviewsOfView:subview]; + if (navBar != nil) { + break; + } + } + } + return navBar; +} +@end + +@interface MMDrawerController () { + CGFloat _maximumRightDrawerWidth; + CGFloat _maximumLeftDrawerWidth; + UIColor * _statusBarViewBackgroundColor; +} + +@property (nonatomic, assign, readwrite) MMDrawerSide openSide; + +@property (nonatomic, strong) UIView * childControllerContainerView; +@property (nonatomic, strong) MMDrawerCenterContainerView * centerContainerView; +@property (nonatomic, strong) UIView * dummyStatusBarView; + +@property (nonatomic, assign) CGRect startingPanRect; +@property (nonatomic, copy) MMDrawerControllerDrawerVisualStateBlock drawerVisualState; +@property (nonatomic, copy) MMDrawerGestureShouldRecognizeTouchBlock gestureShouldRecognizeTouch; +@property (nonatomic, copy) MMDrawerGestureCompletionBlock gestureCompletion; +@property (nonatomic, assign, getter = isAnimatingDrawer) BOOL animatingDrawer; + +@end + +@implementation MMDrawerController + +#pragma mark - Init + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + [self commonSetup]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonSetup]; + } + return self; +} + +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController{ + NSParameterAssert(centerViewController); + self = [super init]; + if(self){ + [self setCenterViewController:centerViewController]; + [self setLeftDrawerViewController:leftDrawerViewController]; + [self setRightDrawerViewController:rightDrawerViewController]; + } + return self; +} + +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController{ + return [self initWithCenterViewController:centerViewController leftDrawerViewController:leftDrawerViewController rightDrawerViewController:nil]; +} + +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController{ + return [self initWithCenterViewController:centerViewController leftDrawerViewController:nil rightDrawerViewController:rightDrawerViewController]; +} + +-(void)commonSetup{ + [self setMaximumLeftDrawerWidth:MMDrawerDefaultWidth]; + [self setMaximumRightDrawerWidth:MMDrawerDefaultWidth]; + + [self setAnimationVelocity:MMDrawerDefaultAnimationVelocity]; + + [self setShowsShadow:YES]; + [self setShouldStretchDrawer:YES]; + + [self setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeNone]; + [self setCloseDrawerGestureModeMask:MMCloseDrawerGestureModeNone]; + [self setCenterHiddenInteractionMode:MMDrawerOpenCenterInteractionModeNavigationBarOnly]; + + // set shadow related default values + [self setShadowOpacity:MMDrawerDefaultShadowOpacity]; + [self setShadowRadius:MMDrawerDefaultShadowRadius]; + [self setShadowOffset:CGSizeMake(0, -3)]; + [self setShadowColor:[UIColor blackColor]]; + + // set default bezel range for panGestureReconizer + [self setBezelPanningCenterViewRange:MMDrawerBezelRange]; + + // set defualt panVelocityXAnimationThreshold + [self setPanVelocityXAnimationThreshold:MMDrawerPanVelocityXAnimationThreshold]; +} + +#pragma mark - State Restoration +- (void)encodeRestorableStateWithCoder:(NSCoder *)coder{ + [super encodeRestorableStateWithCoder:coder]; + if (self.leftDrawerViewController){ + [coder encodeObject:self.leftDrawerViewController forKey:MMDrawerLeftDrawerKey]; + } + + if (self.rightDrawerViewController){ + [coder encodeObject:self.rightDrawerViewController forKey:MMDrawerRightDrawerKey]; + } + + if (self.centerViewController){ + [coder encodeObject:self.centerViewController forKey:MMDrawerCenterKey]; + } + + [coder encodeInteger:self.openSide forKey:MMDrawerOpenSideKey]; +} + +- (void)decodeRestorableStateWithCoder:(NSCoder *)coder{ + UIViewController *controller; + MMDrawerSide openside; + + [super decodeRestorableStateWithCoder:coder]; + + if ((controller = [coder decodeObjectForKey:MMDrawerLeftDrawerKey])){ + self.leftDrawerViewController = controller; + } + + if ((controller = [coder decodeObjectForKey:MMDrawerRightDrawerKey])){ + self.rightDrawerViewController = controller; + } + + if ((controller = [coder decodeObjectForKey:MMDrawerCenterKey])){ + self.centerViewController = controller; + } + + if ((openside = [coder decodeIntegerForKey:MMDrawerOpenSideKey])){ + [self openDrawerSide:openside animated:false completion:nil]; + } +} +#pragma mark - Open/Close methods +-(void)toggleDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ + NSParameterAssert(drawerSide!=MMDrawerSideNone); + if(self.openSide == MMDrawerSideNone){ + [self openDrawerSide:drawerSide animated:animated completion:completion]; + } + else { + if((drawerSide == MMDrawerSideLeft && + self.openSide == MMDrawerSideLeft) || + (drawerSide == MMDrawerSideRight && + self.openSide == MMDrawerSideRight)){ + [self closeDrawerAnimated:animated completion:completion]; + } + else if(completion){ + completion(NO); + } + } +} + +-(void)closeDrawerAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ + [self closeDrawerAnimated:animated velocity:self.animationVelocity animationOptions:UIViewAnimationOptionCurveEaseInOut completion:completion]; +} + +-(void)closeDrawerAnimated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion{ + if(self.isAnimatingDrawer){ + if(completion){ + completion(NO); + } + } + else { + [self setAnimatingDrawer:animated]; + CGRect newFrame = self.childControllerContainerView.bounds; + + CGFloat distance = ABS(CGRectGetMinX(self.centerContainerView.frame)); + NSTimeInterval duration = MAX(distance/ABS(velocity),MMDrawerMinimumAnimationDuration); + + BOOL leftDrawerVisible = CGRectGetMinX(self.centerContainerView.frame) > 0; + BOOL rightDrawerVisible = CGRectGetMinX(self.centerContainerView.frame) < 0; + + MMDrawerSide visibleSide = MMDrawerSideNone; + CGFloat percentVisble = 0.0; + + if(leftDrawerVisible){ + CGFloat visibleDrawerPoints = CGRectGetMinX(self.centerContainerView.frame); + percentVisble = MAX(0.0,visibleDrawerPoints/self.maximumLeftDrawerWidth); + visibleSide = MMDrawerSideLeft; + } + else if(rightDrawerVisible){ + CGFloat visibleDrawerPoints = CGRectGetWidth(self.centerContainerView.frame)-CGRectGetMaxX(self.centerContainerView.frame); + percentVisble = MAX(0.0,visibleDrawerPoints/self.maximumRightDrawerWidth); + visibleSide = MMDrawerSideRight; + } + + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:visibleSide]; + + [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:percentVisble]; + + [sideDrawerViewController beginAppearanceTransition:NO animated:animated]; + + [UIView + animateWithDuration:(animated?duration:0.0) + delay:0.0 + options:options + animations:^{ + [self setNeedsStatusBarAppearanceUpdateIfSupported]; + [self.centerContainerView setFrame:newFrame]; + [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:0.0]; + } + completion:^(BOOL finished) { + [sideDrawerViewController endAppearanceTransition]; + [self setOpenSide:MMDrawerSideNone]; + [self resetDrawerVisualStateForDrawerSide:visibleSide]; + [self setAnimatingDrawer:NO]; + if(completion){ + completion(finished); + } + }]; + } +} + +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ + NSParameterAssert(drawerSide != MMDrawerSideNone); + + [self openDrawerSide:drawerSide animated:animated velocity:self.animationVelocity animationOptions:UIViewAnimationOptionCurveEaseInOut completion:completion]; +} + +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion{ + NSParameterAssert(drawerSide != MMDrawerSideNone); + if (self.isAnimatingDrawer) { + if(completion){ + completion(NO); + } + } + else { + [self setAnimatingDrawer:animated]; + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + if (self.openSide != drawerSide) { + [self prepareToPresentDrawer:drawerSide animated:animated]; + } + + if(sideDrawerViewController){ + CGRect newFrame; + CGRect oldFrame = self.centerContainerView.frame; + if(drawerSide == MMDrawerSideLeft){ + newFrame = self.centerContainerView.frame; + newFrame.origin.x = self.maximumLeftDrawerWidth; + } + else { + newFrame = self.centerContainerView.frame; + newFrame.origin.x = 0-self.maximumRightDrawerWidth; + } + + CGFloat distance = ABS(CGRectGetMinX(oldFrame)-newFrame.origin.x); + NSTimeInterval duration = MAX(distance/ABS(velocity),MMDrawerMinimumAnimationDuration); + + [UIView + animateWithDuration:(animated?duration:0.0) + delay:0.0 + options:options + animations:^{ + [self setNeedsStatusBarAppearanceUpdateIfSupported]; + [self.centerContainerView setFrame:newFrame]; + [self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0]; + } + completion:^(BOOL finished) { + //End the appearance transition if it already wasn't open. + if(drawerSide != self.openSide){ + [sideDrawerViewController endAppearanceTransition]; + } + [self setOpenSide:drawerSide]; + + [self resetDrawerVisualStateForDrawerSide:drawerSide]; + [self setAnimatingDrawer:NO]; + if(completion){ + completion(finished); + } + }]; + } + } +} + +#pragma mark - Updating the Center View Controller +//If animated is NO, then we need to handle all the appearance calls within this method. Otherwise, +//let the method calling this one handle proper appearance methods since they will have more context +-(void)setCenterViewController:(UIViewController *)centerViewController animated:(BOOL)animated{ + if ([self.centerViewController isEqual:centerViewController]) { + return; + } + + if (_centerContainerView == nil) { + //This is related to Issue #152 (https://github.com/mutualmobile/MMDrawerController/issues/152) + // also fixed below in the getter for `childControllerContainerView`. Turns out we have + // two center container views getting added to the view during init, + // because the first request self.centerContainerView.bounds was kicking off a + // viewDidLoad, which caused us to be able to fall through this check twice. + // + //The fix is to grab the bounds, and then check again that the child container view has + //not been created. + + CGRect centerFrame = self.childControllerContainerView.bounds; + if(_centerContainerView == nil){ + _centerContainerView = [[MMDrawerCenterContainerView alloc] initWithFrame:centerFrame]; + [self.centerContainerView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; + [self.centerContainerView setBackgroundColor:[UIColor clearColor]]; + [self.centerContainerView setOpenSide:self.openSide]; + [self.centerContainerView setCenterInteractionMode:self.centerHiddenInteractionMode]; + [self.childControllerContainerView addSubview:self.centerContainerView]; + } + } + + UIViewController * oldCenterViewController = self.centerViewController; + if(oldCenterViewController){ + [oldCenterViewController willMoveToParentViewController:nil]; + if(animated == NO){ + [oldCenterViewController beginAppearanceTransition:NO animated:NO]; + } + [oldCenterViewController.view removeFromSuperview]; + if(animated == NO){ + [oldCenterViewController endAppearanceTransition]; + } + [oldCenterViewController removeFromParentViewController]; + } + + _centerViewController = centerViewController; + + [self addChildViewController:self.centerViewController]; + [self.centerViewController.view setFrame:self.childControllerContainerView.bounds]; + [self.centerContainerView addSubview:self.centerViewController.view]; + [self.childControllerContainerView bringSubviewToFront:self.centerContainerView]; + [self.centerViewController.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; + [self updateShadowForCenterView]; + + if(animated == NO){ + // If drawer is offscreen, then viewWillAppear: will take care of this + if(self.view.window) { + [self.centerViewController beginAppearanceTransition:YES animated:NO]; + [self.centerViewController endAppearanceTransition]; + } + [self.centerViewController didMoveToParentViewController:self]; + } +} + +-(void)setCenterViewController:(UIViewController *)newCenterViewController withCloseAnimation:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + + if(self.openSide == MMDrawerSideNone){ + //If a side drawer isn't open, there is nothing to animate... + animated = NO; + } + + BOOL forwardAppearanceMethodsToCenterViewController = ([self.centerViewController isEqual:newCenterViewController] == NO); + + UIViewController * oldCenterViewController = self.centerViewController; + // This is related to issue 363 (https://github.com/novkostya/MMDrawerController/pull/363) + // This needs to be refactored so the appearance logic is easier + // to follow across the multiple close/setter methods + if (animated && forwardAppearanceMethodsToCenterViewController) { + [oldCenterViewController beginAppearanceTransition:NO animated:NO]; + } + + [self setCenterViewController:newCenterViewController animated:animated]; + + // Related to note above. + if (animated && forwardAppearanceMethodsToCenterViewController) { + [oldCenterViewController endAppearanceTransition]; + } + + if(animated){ + [self updateDrawerVisualStateForDrawerSide:self.openSide percentVisible:1.0]; + if (forwardAppearanceMethodsToCenterViewController) { + [self.centerViewController beginAppearanceTransition:YES animated:animated]; + } + [self + closeDrawerAnimated:animated + completion:^(BOOL finished) { + if (forwardAppearanceMethodsToCenterViewController) { + [self.centerViewController endAppearanceTransition]; + [self.centerViewController didMoveToParentViewController:self]; + } + if(completion){ + completion(finished); + } + }]; + } + else { + if(completion) { + completion(YES); + } + } +} + +-(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + if(self.openSide != MMDrawerSideNone && + animated){ + + BOOL forwardAppearanceMethodsToCenterViewController = ([self.centerViewController isEqual:newCenterViewController] == NO); + + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:self.openSide]; + + CGFloat targetClosePoint = 0.0f; + if(self.openSide == MMDrawerSideRight){ + targetClosePoint = -CGRectGetWidth(self.childControllerContainerView.bounds); + } + else if(self.openSide == MMDrawerSideLeft) { + targetClosePoint = CGRectGetWidth(self.childControllerContainerView.bounds); + } + + CGFloat distance = ABS(self.centerContainerView.frame.origin.x-targetClosePoint); + NSTimeInterval firstDuration = [self animationDurationForAnimationDistance:distance]; + + CGRect newCenterRect = self.centerContainerView.frame; + + [self setAnimatingDrawer:animated]; + + UIViewController * oldCenterViewController = self.centerViewController; + if(forwardAppearanceMethodsToCenterViewController ){ + [oldCenterViewController beginAppearanceTransition:NO animated:animated]; + } + newCenterRect.origin.x = targetClosePoint; + [UIView + animateWithDuration:firstDuration + delay:0.0 + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.centerContainerView setFrame:newCenterRect]; + [sideDrawerViewController.view setFrame:self.childControllerContainerView.bounds]; + } + completion:^(BOOL finished) { + + CGRect oldCenterRect = self.centerContainerView.frame; + [self setCenterViewController:newCenterViewController animated:animated]; + [self.centerContainerView setFrame:oldCenterRect]; + [self updateDrawerVisualStateForDrawerSide:self.openSide percentVisible:1.0]; + if(forwardAppearanceMethodsToCenterViewController) { + [oldCenterViewController endAppearanceTransition]; + [self.centerViewController beginAppearanceTransition:YES animated:animated]; + } + [sideDrawerViewController beginAppearanceTransition:NO animated:animated]; + [UIView + animateWithDuration:[self animationDurationForAnimationDistance:CGRectGetWidth(self.childControllerContainerView.bounds)] + delay:MMDrawerDefaultFullAnimationDelay + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.centerContainerView setFrame:self.childControllerContainerView.bounds]; + [self updateDrawerVisualStateForDrawerSide:self.openSide percentVisible:0.0]; + } + completion:^(BOOL finished) { + if (forwardAppearanceMethodsToCenterViewController) { + [self.centerViewController endAppearanceTransition]; + [self.centerViewController didMoveToParentViewController:self]; + } + [sideDrawerViewController endAppearanceTransition]; + [self resetDrawerVisualStateForDrawerSide:self.openSide]; + + [sideDrawerViewController.view setFrame:sideDrawerViewController.mm_visibleDrawerFrame]; + + [self setOpenSide:MMDrawerSideNone]; + [self setAnimatingDrawer:NO]; + if(completion){ + completion(finished); + } + }]; + }]; + } + else { + [self setCenterViewController:newCenterViewController animated:animated]; + if(self.openSide != MMDrawerSideNone){ + [self closeDrawerAnimated:animated completion:completion]; + } + else if(completion){ + completion(YES); + } + } +} + +#pragma mark - Size Methods +-(void)setMaximumLeftDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + [self setMaximumDrawerWidth:width forSide:MMDrawerSideLeft animated:animated completion:completion]; +} + +-(void)setMaximumRightDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + [self setMaximumDrawerWidth:width forSide:MMDrawerSideRight animated:animated completion:completion]; +} + +- (void)setMaximumDrawerWidth:(CGFloat)width forSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + NSParameterAssert(width > 0); + NSParameterAssert(drawerSide != MMDrawerSideNone); + + UIViewController *sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + CGFloat oldWidth = 0.f; + NSInteger drawerSideOriginCorrection = 1; + if (drawerSide == MMDrawerSideLeft) { + oldWidth = _maximumLeftDrawerWidth; + _maximumLeftDrawerWidth = width; + } + else if(drawerSide == MMDrawerSideRight){ + oldWidth = _maximumRightDrawerWidth; + _maximumRightDrawerWidth = width; + drawerSideOriginCorrection = -1; + } + + CGFloat distance = ABS(width-oldWidth); + NSTimeInterval duration = [self animationDurationForAnimationDistance:distance]; + + if(self.openSide == drawerSide){ + CGRect newCenterRect = self.centerContainerView.frame; + newCenterRect.origin.x = drawerSideOriginCorrection*width; + [UIView + animateWithDuration:(animated?duration:0) + delay:0.0 + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.centerContainerView setFrame:newCenterRect]; + [sideDrawerViewController.view setFrame:sideDrawerViewController.mm_visibleDrawerFrame]; + } + completion:^(BOOL finished) { + if(completion != nil){ + completion(finished); + } + }]; + } + else{ + [sideDrawerViewController.view setFrame:sideDrawerViewController.mm_visibleDrawerFrame]; + if(completion != nil){ + completion(YES); + } + } +} + +#pragma mark - Bounce Methods +-(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide completion:(void(^)(BOOL finished))completion{ + NSParameterAssert(drawerSide!=MMDrawerSideNone); + [self bouncePreviewForDrawerSide:drawerSide distance:MMDrawerDefaultBounceDistance completion:completion]; +} + +-(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide distance:(CGFloat)distance completion:(void(^)(BOOL finished))completion{ + NSParameterAssert(drawerSide!=MMDrawerSideNone); + + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + + if(sideDrawerViewController == nil || + self.openSide != MMDrawerSideNone){ + if(completion){ + completion(NO); + } + return; + } + else { + [self prepareToPresentDrawer:drawerSide animated:YES]; + + [self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0]; + + [CATransaction begin]; + [CATransaction + setCompletionBlock:^{ + [sideDrawerViewController endAppearanceTransition]; + [sideDrawerViewController beginAppearanceTransition:NO animated:NO]; + [sideDrawerViewController endAppearanceTransition]; + if(completion){ + completion(YES); + } + }]; + + CGFloat modifier = ((drawerSide == MMDrawerSideLeft)?1.0:-1.0); + CAKeyframeAnimation *animation = bounceKeyFrameAnimationForDistanceOnView(distance*modifier,self.centerContainerView); + [self.centerContainerView.layer addAnimation:animation forKey:@"bouncing"]; + + [CATransaction commit]; + } +} + +#pragma mark - Setting Drawer Visual State +-(void)setDrawerVisualStateBlock:(void (^)(MMDrawerController *, MMDrawerSide, CGFloat))drawerVisualStateBlock{ + [self setDrawerVisualState:drawerVisualStateBlock]; +} + +#pragma mark - Setting Custom Gesture Handler Block +-(void)setGestureShouldRecognizeTouchBlock:(BOOL (^)(MMDrawerController *, UIGestureRecognizer *, UITouch *))gestureShouldRecognizeTouchBlock{ + [self setGestureShouldRecognizeTouch:gestureShouldRecognizeTouchBlock]; +} + +#pragma mark - Setting the Gesture Completion Block +-(void)setGestureCompletionBlock:(void (^)(MMDrawerController *, UIGestureRecognizer *))gestureCompletionBlock{ + [self setGestureCompletion:gestureCompletionBlock]; +} + +#pragma mark - Subclass Methods +-(BOOL)shouldAutomaticallyForwardAppearanceMethods{ + return NO; +} + +-(BOOL)shouldAutomaticallyForwardRotationMethods{ + return NO; +} + +-(BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{ + return NO; +} + +#pragma mark - View Lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self.view setBackgroundColor:[UIColor blackColor]]; + + [self setupGestureRecognizers]; +} + +-(void)viewWillAppear:(BOOL)animated{ + [super viewWillAppear:animated]; + [self.centerViewController beginAppearanceTransition:YES animated:animated]; + + if(self.openSide == MMDrawerSideLeft) { + [self.leftDrawerViewController beginAppearanceTransition:YES animated:animated]; + } + else if(self.openSide == MMDrawerSideRight) { + [self.rightDrawerViewController beginAppearanceTransition:YES animated:animated]; + } +} + +-(void)viewDidAppear:(BOOL)animated{ + [super viewDidAppear:animated]; + [self updateShadowForCenterView]; + [self.centerViewController endAppearanceTransition]; + + if(self.openSide == MMDrawerSideLeft) { + [self.leftDrawerViewController endAppearanceTransition]; + } + else if(self.openSide == MMDrawerSideRight) { + [self.rightDrawerViewController endAppearanceTransition]; + } +} + +-(void)viewWillDisappear:(BOOL)animated{ + [super viewWillDisappear:animated]; + [self.centerViewController beginAppearanceTransition:NO animated:animated]; + if(self.openSide == MMDrawerSideLeft) { + [self.leftDrawerViewController beginAppearanceTransition:NO animated:animated]; + } + else if (self.openSide == MMDrawerSideRight) { + [self.rightDrawerViewController beginAppearanceTransition:NO animated:animated]; + } +} + +-(void)viewDidDisappear:(BOOL)animated{ + [super viewDidDisappear:animated]; + [self.centerViewController endAppearanceTransition]; + if(self.openSide == MMDrawerSideLeft) { + [self.leftDrawerViewController endAppearanceTransition]; + } + else if (self.openSide == MMDrawerSideRight) { + [self.rightDrawerViewController endAppearanceTransition]; + } +} + +#pragma mark Rotation + +-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ + [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; + //If a rotation begins, we are going to cancel the current gesture and reset transform and anchor points so everything works correctly + BOOL gestureInProgress = NO; + for(UIGestureRecognizer * gesture in self.view.gestureRecognizers){ + if(gesture.state == UIGestureRecognizerStateChanged){ + [gesture setEnabled:NO]; + [gesture setEnabled:YES]; + gestureInProgress = YES; + } + if (gestureInProgress) { + [self resetDrawerVisualStateForDrawerSide:self.openSide]; + } + } + if ([self needsManualForwardingOfRotationEvents]){ + for(UIViewController * childViewController in self.childViewControllers){ + [childViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; + } + } + +} +-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ + [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; + //We need to support the shadow path rotation animation + //Inspired from here: http://blog.radi.ws/post/8348898129/calayers-shadowpath-and-uiview-autoresizing + if(self.showsShadow){ + CGPathRef oldShadowPath = self.centerContainerView.layer.shadowPath; + if(oldShadowPath){ + CFRetain(oldShadowPath); + } + + [self updateShadowForCenterView]; + + if (oldShadowPath) { + [self.centerContainerView.layer addAnimation:((^ { + CABasicAnimation *transition = [CABasicAnimation animationWithKeyPath:@"shadowPath"]; + transition.fromValue = (__bridge id)oldShadowPath; + transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + transition.duration = duration; + return transition; + })()) forKey:@"transition"]; + CFRelease(oldShadowPath); + } + } + + if ([self needsManualForwardingOfRotationEvents]){ + for(UIViewController * childViewController in self.childViewControllers){ + [childViewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; + } + } +} + +-(BOOL)shouldAutorotate{ + return YES; +} + +-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{ + [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; + if ([self needsManualForwardingOfRotationEvents]){ + for(UIViewController * childViewController in self.childViewControllers){ + [childViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation]; + } + } +} + +#pragma mark - Setters +-(void)setRightDrawerViewController:(UIViewController *)rightDrawerViewController{ + [self setDrawerViewController:rightDrawerViewController forSide:MMDrawerSideRight]; +} + +-(void)setLeftDrawerViewController:(UIViewController *)leftDrawerViewController{ + [self setDrawerViewController:leftDrawerViewController forSide:MMDrawerSideLeft]; +} + +- (void)setDrawerViewController:(UIViewController *)viewController forSide:(MMDrawerSide)drawerSide{ + NSParameterAssert(drawerSide != MMDrawerSideNone); + + UIViewController *currentSideViewController = [self sideDrawerViewControllerForSide:drawerSide]; + + if (currentSideViewController == viewController) { + return; + } + + if (currentSideViewController != nil) { + [currentSideViewController beginAppearanceTransition:NO animated:NO]; + [currentSideViewController.view removeFromSuperview]; + [currentSideViewController endAppearanceTransition]; + [currentSideViewController willMoveToParentViewController:nil]; + [currentSideViewController removeFromParentViewController]; + } + + UIViewAutoresizing autoResizingMask = 0; + if (drawerSide == MMDrawerSideLeft) { + _leftDrawerViewController = viewController; + autoResizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleHeight; + + } + else if(drawerSide == MMDrawerSideRight){ + _rightDrawerViewController = viewController; + autoResizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight; + } + + if(viewController){ + [self addChildViewController:viewController]; + + if((self.openSide == drawerSide) && + [self.childControllerContainerView.subviews containsObject:self.centerContainerView]){ + [self.childControllerContainerView insertSubview:viewController.view belowSubview:self.centerContainerView]; + [viewController beginAppearanceTransition:YES animated:NO]; + [viewController endAppearanceTransition]; + } + else{ + [self.childControllerContainerView addSubview:viewController.view]; + [self.childControllerContainerView sendSubviewToBack:viewController.view]; + [viewController.view setHidden:YES]; + } + [viewController didMoveToParentViewController:self]; + [viewController.view setAutoresizingMask:autoResizingMask]; + [viewController.view setFrame:viewController.mm_visibleDrawerFrame]; + } +} + +-(void)setCenterViewController:(UIViewController *)centerViewController{ + [self setCenterViewController:centerViewController animated:NO]; +} + +-(void)setShowsShadow:(BOOL)showsShadow{ + _showsShadow = showsShadow; + [self updateShadowForCenterView]; +} + +- (void)setShadowRadius:(CGFloat)shadowRadius{ + _shadowRadius = shadowRadius; + [self updateShadowForCenterView]; +} + +- (void)setShadowOpacity:(CGFloat)shadowOpacity{ + _shadowOpacity = shadowOpacity; + [self updateShadowForCenterView]; +} + +- (void)setShadowOffset:(CGSize)shadowOffset{ + _shadowOffset = shadowOffset; + [self updateShadowForCenterView]; +} + +- (void)setShadowColor:(UIColor *)shadowColor{ + _shadowColor = shadowColor; + [self updateShadowForCenterView]; +} + +-(void)setOpenSide:(MMDrawerSide)openSide{ + if(_openSide != openSide){ + _openSide = openSide; + [self.centerContainerView setOpenSide:openSide]; + if(openSide == MMDrawerSideNone){ + [self.leftDrawerViewController.view setHidden:YES]; + [self.rightDrawerViewController.view setHidden:YES]; + } + [self setNeedsStatusBarAppearanceUpdateIfSupported]; + } +} + +-(void)setCenterHiddenInteractionMode:(MMDrawerOpenCenterInteractionMode)centerHiddenInteractionMode{ + if(_centerHiddenInteractionMode!=centerHiddenInteractionMode){ + _centerHiddenInteractionMode = centerHiddenInteractionMode; + [self.centerContainerView setCenterInteractionMode:centerHiddenInteractionMode]; + } +} + +-(void)setMaximumLeftDrawerWidth:(CGFloat)maximumLeftDrawerWidth{ + [self setMaximumLeftDrawerWidth:maximumLeftDrawerWidth animated:NO completion:nil]; +} + +-(void)setMaximumRightDrawerWidth:(CGFloat)maximumRightDrawerWidth{ + [self setMaximumRightDrawerWidth:maximumRightDrawerWidth animated:NO completion:nil]; +} + +-(void)setShowsStatusBarBackgroundView:(BOOL)showsDummyStatusBar{ + if(showsDummyStatusBar!=_showsStatusBarBackgroundView){ + _showsStatusBarBackgroundView = showsDummyStatusBar; + CGRect frame = self.childControllerContainerView.frame; + if(_showsStatusBarBackgroundView){ + frame.origin.y = 20; + frame.size.height = CGRectGetHeight(self.view.bounds)-20; + } + else { + frame.origin.y = 0; + frame.size.height = CGRectGetHeight(self.view.bounds); + } + [self.childControllerContainerView setFrame:frame]; + [self.dummyStatusBarView setHidden:!showsDummyStatusBar]; + } +} + +-(void)setStatusBarViewBackgroundColor:(UIColor *)dummyStatusBarColor{ + _statusBarViewBackgroundColor = dummyStatusBarColor; + [self.dummyStatusBarView setBackgroundColor:_statusBarViewBackgroundColor]; +} + +-(void)setAnimatingDrawer:(BOOL)animatingDrawer{ + _animatingDrawer = animatingDrawer; + [self.view setUserInteractionEnabled:!animatingDrawer]; +} + +#pragma mark - Getters +-(CGFloat)maximumLeftDrawerWidth{ + if(self.leftDrawerViewController){ + return _maximumLeftDrawerWidth; + } + else{ + return 0; + } +} + +-(CGFloat)maximumRightDrawerWidth{ + if(self.rightDrawerViewController){ + return _maximumRightDrawerWidth; + } + else { + return 0; + } +} + +-(CGFloat)visibleLeftDrawerWidth{ + return MAX(0.0,CGRectGetMinX(self.centerContainerView.frame)); +} + +-(CGFloat)visibleRightDrawerWidth{ + if(CGRectGetMinX(self.centerContainerView.frame)<0){ + return CGRectGetWidth(self.childControllerContainerView.bounds)-CGRectGetMaxX(self.centerContainerView.frame); + } + else { + return 0.0f; + } +} + +-(UIView*)childControllerContainerView{ + if(_childControllerContainerView == nil){ + //Issue #152 (https://github.com/mutualmobile/MMDrawerController/issues/152) + //Turns out we have two child container views getting added to the view during init, + //because the first request self.view.bounds was kicking off a viewDidLoad, which + //caused us to be able to fall through this check twice. + // + //The fix is to grab the bounds, and then check again that the child container view has + //not been created. + CGRect childContainerViewFrame = self.view.bounds; + if(_childControllerContainerView == nil){ + _childControllerContainerView = [[UIView alloc] initWithFrame:childContainerViewFrame]; + [_childControllerContainerView setBackgroundColor:[UIColor clearColor]]; + [_childControllerContainerView setAutoresizingMask:UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth]; + [self.view addSubview:_childControllerContainerView]; + } + + } + return _childControllerContainerView; +} + +-(UIView*)dummyStatusBarView{ + if(_dummyStatusBarView==nil){ + _dummyStatusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 20)]; + [_dummyStatusBarView setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [_dummyStatusBarView setBackgroundColor:self.statusBarViewBackgroundColor]; + [_dummyStatusBarView setHidden:!_showsStatusBarBackgroundView]; + [self.view addSubview:_dummyStatusBarView]; + } + return _dummyStatusBarView; +} + +-(UIColor*)statusBarViewBackgroundColor{ + if(_statusBarViewBackgroundColor == nil){ + _statusBarViewBackgroundColor = [UIColor blackColor]; + } + return _statusBarViewBackgroundColor; +} + +#pragma mark - Gesture Handlers + +-(void)tapGestureCallback:(UITapGestureRecognizer *)tapGesture{ + if(self.openSide != MMDrawerSideNone && + self.isAnimatingDrawer == NO){ + [self closeDrawerAnimated:YES completion:^(BOOL finished) { + if(self.gestureCompletion){ + self.gestureCompletion(self, tapGesture); + } + }]; + } +} + +-(void)panGestureCallback:(UIPanGestureRecognizer *)panGesture{ + switch (panGesture.state) { + case UIGestureRecognizerStateBegan:{ + if(self.animatingDrawer){ + [panGesture setEnabled:NO]; + break; + } + else { + self.startingPanRect = self.centerContainerView.frame; + } + } + case UIGestureRecognizerStateChanged:{ + self.view.userInteractionEnabled = NO; + CGRect newFrame = self.startingPanRect; + CGPoint translatedPoint = [panGesture translationInView:self.centerContainerView]; + newFrame.origin.x = [self roundedOriginXForDrawerConstriants:CGRectGetMinX(self.startingPanRect)+translatedPoint.x]; + newFrame = CGRectIntegral(newFrame); + CGFloat xOffset = newFrame.origin.x; + + MMDrawerSide visibleSide = MMDrawerSideNone; + CGFloat percentVisible = 0.0; + if(xOffset > 0){ + visibleSide = MMDrawerSideLeft; + percentVisible = xOffset/self.maximumLeftDrawerWidth; + } + else if(xOffset < 0){ + visibleSide = MMDrawerSideRight; + percentVisible = ABS(xOffset)/self.maximumRightDrawerWidth; + } + UIViewController * visibleSideDrawerViewController = [self sideDrawerViewControllerForSide:visibleSide]; + + if(self.openSide != visibleSide){ + //Handle disappearing the visible drawer + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:self.openSide]; + [sideDrawerViewController beginAppearanceTransition:NO animated:NO]; + [sideDrawerViewController endAppearanceTransition]; + + //Drawer is about to become visible + [self prepareToPresentDrawer:visibleSide animated:NO]; + [visibleSideDrawerViewController endAppearanceTransition]; + [self setOpenSide:visibleSide]; + } + else if(visibleSide == MMDrawerSideNone){ + [self setOpenSide:MMDrawerSideNone]; + } + + [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:percentVisible]; + + [self.centerContainerView setCenter:CGPointMake(CGRectGetMidX(newFrame), CGRectGetMidY(newFrame))]; + + newFrame = self.centerContainerView.frame; + newFrame.origin.x = floor(newFrame.origin.x); + newFrame.origin.y = floor(newFrame.origin.y); + self.centerContainerView.frame = newFrame; + + break; + } + case UIGestureRecognizerStateEnded: + case UIGestureRecognizerStateCancelled: { + self.startingPanRect = CGRectNull; + CGPoint velocity = [panGesture velocityInView:self.childControllerContainerView]; + [self finishAnimationForPanGestureWithXVelocity:velocity.x completion:^(BOOL finished) { + if(self.gestureCompletion){ + self.gestureCompletion(self, panGesture); + } + }]; + self.view.userInteractionEnabled = YES; + break; + } + default: + break; + } +} + +#pragma mark - iOS 7 Status Bar Helpers +-(UIViewController*)childViewControllerForStatusBarStyle{ + return [self childViewControllerForSide:self.openSide]; +} + +-(UIViewController*)childViewControllerForStatusBarHidden{ + return [self childViewControllerForSide:self.openSide]; +} + +-(void)setNeedsStatusBarAppearanceUpdateIfSupported{ + if([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]){ + [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)]; + } +} + +#pragma mark - iOS 8 Rotation Helpers +- (BOOL)needsManualForwardingOfRotationEvents{ + BOOL isIOS8 = (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1); + return !isIOS8; +} + +#pragma mark - Animation helpers +-(void)finishAnimationForPanGestureWithXVelocity:(CGFloat)xVelocity completion:(void(^)(BOOL finished))completion{ + CGFloat currentOriginX = CGRectGetMinX(self.centerContainerView.frame); + + CGFloat animationVelocity = MAX(ABS(xVelocity),self.panVelocityXAnimationThreshold*2); + + if(self.openSide == MMDrawerSideLeft) { + CGFloat midPoint = self.maximumLeftDrawerWidth / 2.0; + if(xVelocity > self.panVelocityXAnimationThreshold){ + [self openDrawerSide:MMDrawerSideLeft animated:YES velocity:animationVelocity animationOptions:UIViewAnimationOptionCurveEaseOut completion:completion]; + } + else if(xVelocity < -self.panVelocityXAnimationThreshold){ + [self closeDrawerAnimated:YES velocity:animationVelocity animationOptions:UIViewAnimationOptionCurveEaseOut completion:completion]; + } + else if(currentOriginX < midPoint){ + [self closeDrawerAnimated:YES completion:completion]; + } + else { + [self openDrawerSide:MMDrawerSideLeft animated:YES completion:completion]; + } + } + else if(self.openSide == MMDrawerSideRight){ + currentOriginX = CGRectGetMaxX(self.centerContainerView.frame); + CGFloat midPoint = (CGRectGetWidth(self.childControllerContainerView.bounds)-self.maximumRightDrawerWidth) + (self.maximumRightDrawerWidth / 2.0); + if(xVelocity > self.panVelocityXAnimationThreshold){ + [self closeDrawerAnimated:YES velocity:animationVelocity animationOptions:UIViewAnimationOptionCurveEaseOut completion:completion]; + } + else if (xVelocity < -self.panVelocityXAnimationThreshold){ + [self openDrawerSide:MMDrawerSideRight animated:YES velocity:animationVelocity animationOptions:UIViewAnimationOptionCurveEaseOut completion:completion]; + } + else if(currentOriginX > midPoint){ + [self closeDrawerAnimated:YES completion:completion]; + } + else { + [self openDrawerSide:MMDrawerSideRight animated:YES completion:completion]; + } + } + else { + if(completion){ + completion(NO); + } + } +} + +-(void)updateDrawerVisualStateForDrawerSide:(MMDrawerSide)drawerSide percentVisible:(CGFloat)percentVisible{ + if(self.drawerVisualState){ + self.drawerVisualState(self,drawerSide,percentVisible); + } + else if(self.shouldStretchDrawer){ + [self applyOvershootScaleTransformForDrawerSide:drawerSide percentVisible:percentVisible]; + } +} + +- (void)applyOvershootScaleTransformForDrawerSide:(MMDrawerSide)drawerSide percentVisible:(CGFloat)percentVisible{ + + if (percentVisible >= 1.f) { + CATransform3D transform = CATransform3DIdentity; + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + if(drawerSide == MMDrawerSideLeft) { + transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + transform = CATransform3DTranslate(transform, self.maximumLeftDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); + } + else if(drawerSide == MMDrawerSideRight){ + transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + transform = CATransform3DTranslate(transform, -self.maximumRightDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); + } + sideDrawerViewController.view.layer.transform = transform; + } +} + +-(void)resetDrawerVisualStateForDrawerSide:(MMDrawerSide)drawerSide{ + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + + [sideDrawerViewController.view.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)]; + [sideDrawerViewController.view.layer setTransform:CATransform3DIdentity]; + [sideDrawerViewController.view setAlpha:1.0]; +} + +-(CGFloat)roundedOriginXForDrawerConstriants:(CGFloat)originX{ + + if (originX < -self.maximumRightDrawerWidth) { + if (self.shouldStretchDrawer && + self.rightDrawerViewController) { + CGFloat maxOvershoot = (CGRectGetWidth(self.centerContainerView.frame)-self.maximumRightDrawerWidth)*MMDrawerOvershootPercentage; + return originXForDrawerOriginAndTargetOriginOffset(originX, -self.maximumRightDrawerWidth, maxOvershoot); + } + else{ + return -self.maximumRightDrawerWidth; + } + } + else if(originX > self.maximumLeftDrawerWidth){ + if (self.shouldStretchDrawer && + self.leftDrawerViewController) { + CGFloat maxOvershoot = (CGRectGetWidth(self.centerContainerView.frame)-self.maximumLeftDrawerWidth)*MMDrawerOvershootPercentage; + return originXForDrawerOriginAndTargetOriginOffset(originX, self.maximumLeftDrawerWidth, maxOvershoot); + } + else{ + return self.maximumLeftDrawerWidth; + } + } + + return originX; +} + +static inline CGFloat originXForDrawerOriginAndTargetOriginOffset(CGFloat originX, CGFloat targetOffset, CGFloat maxOvershoot){ + CGFloat delta = ABS(originX - targetOffset); + CGFloat maxLinearPercentage = MMDrawerOvershootLinearRangePercentage; + CGFloat nonLinearRange = maxOvershoot * maxLinearPercentage; + CGFloat nonLinearScalingDelta = (delta - nonLinearRange); + CGFloat overshoot = nonLinearRange + nonLinearScalingDelta * nonLinearRange/sqrt(pow(nonLinearScalingDelta,2.f) + 15000); + + if (delta < nonLinearRange) { + return originX; + } + else if (targetOffset < 0) { + return targetOffset - round(overshoot); + } + else{ + return targetOffset + round(overshoot); + } +} + +#pragma mark - Helpers +-(void)setupGestureRecognizers{ + UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureCallback:)]; + [pan setDelegate:self]; + [self.view addGestureRecognizer:pan]; + + UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureCallback:)]; + [tap setDelegate:self]; + [self.view addGestureRecognizer:tap]; +} + +-(void)prepareToPresentDrawer:(MMDrawerSide)drawer animated:(BOOL)animated{ + MMDrawerSide drawerToHide = MMDrawerSideNone; + if(drawer == MMDrawerSideLeft){ + drawerToHide = MMDrawerSideRight; + } + else if(drawer == MMDrawerSideRight){ + drawerToHide = MMDrawerSideLeft; + } + + UIViewController * sideDrawerViewControllerToPresent = [self sideDrawerViewControllerForSide:drawer]; + UIViewController * sideDrawerViewControllerToHide = [self sideDrawerViewControllerForSide:drawerToHide]; + + [self.childControllerContainerView sendSubviewToBack:sideDrawerViewControllerToHide.view]; + [sideDrawerViewControllerToHide.view setHidden:YES]; + [sideDrawerViewControllerToPresent.view setHidden:NO]; + [self resetDrawerVisualStateForDrawerSide:drawer]; + [sideDrawerViewControllerToPresent.view setFrame:sideDrawerViewControllerToPresent.mm_visibleDrawerFrame]; + [self updateDrawerVisualStateForDrawerSide:drawer percentVisible:0.0]; + [sideDrawerViewControllerToPresent beginAppearanceTransition:YES animated:animated]; +} + +-(void)updateShadowForCenterView{ + UIView * centerView = self.centerContainerView; + if(self.showsShadow){ + centerView.layer.masksToBounds = NO; + centerView.layer.shadowRadius = self.shadowRadius; + centerView.layer.shadowOpacity = self.shadowOpacity; + centerView.layer.shadowOffset = self.shadowOffset; + centerView.layer.shadowColor = [self.shadowColor CGColor]; + + /** In the event this gets called a lot, we won't update the shadowPath + unless it needs to be updated (like during rotation) */ + if (centerView.layer.shadowPath == NULL) { + centerView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:self.centerContainerView.bounds] CGPath]; + } + else{ + CGRect currentPath = CGPathGetPathBoundingBox(centerView.layer.shadowPath); + if (CGRectEqualToRect(currentPath, centerView.bounds) == NO){ + centerView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:self.centerContainerView.bounds] CGPath]; + } + } + } + else if (centerView.layer.shadowPath != NULL) { + centerView.layer.shadowRadius = 0.f; + centerView.layer.shadowOpacity = 0.f; + centerView.layer.shadowOffset = CGSizeMake(0, -3); + centerView.layer.shadowPath = NULL; + centerView.layer.masksToBounds = YES; + } +} + +-(NSTimeInterval)animationDurationForAnimationDistance:(CGFloat)distance{ + NSTimeInterval duration = MAX(distance/self.animationVelocity,MMDrawerMinimumAnimationDuration); + return duration; +} + +-(UIViewController*)sideDrawerViewControllerForSide:(MMDrawerSide)drawerSide{ + UIViewController * sideDrawerViewController = nil; + if(drawerSide != MMDrawerSideNone){ + sideDrawerViewController = [self childViewControllerForSide:drawerSide]; + } + return sideDrawerViewController; +} + +-(UIViewController*)childViewControllerForSide:(MMDrawerSide)drawerSide{ + UIViewController * childViewController = nil; + switch (drawerSide) { + case MMDrawerSideLeft: + childViewController = self.leftDrawerViewController; + break; + case MMDrawerSideRight: + childViewController = self.rightDrawerViewController; + break; + case MMDrawerSideNone: + childViewController = self.centerViewController; + break; + } + return childViewController; +} + +#pragma mark - UIGestureRecognizerDelegate +-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{ + + if(self.openSide == MMDrawerSideNone){ + MMOpenDrawerGestureMode possibleOpenGestureModes = [self possibleOpenGestureModesForGestureRecognizer:gestureRecognizer + withTouch:touch]; + return ((self.openDrawerGestureModeMask & possibleOpenGestureModes)>0); + } + else{ + MMCloseDrawerGestureMode possibleCloseGestureModes = [self possibleCloseGestureModesForGestureRecognizer:gestureRecognizer + withTouch:touch]; + return ((self.closeDrawerGestureModeMask & possibleCloseGestureModes)>0); + } +} + +#pragma mark Gesture Recogizner Delegate Helpers +-(MMCloseDrawerGestureMode)possibleCloseGestureModesForGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer withTouch:(UITouch*)touch{ + CGPoint point = [touch locationInView:self.childControllerContainerView]; + MMCloseDrawerGestureMode possibleCloseGestureModes = MMCloseDrawerGestureModeNone; + if([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){ + if([self isPointContainedWithinNavigationRect:point]){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeTapNavigationBar; + } + if([self isPointContainedWithinCenterViewContentRect:point]){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeTapCenterView; + } + } + else if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]){ + if([self isPointContainedWithinNavigationRect:point]){ + possibleCloseGestureModes |= MMCloseDrawerGestureModePanningNavigationBar; + } + if([self isPointContainedWithinCenterViewContentRect:point]){ + possibleCloseGestureModes |= MMCloseDrawerGestureModePanningCenterView; + } + if([self isPointContainedWithinRightBezelRect:point] && + self.openSide == MMDrawerSideLeft){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeBezelPanningCenterView; + } + if([self isPointContainedWithinLeftBezelRect:point] && + self.openSide == MMDrawerSideRight){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeBezelPanningCenterView; + } + if([self isPointContainedWithinCenterViewContentRect:point] == NO && + [self isPointContainedWithinNavigationRect:point] == NO){ + possibleCloseGestureModes |= MMCloseDrawerGestureModePanningDrawerView; + } + } + if((self.closeDrawerGestureModeMask & MMCloseDrawerGestureModeCustom) > 0 && + self.gestureShouldRecognizeTouch){ + if(self.gestureShouldRecognizeTouch(self,gestureRecognizer,touch)){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeCustom; + } + } + return possibleCloseGestureModes; +} + +-(MMOpenDrawerGestureMode)possibleOpenGestureModesForGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer withTouch:(UITouch*)touch{ + CGPoint point = [touch locationInView:self.childControllerContainerView]; + MMOpenDrawerGestureMode possibleOpenGestureModes = MMOpenDrawerGestureModeNone; + if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]){ + if([self isPointContainedWithinNavigationRect:point]){ + possibleOpenGestureModes |= MMOpenDrawerGestureModePanningNavigationBar; + } + if([self isPointContainedWithinCenterViewContentRect:point]){ + possibleOpenGestureModes |= MMOpenDrawerGestureModePanningCenterView; + } + if([self isPointContainedWithinLeftBezelRect:point] && + self.leftDrawerViewController){ + possibleOpenGestureModes |= MMOpenDrawerGestureModeBezelPanningCenterView; + } + if([self isPointContainedWithinRightBezelRect:point] && + self.rightDrawerViewController){ + possibleOpenGestureModes |= MMOpenDrawerGestureModeBezelPanningCenterView; + } + } + if((self.openDrawerGestureModeMask & MMOpenDrawerGestureModeCustom) > 0 && + self.gestureShouldRecognizeTouch){ + if(self.gestureShouldRecognizeTouch(self,gestureRecognizer,touch)){ + possibleOpenGestureModes |= MMOpenDrawerGestureModeCustom; + } + } + return possibleOpenGestureModes; +} + +-(BOOL)isPointContainedWithinNavigationRect:(CGPoint)point{ + CGRect navigationBarRect = CGRectNull; + if([self.centerViewController isKindOfClass:[UINavigationController class]]){ + UINavigationBar * navBar = [(UINavigationController*)self.centerViewController navigationBar]; + navigationBarRect = [navBar convertRect:navBar.bounds toView:self.childControllerContainerView]; + navigationBarRect = CGRectIntersection(navigationBarRect,self.childControllerContainerView.bounds); + } + return CGRectContainsPoint(navigationBarRect,point); +} + +-(BOOL)isPointContainedWithinCenterViewContentRect:(CGPoint)point{ + CGRect centerViewContentRect = self.centerContainerView.frame; + centerViewContentRect = CGRectIntersection(centerViewContentRect,self.childControllerContainerView.bounds); + return (CGRectContainsPoint(centerViewContentRect, point) && + [self isPointContainedWithinNavigationRect:point] == NO); +} + +-(BOOL)isPointContainedWithinLeftBezelRect:(CGPoint)point{ + CGRect leftBezelRect = CGRectNull; + CGRect tempRect; + CGRectDivide(self.childControllerContainerView.bounds, &leftBezelRect, &tempRect, self.bezelPanningCenterViewRange, CGRectMinXEdge); + return (CGRectContainsPoint(leftBezelRect, point) && + [self isPointContainedWithinCenterViewContentRect:point]); +} + +-(BOOL)isPointContainedWithinRightBezelRect:(CGPoint)point{ + CGRect rightBezelRect = CGRectNull; + CGRect tempRect; + CGRectDivide(self.childControllerContainerView.bounds, &rightBezelRect, &tempRect, self.bezelPanningCenterViewRange, CGRectMaxXEdge); + + return (CGRectContainsPoint(rightBezelRect, point) && + [self isPointContainedWithinCenterViewContentRect:point]); +} +@end diff --git a/ListApp/MMDrawerVisualState.h b/ListApp/MMDrawerVisualState.h new file mode 100755 index 0000000..7cb8fff --- /dev/null +++ b/ListApp/MMDrawerVisualState.h @@ -0,0 +1,61 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 +#import "MMDrawerController.h" + +/** + `MMDrawerVisualState` provides prebuilt visual state update blocks that can be used in the callback block of `MMDrawerController` for drawer animation. + */ + +@interface MMDrawerVisualState : NSObject + +/** + Creates a slide and scale visual state block that gives an experience similar to Mailbox.app. It scales from 90% to 100%, and translates 50 pixels in the x direction. In addition, it also sets alpha from 0.0 to 1.0. + + @return The visual state block. + */ ++(MMDrawerControllerDrawerVisualStateBlock)slideAndScaleVisualStateBlock; + +/** + Creates a slide visual state block that gives the user an experience that slides at the same speed of the center view controller during animation. This is equal to calling `parallaxVisualStateBlockWithParallaxFactor:` with a parallax factor of 1.0. + + @return The visual state block. + */ ++(MMDrawerControllerDrawerVisualStateBlock)slideVisualStateBlock; + +/** + Creates a swinging door visual state block that gives the user an experience that animates the drawer in along the hinge. + + @return The visual state block. + */ ++(MMDrawerControllerDrawerVisualStateBlock)swingingDoorVisualStateBlock; + +/** + Creates a parallax experience that slides the side drawer view controller at a different rate than the center view controller during animation. For every parallaxFactor of points moved by the center view controller, the side drawer view controller will move 1 point. Passing in 1.0 is the equivalent of a applying a sliding animation, while passing in MAX_FLOAT is the equivalent of having no animation at all. + + @param parallaxFactor The amount of parallax applied to the side drawer conroller. This value must be greater than 1.0. The closer the value is to 1.0, the faster the side drawer view controller will be parallaxing. + + @return The visual state block. + */ ++(MMDrawerControllerDrawerVisualStateBlock)parallaxVisualStateBlockWithParallaxFactor:(CGFloat)parallaxFactor; + +@end diff --git a/ListApp/MMDrawerVisualState.m b/ListApp/MMDrawerVisualState.m new file mode 100755 index 0000000..013d019 --- /dev/null +++ b/ListApp/MMDrawerVisualState.m @@ -0,0 +1,150 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "MMDrawerVisualState.h" +#import + +@implementation MMDrawerVisualState ++(MMDrawerControllerDrawerVisualStateBlock)slideAndScaleVisualStateBlock{ + MMDrawerControllerDrawerVisualStateBlock visualStateBlock = + ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ + CGFloat minScale = .90; + CGFloat scale = minScale + (percentVisible*(1.0-minScale)); + CATransform3D scaleTransform = CATransform3DMakeScale(scale, scale, scale); + + CGFloat maxDistance = 50; + CGFloat distance = maxDistance * percentVisible; + CATransform3D translateTransform = CATransform3DIdentity; + UIViewController * sideDrawerViewController; + if(drawerSide == MMDrawerSideLeft) { + sideDrawerViewController = drawerController.leftDrawerViewController; + translateTransform = CATransform3DMakeTranslation((maxDistance-distance), 0.0, 0.0); + } + else if(drawerSide == MMDrawerSideRight){ + sideDrawerViewController = drawerController.rightDrawerViewController; + translateTransform = CATransform3DMakeTranslation(-(maxDistance-distance), 0.0, 0.0); + } + + [sideDrawerViewController.view.layer setTransform:CATransform3DConcat(scaleTransform, translateTransform)]; + [sideDrawerViewController.view setAlpha:percentVisible]; + }; + return visualStateBlock; +} + ++(MMDrawerControllerDrawerVisualStateBlock)slideVisualStateBlock{ + return [self parallaxVisualStateBlockWithParallaxFactor:1.0]; +} + + ++(MMDrawerControllerDrawerVisualStateBlock)swingingDoorVisualStateBlock{ + MMDrawerControllerDrawerVisualStateBlock visualStateBlock = + ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ + UIViewController * sideDrawerViewController; + CGPoint anchorPoint; + CGFloat maxDrawerWidth = 0.0; + CGFloat xOffset; + CGFloat angle = 0.0; + + if(drawerSide==MMDrawerSideLeft){ + + sideDrawerViewController = drawerController.leftDrawerViewController; + anchorPoint = CGPointMake(1.0, .5); + maxDrawerWidth = MAX(drawerController.maximumLeftDrawerWidth,drawerController.visibleLeftDrawerWidth); + xOffset = -(maxDrawerWidth/2.0) + (maxDrawerWidth)*percentVisible; + angle = -M_PI_2+(percentVisible*M_PI_2); + } + else { + sideDrawerViewController = drawerController.rightDrawerViewController; + anchorPoint = CGPointMake(0.0, .5); + maxDrawerWidth = MAX(drawerController.maximumRightDrawerWidth,drawerController.visibleRightDrawerWidth); + xOffset = (maxDrawerWidth/2.0) - (maxDrawerWidth)*percentVisible; + angle = M_PI_2-(percentVisible*M_PI_2); + } + + [sideDrawerViewController.view.layer setAnchorPoint:anchorPoint]; + [sideDrawerViewController.view.layer setShouldRasterize:YES]; + [sideDrawerViewController.view.layer setRasterizationScale:[[UIScreen mainScreen] scale]]; + + CATransform3D swingingDoorTransform = CATransform3DIdentity; + if (percentVisible <= 1.f) { + + CATransform3D identity = CATransform3DIdentity; + identity.m34 = -1.0/1000.0; + CATransform3D rotateTransform = CATransform3DRotate(identity, angle, 0.0, 1.0, 0.0); + + CATransform3D translateTransform = CATransform3DMakeTranslation(xOffset, 0.0, 0.0); + + CATransform3D concatTransform = CATransform3DConcat(rotateTransform, translateTransform); + + swingingDoorTransform = concatTransform; + } + else{ + CATransform3D overshootTransform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + + NSInteger scalingModifier = 1.f; + if (drawerSide == MMDrawerSideRight) { + scalingModifier = -1.f; + } + + overshootTransform = CATransform3DTranslate(overshootTransform, scalingModifier*maxDrawerWidth/2, 0.f, 0.f); + swingingDoorTransform = overshootTransform; + } + + [sideDrawerViewController.view.layer setTransform:swingingDoorTransform]; + }; + return visualStateBlock; +} + ++(MMDrawerControllerDrawerVisualStateBlock)parallaxVisualStateBlockWithParallaxFactor:(CGFloat)parallaxFactor{ + MMDrawerControllerDrawerVisualStateBlock visualStateBlock = + ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ + NSParameterAssert(parallaxFactor >= 1.0); + CATransform3D transform = CATransform3DIdentity; + UIViewController * sideDrawerViewController; + if(drawerSide == MMDrawerSideLeft) { + sideDrawerViewController = drawerController.leftDrawerViewController; + CGFloat distance = MAX(drawerController.maximumLeftDrawerWidth,drawerController.visibleLeftDrawerWidth); + if (percentVisible <= 1.f) { + transform = CATransform3DMakeTranslation((-distance)/parallaxFactor+(distance*percentVisible/parallaxFactor), 0.0, 0.0); + } + else{ + transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + transform = CATransform3DTranslate(transform, drawerController.maximumLeftDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); + } + } + else if(drawerSide == MMDrawerSideRight){ + sideDrawerViewController = drawerController.rightDrawerViewController; + CGFloat distance = MAX(drawerController.maximumRightDrawerWidth,drawerController.visibleRightDrawerWidth); + if(percentVisible <= 1.f){ + transform = CATransform3DMakeTranslation((distance)/parallaxFactor-(distance*percentVisible)/parallaxFactor, 0.0, 0.0); + } + else{ + transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + transform = CATransform3DTranslate(transform, -drawerController.maximumRightDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); + } + } + + [sideDrawerViewController.view.layer setTransform:transform]; + }; + return visualStateBlock; +} + +@end diff --git a/ListApp/MainVC.swift b/ListApp/MainVC.swift old mode 100644 new mode 100755 diff --git a/ListApp/MenuVC.swift b/ListApp/MenuVC.swift new file mode 100755 index 0000000..6890aca --- /dev/null +++ b/ListApp/MenuVC.swift @@ -0,0 +1,92 @@ +// +// MenuVC.swift +// ListApp +// +// Created by Suruchi on 19/11/2015. +// Copyright © 2015 BAPAT. All rights reserved. +// + +import UIKit + +class MenuVC: UIViewController, UITableViewDataSource, UITableViewDelegate{ + + var menuItems:[String] = ["Main","About","Privacy Policy"]; + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + + return menuItems.count; + } + + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + + let mycell = tableView.dequeueReusableCellWithIdentifier("MyCell", forIndexPath: indexPath) as! menuCustomTableViewCell + mycell.menuItemLabel.text = menuItems[indexPath.row] + return mycell + + } + + func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath){ + switch(indexPath.row){ + case 0://Main + + let centerViewController = self.storyboard?.instantiateViewControllerWithIdentifier("TableVC") as! TableVC + let centerNavController = UINavigationController(rootViewController: centerViewController) + let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate + + appDelegate.centerContainer!.centerViewController = centerNavController + appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil) + break; + + case 1://About + + let aboutViewController = self.storyboard?.instantiateViewControllerWithIdentifier("AboutVC") as! AboutVC + let aboutNavController = UINavigationController(rootViewController: aboutViewController) + let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate + + appDelegate.centerContainer!.centerViewController = aboutNavController + appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil) + break; + + case 2://Privacy + + let privacyViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PrivacyVC") as! PrivacyVC + let privacyNavController = UINavigationController(rootViewController: privacyViewController) + let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate + + appDelegate.centerContainer!.centerViewController = privacyNavController + appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil) + break; + default: + // print("\(menuItems[indexPath.row]) is selected); + break; + + + + + } + } + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + // Get the new view controller using segue.destinationViewController. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/ListApp/PrivacyVC.swift b/ListApp/PrivacyVC.swift new file mode 100755 index 0000000..a4f0b79 --- /dev/null +++ b/ListApp/PrivacyVC.swift @@ -0,0 +1,35 @@ +// +// PrivacyVC.swift +// ListApp +// +// Created by Suruchi on 19/11/2015. +// Copyright © 2015 BAPAT. All rights reserved. +// + +import UIKit + +class PrivacyVC: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + // Get the new view controller using segue.destinationViewController. + // Pass the selected object to the new view controller. + } + */ + +} diff --git a/ListApp/ShopList+CoreDataProperties.swift b/ListApp/ShopList+CoreDataProperties.swift old mode 100644 new mode 100755 diff --git a/ListApp/ShopList.swift b/ListApp/ShopList.swift old mode 100644 new mode 100755 diff --git a/ListApp/UIViewController+MMDrawerController.h b/ListApp/UIViewController+MMDrawerController.h new file mode 100755 index 0000000..1bf05ef --- /dev/null +++ b/ListApp/UIViewController+MMDrawerController.h @@ -0,0 +1,45 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 + +#import "MMDrawerController.h" + +/** + A helper category on `UIViewController` that exposes the parent drawer controller, the visible side drawer frame, and a `mm_drawerWillAppear` method that is called when the drawer is about to appear. + */ + +@interface UIViewController (MMDrawerController) + +///--------------------------------------- +/// @name Accessing Drawer View Controller Properties +///--------------------------------------- + +/** + The `MMDrawerController` that the view controller is contained within. If the view controller is not contained within a `MMDrawerController`, this property is nil. Note that if the view controller is contained within a `UINavigationController`, that navigation controller is contained within a `MMDrawerController`, this property will return a refernce to the `MMDrawerController`, despite the fact that it is not the direct parent of the view controller. + */ +@property(nonatomic, strong, readonly) MMDrawerController *mm_drawerController; + +/** + The visible rect of the side drawer view controller in the drawer controller coordinate space. If the view controller is not a drawer in a `MMDrawerController`, then this property returns `CGRectNull` + */ +@property(nonatomic, assign, readonly) CGRect mm_visibleDrawerFrame; + +@end diff --git a/ListApp/UIViewController+MMDrawerController.m b/ListApp/UIViewController+MMDrawerController.m new file mode 100755 index 0000000..b1f8662 --- /dev/null +++ b/ListApp/UIViewController+MMDrawerController.m @@ -0,0 +1,64 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "UIViewController+MMDrawerController.h" + +@implementation UIViewController (MMDrawerController) + + +-(MMDrawerController*)mm_drawerController{ + UIViewController *parentViewController = self.parentViewController; + while (parentViewController != nil) { + if([parentViewController isKindOfClass:[MMDrawerController class]]){ + return (MMDrawerController *)parentViewController; + } + parentViewController = parentViewController.parentViewController; + } + return nil; +} + +-(CGRect)mm_visibleDrawerFrame{ + if([self isEqual:self.mm_drawerController.leftDrawerViewController] || + [self.navigationController isEqual:self.mm_drawerController.leftDrawerViewController]){ + CGRect rect = self.mm_drawerController.view.bounds; + rect.size.width = self.mm_drawerController.maximumLeftDrawerWidth; + if (self.mm_drawerController.showsStatusBarBackgroundView) { + rect.size.height -= 20.0f; + } + return rect; + + } + else if([self isEqual:self.mm_drawerController.rightDrawerViewController] || + [self.navigationController isEqual:self.mm_drawerController.rightDrawerViewController]){ + CGRect rect = self.mm_drawerController.view.bounds; + rect.size.width = self.mm_drawerController.maximumRightDrawerWidth; + rect.origin.x = CGRectGetWidth(self.mm_drawerController.view.bounds)-rect.size.width; + if (self.mm_drawerController.showsStatusBarBackgroundView) { + rect.size.height -= 20.0f; + } + return rect; + } + else { + return CGRectNull; + } +} + +@end diff --git a/ListApp/menuCustomTableViewCell.swift b/ListApp/menuCustomTableViewCell.swift new file mode 100755 index 0000000..eacab94 --- /dev/null +++ b/ListApp/menuCustomTableViewCell.swift @@ -0,0 +1,26 @@ +// +// menuCustomTableViewCell.swift +// ListApp +// +// Created by Suruchi on 19/11/2015. +// Copyright © 2015 BAPAT. All rights reserved. +// + +import UIKit + +class menuCustomTableViewCell: UITableViewCell { + + @IBOutlet weak var menuItemLabel: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + override func setSelected(selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + +} diff --git a/ListAppTests/Info.plist b/ListAppTests/Info.plist old mode 100644 new mode 100755 diff --git a/ListAppTests/ListAppTests.swift b/ListAppTests/ListAppTests.swift old mode 100644 new mode 100755 diff --git a/ListAppUITests/Info.plist b/ListAppUITests/Info.plist old mode 100644 new mode 100755 diff --git a/ListAppUITests/ListAppUITests.swift b/ListAppUITests/ListAppUITests.swift old mode 100644 new mode 100755 diff --git a/MMDrawerController/MMDrawerBarButtonItem.h b/MMDrawerController/MMDrawerBarButtonItem.h new file mode 100755 index 0000000..f0ee364 --- /dev/null +++ b/MMDrawerController/MMDrawerBarButtonItem.h @@ -0,0 +1,77 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 + +/** + `MMDrawerBarButtonItem` provides convenience methods to create `UIBarButtonItems` with a default hamburger-menu asset. + */ + +@interface MMDrawerBarButtonItem : UIBarButtonItem + +///--------------------------------------- +/// @name Initializing a `MMDrawerBarButtonItem` +///--------------------------------------- + +/** + Creates and initializes an `MMDrawerBarButtonItem` without a border. + + @param target The target to forward the `action` to when the button is pressed. + @param action The action to call when the button is pressed. + + @return The newly-initialized bar button item. + */ +-(instancetype)initWithTarget:(id)target action:(SEL)action; + +/** + Returns the current color of the menu button for the state requested. This property is deprecated in iOS 7.0. Use `tintColor` instead. + + @param state The UIControl state that the color is being requested for. + + @return The menu button color for the requested state. + */ +-(UIColor *)menuButtonColorForState:(UIControlState)state __attribute__((deprecated("Use tintColor instead"))); + +/** + Sets the color of the menu button for the specified state. For this control, only set colors for `UIControlStateNormal` and `UIControlStateHighlighted`. This property is deprecated in iOS 7.0. Use `tintColor` instead. + + @param color The color to set. + @param state The state to set the color for. + */ +-(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state __attribute__((deprecated("Use tintColor instead"))); + +/** + Returns the current color of the shadow for the state requested. This property is deprecated in iOS 7.0. The menu button no longer supports a shadow. + + @param state The UIControl state that the color is being requested for. + + @return The menu button color for the requested state. + */ +-(UIColor *)shadowColorForState:(UIControlState)state __attribute__((deprecated("Shadow is no longer supported"))); + +/** + Sets the color of the shadow for the specified state. For this control, only set colors for `UIControlStateNormal` and `UIControlStateHighlighted`. This property is deprecated in iOS 7.0. The menu button no longer supports a shadow. + + @param color The color to set. + @param state The state to set the color for. + */ +-(void)setShadowColor:(UIColor *)color forState:(UIControlState)state __attribute__((deprecated("Shadow is no longer supported"))); + +@end diff --git a/MMDrawerController/MMDrawerBarButtonItem.m b/MMDrawerController/MMDrawerBarButtonItem.m new file mode 100755 index 0000000..d29609c --- /dev/null +++ b/MMDrawerController/MMDrawerBarButtonItem.m @@ -0,0 +1,302 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "MMDrawerBarButtonItem.h" + +@interface MMDrawerMenuButtonView : UIButton +@property (nonatomic,strong) UIColor * menuButtonNormalColor; +@property (nonatomic,strong) UIColor * menuButtonHighlightedColor; + +@property (nonatomic,strong) UIColor * shadowNormalColor; +@property (nonatomic,strong) UIColor * shadowHighlightedColor; + +-(UIColor *)menuButtonColorForState:(UIControlState)state; +-(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state; + +-(UIColor *)shadowColorForState:(UIControlState)state; +-(void)setShadowColor:(UIColor *)color forState:(UIControlState)state; + +@end + +@implementation MMDrawerMenuButtonView + +-(instancetype)initWithFrame:(CGRect)frame{ + self = [super initWithFrame:frame]; + if(self){ + [self setMenuButtonNormalColor:[[UIColor whiteColor] colorWithAlphaComponent:0.9f]]; + [self setMenuButtonHighlightedColor:[UIColor colorWithRed:139.0/255.0 + green:135.0/255.0 + blue:136.0/255.0 + alpha:0.9f]]; + + [self setShadowNormalColor:[[UIColor blackColor] colorWithAlphaComponent:0.5f]]; + [self setShadowHighlightedColor:[[UIColor blackColor] colorWithAlphaComponent:0.2f]]; + } + return self; +} + +-(UIColor *)menuButtonColorForState:(UIControlState)state{ + UIColor * color; + switch (state) { + case UIControlStateNormal: + color = self.menuButtonNormalColor; + break; + case UIControlStateHighlighted: + color = self.menuButtonHighlightedColor; + break; + default: + break; + } + return color; +} + +-(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state{ + switch (state) { + case UIControlStateNormal: + [self setMenuButtonNormalColor:color]; + break; + case UIControlStateHighlighted: + [self setMenuButtonHighlightedColor:color]; + break; + default: + break; + } + [self setNeedsDisplay]; +} + +-(UIColor *)shadowColorForState:(UIControlState)state{ + UIColor * color; + switch (state) { + case UIControlStateNormal: + color = self.shadowNormalColor; + break; + case UIControlStateHighlighted: + color = self.shadowHighlightedColor; + break; + default: + break; + } + return color; +} + +-(void)setShadowColor:(UIColor *)color forState:(UIControlState)state{ + switch (state) { + case UIControlStateNormal: + [self setShadowNormalColor:color]; + break; + case UIControlStateHighlighted: + [self setShadowHighlightedColor:color]; + break; + default: + break; + } + [self setNeedsDisplay]; +} + +-(void)drawRect:(CGRect)rect{ + //// General Declarations + CGContextRef context = UIGraphicsGetCurrentContext(); + + //Sizes + CGFloat buttonWidth = CGRectGetWidth(self.bounds)*.80; + CGFloat buttonHeight = CGRectGetHeight(self.bounds)*.16; + CGFloat xOffset = CGRectGetWidth(self.bounds)*.10; + CGFloat yOffset = CGRectGetHeight(self.bounds)*.12; + CGFloat cornerRadius = 1.0; + + //// Color Declarations + UIColor* buttonColor = [self menuButtonColorForState:self.state]; + UIColor* shadowColor = [self shadowColorForState:self.state]; + + + //// Shadow Declarations + UIColor* shadow = shadowColor; + CGSize shadowOffset = CGSizeMake(0.0, 1.0); + CGFloat shadowBlurRadius = 0; + + //// Top Bun Drawing + UIBezierPath* topBunPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); + [buttonColor setFill]; + [topBunPath fill]; + CGContextRestoreGState(context); + + //// Meat Drawing + UIBezierPath* meatPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset*2 + buttonHeight, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); + [buttonColor setFill]; + [meatPath fill]; + CGContextRestoreGState(context); + + //// Bottom Bun Drawing + UIBezierPath* bottomBunPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(xOffset, yOffset*3 + buttonHeight*2, buttonWidth, buttonHeight) cornerRadius:cornerRadius]; + CGContextSaveGState(context); + CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, shadow.CGColor); + [buttonColor setFill]; + [bottomBunPath fill]; + CGContextRestoreGState(context); +} + +-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ + [super touchesBegan:touches withEvent:event]; + [self setNeedsDisplay]; +} + +-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ + [super touchesEnded:touches withEvent:event]; + [self setNeedsDisplay]; +} + +-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{ + [super touchesCancelled:touches withEvent:event]; + [self setNeedsDisplay]; +} + +-(void)setSelected:(BOOL)selected{ + [super setSelected:selected]; + [self setNeedsDisplay]; +} + +-(void)setHighlighted:(BOOL)highlighted{ + [super setHighlighted:highlighted]; + [self setNeedsDisplay]; +} + +-(void)setTintColor:(UIColor *)tintColor{ + if([super respondsToSelector:@selector(setTintColor:)]){ + [super setTintColor:tintColor]; + } +} + +-(void)tintColorDidChange{ + [self setNeedsDisplay]; +} + +@end + +@interface MMDrawerBarButtonItem () +@property (nonatomic,strong) MMDrawerMenuButtonView * buttonView; + +@end + +@implementation MMDrawerBarButtonItem + ++(UIImage*)drawerButtonItemImage{ + + static UIImage *drawerButtonImage = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + + UIGraphicsBeginImageContextWithOptions( CGSizeMake(26, 26), NO, 0 ); + + //// Color Declarations + UIColor* fillColor = [UIColor whiteColor]; + + //// Frames + CGRect frame = CGRectMake(0, 0, 26, 26); + + //// Bottom Bar Drawing + UIBezierPath* bottomBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.72000 + 0.5), 16, 1)]; + [fillColor setFill]; + [bottomBarPath fill]; + + + //// Middle Bar Drawing + UIBezierPath* middleBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.48000 + 0.5), 16, 1)]; + [fillColor setFill]; + [middleBarPath fill]; + + + //// Top Bar Drawing + UIBezierPath* topBarPath = [UIBezierPath bezierPathWithRect: CGRectMake(CGRectGetMinX(frame) + floor((CGRectGetWidth(frame) - 16) * 0.50000 + 0.5), CGRectGetMinY(frame) + floor((CGRectGetHeight(frame) - 1) * 0.24000 + 0.5), 16, 1)]; + [fillColor setFill]; + [topBarPath fill]; + + drawerButtonImage = UIGraphicsGetImageFromCurrentImageContext(); + }); + + return drawerButtonImage; +} + +-(instancetype)initWithTarget:(id)target action:(SEL)action{ + + if((floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1)){ + return [self initWithImage:[self.class drawerButtonItemImage] + style:UIBarButtonItemStylePlain + target:target + action:action]; + } + else { + MMDrawerMenuButtonView * buttonView = [[MMDrawerMenuButtonView alloc] initWithFrame:CGRectMake(0, 0, 26, 26)]; + [buttonView addTarget:self action:@selector(touchUpInside:) forControlEvents:UIControlEventTouchUpInside]; + self = [self initWithCustomView:buttonView]; + if(self){ + [self setButtonView:buttonView]; + } + self.action = action; + self.target = target; + return self; + } +} + +-(instancetype)initWithCoder:(NSCoder *)aDecoder{ + // non-ideal way to get the target/action, but it works + UIBarButtonItem* barButtonItem = [[UIBarButtonItem alloc] initWithCoder: aDecoder]; + return [self initWithTarget:barButtonItem.target action:barButtonItem.action]; +} + +-(void)touchUpInside:(id)sender{ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [self.target performSelector:self.action withObject:sender]; +#pragma clang diagnostic pop; + +} + +-(UIColor *)menuButtonColorForState:(UIControlState)state{ + return [self.buttonView menuButtonColorForState:state]; +} + +-(void)setMenuButtonColor:(UIColor *)color forState:(UIControlState)state{ + [self.buttonView setMenuButtonColor:color forState:state]; +} + +-(UIColor *)shadowColorForState:(UIControlState)state{ + return [self.buttonView shadowColorForState:state]; +} + +-(void)setShadowColor:(UIColor *)color forState:(UIControlState)state{ + [self.buttonView setShadowColor:color forState:state]; +} + +-(void)setTintColor:(UIColor *)tintColor{ + if([super respondsToSelector:@selector(setTintColor:)]){ + [super setTintColor:tintColor]; + } + if([self.buttonView respondsToSelector:@selector(setTintColor:)]){ + [self.buttonView setTintColor:tintColor]; + } +} + +@end diff --git a/MMDrawerController/MMDrawerController+Subclass.h b/MMDrawerController/MMDrawerController+Subclass.h new file mode 100755 index 0000000..419ff68 --- /dev/null +++ b/MMDrawerController/MMDrawerController+Subclass.h @@ -0,0 +1,111 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "MMDrawerController.h" + +/** + This class extension is designed for use by subclasses of `MMDrawerController` to customize the functionality to support a specific use-case by a developer. When importing this file, there is no need to also call `#import MMDrawerController.h`. + + None of these methods are meant to be called by non-subclasses of `MMDrawerController`. + */ + +@interface MMDrawerController (Subclass) +///--------------------------------------- +/// @name Gesture Interaction +///--------------------------------------- +/** + `MMDrawerController`'s single-tap gesture recognizer callback. This method is called every time the `UITapGestureRecognizer` is triggered. + + @param tapGesture The single-tap gesture recognizer instance that triggered the callback + */ +-(void)tapGestureCallback:(UITapGestureRecognizer *)tapGesture __attribute((objc_requires_super)); + +/** + `MMDrawerController`'s pan gesture recognizer callback. This method is called every time the `UIPanGestureRecognizer` is updated. + + @warning This method do the minimal amount of work to keep the pan gesture responsive. + + @param panGesture The pan gesture recognizer instance that triggered the callback + */ +-(void)panGestureCallback:(UIPanGestureRecognizer *)panGesture __attribute((objc_requires_super)); + +/** + A `UIGestureRecognizerDelegate` method that is queried by `MMDrawerController`'s gestures to determine if it should receive the touch. + + @param gestureRecognizer The gesture recognizer that is asking if it should recieve a touch + @param touch The touch in question in gestureRecognizer.view's coordinate space + */ +-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch __attribute((objc_requires_super)); + +///--------------------------------------- +/// @name Drawer Presentation +///--------------------------------------- +/** + Sets the initial conditions for `MMDrawerController` and its child view controllers to prepare the drawer for a transition. If a drawer is open and the opposite drawer is being presented, it prepares that drawer to be hidden and vice-versa for the closing drawer. + + @param drawer The drawer side that will be presented + @param animated A boolean that indicates whether the presentation is being animated or not + */ +-(void)prepareToPresentDrawer:(MMDrawerSide)drawer animated:(BOOL)animated __attribute((objc_requires_super)); + +///--------------------------------------- +/// @name Opening/Closing Drawer +///--------------------------------------- +/** + The method that handles closing the drawer. You can subclass this method to get a callback every time the drawer is about to be closed. You can inspect the current open side to determine what side is about to be closed. + + @param animated A boolean that indicates whether the drawer should close with animation + @param velocity A float indicating how fast the drawer should close + @param animationOptions A mask defining the animation options of the animation + @param completion A completion block to be called when the drawer is finished closing + */ +-(void)closeDrawerAnimated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL))completion __attribute((objc_requires_super)); + +/** + The method that handles opening the drawer. You can subclass this method to get a callback every time the drawer is about to be opened. + + @param drawerSide The drawer side that will be opened + @param animated A boolean that indicates whether the drawer should open with animation + @param velocity A float indicating how fast the drawer should open + @param animationOptions A mask defining the animation options of the animation + @param completion A completion block to be called when the drawer is finished opening + */ +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL))completion __attribute((objc_requires_super)); + +///--------------------------------------- +/// @name `UIViewController` Subclass Methods +///--------------------------------------- +/** + Included here to ensure subclasses call `super`. + + @param toInterfaceOrientation The interface orientation that the interface is moving to + @param duration The duration of the interface orientation animation + */ +-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration __attribute((objc_requires_super)); + +/** + Included here to ensure subclasses call `super`. + + @param toInterfaceOrientation The interface orientation that the interface is moving to + @param duration The duration of the interface orientation animation + */ +-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration __attribute((objc_requires_super)); + +@end diff --git a/MMDrawerController/MMDrawerController.h b/MMDrawerController/MMDrawerController.h new file mode 100755 index 0000000..92833a6 --- /dev/null +++ b/MMDrawerController/MMDrawerController.h @@ -0,0 +1,455 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 + +/** + `MMDrawerController` is a side drawer navigation container view controller designed to support the growing number of applications that leverage the side drawer paradigm. This library is designed to exclusively support side drawer navigation in light-weight, focused approach. + + ## Creating a MMDrawerController + `MMDrawerController` is a container view controller, similiar to `UINavigationController` or `UITabBarController`, with up to three child view controllers - Center, LeftDrawer, and RightDrawer. To create a `MMDrawerController`, you must first instantiate the drawer view controllers and the initial center controller, then call one of the init methods listed in this class. + + ## Handling a UINavigationController as the centerViewController + `MMDrawerController` automatically supports handling a `UINavigationController` as the `centerViewController`, and will correctly handle the proper gestures on each view (the navigation bar view as well as the content view for the visible view controller). Note that while this library does support other container view controllers, the open/close gestures are not customized to support them. + + ## Accessing MMDrawerController from the Child View Controller + You can leverage the category class (UIViewController+MMDrawerController) included with this library to access information about the parent `MMDrawerController`. Note that if you are contained within a UINavigationController, the `drawerContainerViewController` will still return the proper reference to the `drawerContainerViewController` parent, even though it is not the direct parent. Refer to the documentation included with the category for more information. + + ## How MMDrawerOpenCenterInteractionMode is handled + `MMDrawerOpenCenterInteractionMode` controls how the user should be able to interact with the center view controller when either drawer is open. By default, this is set to `MMDrawerOpenCenterInteractionModeNavigationBarOnly`, which allows the user to interact with UINavigationBarItems while either drawer is open (typicaly used to click the menu button to close). If you set the interaction mode to `MMDrawerOpenCenterInteractionModeNone`, no items within the center view will be able to be interacted with while a drawer is open. Note that this setting has no effect at all on the `MMCloseDrawerGestureMode`. + + ## How Open/Close Gestures are handled + Two gestures are added to every instance of a drawer controller, one for pan and one for touch. `MMDrawerController` is the delegate for each of the gesture recoginzers, and determines if a touch should be sent to the appropriate gesture when a touch is detected compared with the masks set for open and close gestures and the state of the drawer controller. + + ## Integrating with State Restoration + In order to opt in to state restoration for `MMDrawerController`, you must set the `restorationIdentifier` of your drawer controller. Instances of your centerViewController, leftDrawerViewController and rightDrawerViewController must also be configured with their own `restorationIdentifier` (and optionally a restorationClass) if you intend for those to be restored as well. If your MMDrawerController had an open drawer when your app was sent to the background, that state will also be restored. + + ## What this library doesn't do. + This library is not meant for: + - Top or bottom drawer views + - Displaying both drawers at one time + - Displaying a minimum drawer width + - Support container view controllers other than `UINavigationController` as the center view controller. + */ + +typedef NS_ENUM(NSInteger,MMDrawerSide){ + MMDrawerSideNone = 0, + MMDrawerSideLeft, + MMDrawerSideRight, +}; + +typedef NS_OPTIONS(NSInteger, MMOpenDrawerGestureMode) { + MMOpenDrawerGestureModeNone = 0, + MMOpenDrawerGestureModePanningNavigationBar = 1 << 1, + MMOpenDrawerGestureModePanningCenterView = 1 << 2, + MMOpenDrawerGestureModeBezelPanningCenterView = 1 << 3, + MMOpenDrawerGestureModeCustom = 1 << 4, + MMOpenDrawerGestureModeAll = MMOpenDrawerGestureModePanningNavigationBar | + MMOpenDrawerGestureModePanningCenterView | + MMOpenDrawerGestureModeBezelPanningCenterView | + MMOpenDrawerGestureModeCustom, +}; + +typedef NS_OPTIONS(NSInteger, MMCloseDrawerGestureMode) { + MMCloseDrawerGestureModeNone = 0, + MMCloseDrawerGestureModePanningNavigationBar = 1 << 1, + MMCloseDrawerGestureModePanningCenterView = 1 << 2, + MMCloseDrawerGestureModeBezelPanningCenterView = 1 << 3, + MMCloseDrawerGestureModeTapNavigationBar = 1 << 4, + MMCloseDrawerGestureModeTapCenterView = 1 << 5, + MMCloseDrawerGestureModePanningDrawerView = 1 << 6, + MMCloseDrawerGestureModeCustom = 1 << 7, + MMCloseDrawerGestureModeAll = MMCloseDrawerGestureModePanningNavigationBar | + MMCloseDrawerGestureModePanningCenterView | + MMCloseDrawerGestureModeBezelPanningCenterView | + MMCloseDrawerGestureModeTapNavigationBar | + MMCloseDrawerGestureModeTapCenterView | + MMCloseDrawerGestureModePanningDrawerView | + MMCloseDrawerGestureModeCustom, +}; + +typedef NS_ENUM(NSInteger, MMDrawerOpenCenterInteractionMode) { + MMDrawerOpenCenterInteractionModeNone, + MMDrawerOpenCenterInteractionModeFull, + MMDrawerOpenCenterInteractionModeNavigationBarOnly, +}; + +@class MMDrawerController; +typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible); + +@interface MMDrawerController : UIViewController + +///--------------------------------------- +/// @name Accessing Drawer Container View Controller Properties +///--------------------------------------- + +/** + The center view controller. + + This can only be set via the init methods, as well as the `setNewCenterViewController:...` methods. The size of this view controller will automatically be set to the size of the drawer container view controller, and it's position is modified from within this class. Do not modify the frame externally. + */ +@property (nonatomic, strong) UIViewController * centerViewController; + +/** + The left drawer view controller. + + The size of this view controller is managed within this class, and is automatically set to the appropriate size based on the `maximumLeftDrawerWidth`. Do not modify the frame externally. + */ +@property (nonatomic, strong) UIViewController * leftDrawerViewController; + +/** + The right drawer view controller. + + The size of this view controller is managed within this class, and is automatically set to the appropriate size based on the `maximumRightDrawerWidth`. Do not modify the frame externally. + */ +@property (nonatomic, strong) UIViewController * rightDrawerViewController; + +/** + The maximum width of the `leftDrawerViewController`. + + By default, this is set to 280. If the `leftDrawerViewController` is nil, this property will return 0.0; + */ +@property (nonatomic, assign) CGFloat maximumLeftDrawerWidth; + +/** + The maximum width of the `rightDrawerViewController`. + + By default, this is set to 280. If the `rightDrawerViewController` is nil, this property will return 0.0; + + */ +@property (nonatomic, assign) CGFloat maximumRightDrawerWidth; + +/** + The visible width of the `leftDrawerViewController`. + + Note this value can be greater than `maximumLeftDrawerWidth` during the full close animation when setting a new center view controller; + */ +@property (nonatomic, assign, readonly) CGFloat visibleLeftDrawerWidth; + +/** + The visible width of the `rightDrawerViewController`. + + Note this value can be greater than `maximumRightDrawerWidth` during the full close animation when setting a new center view controller; + */ +@property (nonatomic, assign, readonly) CGFloat visibleRightDrawerWidth; + +/** + The animation velocity of the open and close methods, measured in points per second. + + By default, this is set to 840 points per second (three times the default drawer width), meaning it takes 1/3 of a second for the `centerViewController` to open/close across the default drawer width. Note that there is a minimum .1 second duration for built in animations, to account for small distance animations. + */ +@property (nonatomic, assign) CGFloat animationVelocity; + +/** + A boolean that determines whether or not the panning gesture will "hard-stop" at the maximum width for a given drawer side. + + By default, this value is set to YES. Enabling `shouldStretchDrawer` will give the pan a gradual asymptotic stopping point much like `UIScrollView` behaves. Note that if this value is set to YES, the `drawerVisualStateBlock` can be passed a `percentVisible` greater than 1.0, so be sure to handle that case appropriately. + */ +@property (nonatomic, assign) BOOL shouldStretchDrawer; + +/** + The current open side of the drawer. + + Note this value will change as soon as a pan gesture opens a drawer, or when a open/close animation is finished. + */ +@property (nonatomic, assign, readonly) MMDrawerSide openSide; + +/** + How a user is allowed to open a drawer using gestures. + + By default, this is set to `MMOpenDrawerGestureModeNone`. Note these gestures may affect user interaction with the `centerViewController`, so be sure to use appropriately. + */ +@property (nonatomic, assign) MMOpenDrawerGestureMode openDrawerGestureModeMask; + +/** + How a user is allowed to close a drawer. + + By default, this is set to `MMCloseDrawerGestureModeNone`. Note these gestures may affect user interaction with the `centerViewController`, so be sure to use appropriately. + */ +@property (nonatomic, assign) MMCloseDrawerGestureMode closeDrawerGestureModeMask; + +/** + The value determining if the user can interact with the `centerViewController` when a side drawer is open. + + By default, it is `MMDrawerOpenCenterInteractionModeNavigationBarOnly`, meaning that the user can only interact with the buttons on the `UINavigationBar`, if the center view controller is a `UINavigationController`. Otherwise, the user cannot interact with any other center view controller elements. + */ +@property (nonatomic, assign) MMDrawerOpenCenterInteractionMode centerHiddenInteractionMode; + +/** + The flag determining if a shadow should be drawn off of `centerViewController` when a drawer is open. + + By default, this is set to YES. + */ +@property (nonatomic, assign) BOOL showsShadow; + +/** + The shadow radius of `centerViewController` when a drawer is open. + + By default, this is set to 10.0f; + */ +@property (nonatomic, assign) CGFloat shadowRadius; + +/** + The shadow opacity of `centerViewController` when a drawer is open. + + By default, this is set to 0.8f; + */ +@property (nonatomic, assign) CGFloat shadowOpacity; + +/** + The shadow offset of `centerViewController` when a drawer is open. + + By default, this is set to (0, -3); + */ +@property (nonatomic, assign) CGSize shadowOffset; + +/** + The color of the shadow drawn off of 'centerViewController` when a drawer is open. + + By default, this is set to the systme default (opaque black). + */ +@property (nonatomic, strong) UIColor * shadowColor; + +/** + The flag determining if a custom background view should appear beneath the status bar, forcing the child content to be drawn lower than the status bar. + + By default, this is set to NO. + */ +@property (nonatomic, assign) BOOL showsStatusBarBackgroundView; + +/** + The color of the status bar background view if `showsStatusBarBackgroundView` is set to YES. + + By default, this is set `[UIColor blackColor]`. + */ +@property (nonatomic, strong) UIColor * statusBarViewBackgroundColor; + +/** + The value determining panning range of centerView's bezel if the user can open drawer with 'MMOpenDrawerGestureModeBezelPanningCenterView' or close drawer with 'MMCloseDrawerGestureModeBezelPanningCenterView' . + + By default, this is set 20.0f. + */ +@property (nonatomic, assign) CGFloat bezelPanningCenterViewRange; + +/** + The value determining if the user can open or close drawer with panGesture velocity. + + By default, this is set 200.0f. + */ +@property (nonatomic, assign) CGFloat panVelocityXAnimationThreshold; + +///--------------------------------------- +/// @name Initializing a `MMDrawerController` +///--------------------------------------- + +/** + Creates and initializes an `MMDrawerController` object with the specified center view controller, left drawer view controller, and right drawer view controller. + + @param centerViewController The center view controller. This argument must not be `nil`. + @param leftDrawerViewController The left drawer view controller. + @param rightDrawerViewController The right drawer controller. + + @return The newly-initialized drawer container view controller. + */ +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController; + +/** + Creates and initializes an `MMDrawerController` object with the specified center view controller, left drawer view controller. + + @param centerViewController The center view controller. This argument must not be `nil`. + @param leftDrawerViewController The left drawer view controller. + + @return The newly-initialized drawer container view controller. + */ +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController; + +/** + Creates and initializes an `MMDrawerController` object with the specified center view controller, right drawer view controller. + + @param centerViewController The center view controller. This argument must not be `nil`. + @param rightDrawerViewController The right drawer controller. + + @return The newly-initialized drawer container view controller. + */ +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController; + +///--------------------------------------- +/// @name Opening and Closing a Drawer +///--------------------------------------- + +/** + Toggles the drawer open/closed based on the `drawer` passed in. + + Note that if you attempt to toggle a drawer closed while the other is open, nothing will happen. For example, if you pass in MMDrawerSideLeft, but the right drawer is open, nothing will happen. In addition, the completion block will be called with the finished flag set to NO. + + @param drawerSide The `MMDrawerSide` to toggle. This value cannot be `MMDrawerSideNone`. + @param animated Determines whether the `drawer` should be toggle animated. + @param completion The block that is called when the toggle is complete, or if no toggle took place at all. + + */ +-(void)toggleDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +/** + Closes the open drawer. + + @param animated Determines whether the drawer side should be closed animated + @param completion The block that is called when the close is complete + + */ +-(void)closeDrawerAnimated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +/** + Opens the `drawer` passed in. + + @param drawerSide The `MMDrawerSide` to open. This value cannot be `MMDrawerSideNone`. + @param animated Determines whether the `drawer` should be open animated. + @param completion The block that is called when the toggle is open. + + */ +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +///--------------------------------------- +/// @name Setting a new Center View Controller +///--------------------------------------- + +/** + Sets the new `centerViewController`. + + This sets the view controller and will automatically adjust the frame based on the current state of the drawer controller. If `closeAnimated` is YES, it will immediately change the center view controller, and close the drawer from its current position. + + @param centerViewController The new `centerViewController`. + @param closeAnimated Determines whether the drawer should be closed with an animation. + @param completion The block called when the animation is finsihed. + + */ +-(void)setCenterViewController:(UIViewController *)centerViewController withCloseAnimation:(BOOL)closeAnimated completion:(void(^)(BOOL finished))completion; + +/** + Sets the new `centerViewController`. + + This sets the view controller and will automatically adjust the frame based on the current state of the drawer controller. If `closeFullAnimated` is YES, the current center view controller will animate off the screen, the new center view controller will then be set, followed by the drawer closing across the full width of the screen. + + @param newCenterViewController The new `centerViewController`. + @param fullCloseAnimated Determines whether the drawer should be closed with an animation. + @param completion The block called when the animation is finsihed. + + */ +-(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)fullCloseAnimated completion:(void(^)(BOOL finished))completion; + +///--------------------------------------- +/// @name Animating the Width of a Drawer +///--------------------------------------- + +/** + Sets the maximum width of the left drawer view controller. + + If the drawer is open, and `animated` is YES, it will animate the drawer frame as well as adjust the center view controller. If the drawer is not open, this change will take place immediately. + + @param width The new width of left drawer view controller. This must be greater than zero. + @param animated Determines whether the drawer should be adjusted with an animation. + @param completion The block called when the animation is finished. + + */ +-(void)setMaximumLeftDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +/** + Sets the maximum width of the right drawer view controller. + + If the drawer is open, and `animated` is YES, it will animate the drawer frame as well as adjust the center view controller. If the drawer is not open, this change will take place immediately. + + @param width The new width of right drawer view controller. This must be greater than zero. + @param animated Determines whether the drawer should be adjusted with an animation. + @param completion The block called when the animation is finished. + + */ +-(void)setMaximumRightDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion; + +///--------------------------------------- +/// @name Previewing a Drawer +///--------------------------------------- + +/** + Bounce preview for the specified `drawerSide` a distance of 40 points. + + @param drawerSide The drawer to preview. This value cannot be `MMDrawerSideNone`. + @param completion The block called when the animation is finsihed. + + */ +-(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide completion:(void(^)(BOOL finished))completion; + +/** + Bounce preview for the specified `drawerSide`. + + @param drawerSide The drawer side to preview. This value cannot be `MMDrawerSideNone`. + @param distance The distance to bounce. + @param completion The block called when the animation is finsihed. + + */ +-(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide distance:(CGFloat)distance completion:(void(^)(BOOL finished))completion; + +///--------------------------------------- +/// @name Custom Drawer Animations +///--------------------------------------- + +/** + Sets a callback to be called when a drawer visual state needs to be updated. + + This block is responsible for updating the drawer's view state, and the drawer controller will handle animating to that state from the current state. This block will be called when the drawer is opened or closed, as well when the user is panning the drawer. This block is not responsible for doing animations directly, but instead just updating the state of the properies (such as alpha, anchor point, transform, etc). Note that if `shouldStretchDrawer` is set to YES, it is possible for `percentVisible` to be greater than 1.0. If `shouldStretchDrawer` is set to NO, `percentVisible` will never be greater than 1.0. + + Note that when the drawer is finished opening or closing, the side drawer controller view will be reset with the following properies: + + - alpha: 1.0 + - transform: CATransform3DIdentity + - anchorPoint: (0.5,0.5) + + @param drawerVisualStateBlock A block object to be called that allows the implementer to update visual state properties on the drawer. `percentVisible` represents the amount of the drawer space that is current visible, with drawer space being defined as the edge of the screen to the maxmimum drawer width. Note that you do have access to the drawerController, which will allow you to update things like the anchor point of the side drawer layer. + */ +-(void)setDrawerVisualStateBlock:(void(^)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible))drawerVisualStateBlock; + +///--------------------------------------- +/// @name Gesture Completion Handling +///--------------------------------------- + +/** + Sets a callback to be called when a gesture has been completed. + + This block is called when a gesture action has been completed. You can query the `openSide` of the `drawerController` to determine what the new state of the drawer is. + + @param gestureCompletionBlock A block object to be called that allows the implementer be notified when a gesture action has been completed. + */ +-(void)setGestureCompletionBlock:(void(^)(MMDrawerController * drawerController, UIGestureRecognizer * gesture))gestureCompletionBlock; + +///--------------------------------------- +/// @name Custom Gesture Handler +///--------------------------------------- + +/** + Sets a callback to be called to determine if a UIGestureRecognizer should recieve the given UITouch. + + This block provides a way to allow a gesture to be recognized with custom logic. For example, you may have a certain part of your view that should accept a pan gesture recognizer to open the drawer, but not another a part. If you return YES, the gesture is recognized and the appropriate action is taken. This provides similar support to how Facebook allows you to pan on the background view of the main table view, but not the content itself. You can inspect the `openSide` property of the `drawerController` to determine the current state of the drawer, and apply the appropriate logic within your block. + + Note that either `openDrawerGestureModeMask` must contain `MMOpenDrawerGestureModeCustom`, or `closeDrawerGestureModeMask` must contain `MMCloseDrawerGestureModeCustom` for this block to be consulted. + + @param gestureShouldRecognizeTouchBlock A block object to be called to determine if the given `touch` should be recognized by the given gesture. + */ +-(void)setGestureShouldRecognizeTouchBlock:(BOOL(^)(MMDrawerController * drawerController, UIGestureRecognizer * gesture, UITouch * touch))gestureShouldRecognizeTouchBlock; + +@end diff --git a/MMDrawerController/MMDrawerController.m b/MMDrawerController/MMDrawerController.m new file mode 100755 index 0000000..4aec238 --- /dev/null +++ b/MMDrawerController/MMDrawerController.m @@ -0,0 +1,1479 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "MMDrawerController.h" +#import "UIViewController+MMDrawerController.h" + +#import + +CGFloat const MMDrawerDefaultWidth = 280.0f; +CGFloat const MMDrawerDefaultAnimationVelocity = 840.0f; + +NSTimeInterval const MMDrawerDefaultFullAnimationDelay = 0.10f; + +CGFloat const MMDrawerDefaultBounceDistance = 50.0f; + +NSTimeInterval const MMDrawerDefaultBounceAnimationDuration = 0.2f; +CGFloat const MMDrawerDefaultSecondBounceDistancePercentage = .25f; + +CGFloat const MMDrawerDefaultShadowRadius = 10.0f; +CGFloat const MMDrawerDefaultShadowOpacity = 0.8; + +NSTimeInterval const MMDrawerMinimumAnimationDuration = 0.15f; + +CGFloat const MMDrawerBezelRange = 20.0f; + +CGFloat const MMDrawerPanVelocityXAnimationThreshold = 200.0f; + +/** The amount of overshoot that is panned linearly. The remaining percentage nonlinearly asymptotes to the max percentage. */ +CGFloat const MMDrawerOvershootLinearRangePercentage = 0.75f; + +/** The percent of the possible overshoot width to use as the actual overshoot percentage. */ +CGFloat const MMDrawerOvershootPercentage = 0.1f; + +typedef BOOL (^MMDrawerGestureShouldRecognizeTouchBlock)(MMDrawerController * drawerController, UIGestureRecognizer * gesture, UITouch * touch); +typedef void (^MMDrawerGestureCompletionBlock)(MMDrawerController * drawerController, UIGestureRecognizer * gesture); + +static CAKeyframeAnimation * bounceKeyFrameAnimationForDistanceOnView(CGFloat distance, UIView * view) { + CGFloat factors[32] = {0, 32, 60, 83, 100, 114, 124, 128, 128, 124, 114, 100, 83, 60, 32, + 0, 24, 42, 54, 62, 64, 62, 54, 42, 24, 0, 18, 28, 32, 28, 18, 0}; + + NSMutableArray *values = [NSMutableArray array]; + + for (int i=0; i<32; i++) + { + CGFloat positionOffset = factors[i]/128.0f * distance + CGRectGetMidX(view.bounds); + [values addObject:@(positionOffset)]; + } + + CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position.x"]; + animation.repeatCount = 1; + animation.duration = .8; + animation.fillMode = kCAFillModeForwards; + animation.values = values; + animation.removedOnCompletion = YES; + animation.autoreverses = NO; + + return animation; +} + +static NSString *MMDrawerLeftDrawerKey = @"MMDrawerLeftDrawer"; +static NSString *MMDrawerRightDrawerKey = @"MMDrawerRightDrawer"; +static NSString *MMDrawerCenterKey = @"MMDrawerCenter"; +static NSString *MMDrawerOpenSideKey = @"MMDrawerOpenSide"; + +@interface MMDrawerCenterContainerView : UIView +@property (nonatomic,assign) MMDrawerOpenCenterInteractionMode centerInteractionMode; +@property (nonatomic,assign) MMDrawerSide openSide; +@end + +@implementation MMDrawerCenterContainerView + +-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ + UIView *hitView = [super hitTest:point withEvent:event]; + if(hitView && + self.openSide != MMDrawerSideNone){ + UINavigationBar * navBar = [self navigationBarContainedWithinSubviewsOfView:self]; + CGRect navBarFrame = [navBar convertRect:navBar.bounds toView:self]; + if((self.centerInteractionMode == MMDrawerOpenCenterInteractionModeNavigationBarOnly && + CGRectContainsPoint(navBarFrame, point) == NO) || + self.centerInteractionMode == MMDrawerOpenCenterInteractionModeNone){ + hitView = nil; + } + } + return hitView; +} + +-(UINavigationBar*)navigationBarContainedWithinSubviewsOfView:(UIView*)view{ + UINavigationBar * navBar = nil; + for(UIView * subview in [view subviews]){ + if([view isKindOfClass:[UINavigationBar class]]){ + navBar = (UINavigationBar*)view; + break; + } + else { + navBar = [self navigationBarContainedWithinSubviewsOfView:subview]; + if (navBar != nil) { + break; + } + } + } + return navBar; +} +@end + +@interface MMDrawerController () { + CGFloat _maximumRightDrawerWidth; + CGFloat _maximumLeftDrawerWidth; + UIColor * _statusBarViewBackgroundColor; +} + +@property (nonatomic, assign, readwrite) MMDrawerSide openSide; + +@property (nonatomic, strong) UIView * childControllerContainerView; +@property (nonatomic, strong) MMDrawerCenterContainerView * centerContainerView; +@property (nonatomic, strong) UIView * dummyStatusBarView; + +@property (nonatomic, assign) CGRect startingPanRect; +@property (nonatomic, copy) MMDrawerControllerDrawerVisualStateBlock drawerVisualState; +@property (nonatomic, copy) MMDrawerGestureShouldRecognizeTouchBlock gestureShouldRecognizeTouch; +@property (nonatomic, copy) MMDrawerGestureCompletionBlock gestureCompletion; +@property (nonatomic, assign, getter = isAnimatingDrawer) BOOL animatingDrawer; + +@end + +@implementation MMDrawerController + +#pragma mark - Init + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{ + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + [self commonSetup]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder{ + self = [super initWithCoder:aDecoder]; + if (self) { + [self commonSetup]; + } + return self; +} + +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController{ + NSParameterAssert(centerViewController); + self = [super init]; + if(self){ + [self setCenterViewController:centerViewController]; + [self setLeftDrawerViewController:leftDrawerViewController]; + [self setRightDrawerViewController:rightDrawerViewController]; + } + return self; +} + +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController{ + return [self initWithCenterViewController:centerViewController leftDrawerViewController:leftDrawerViewController rightDrawerViewController:nil]; +} + +-(instancetype)initWithCenterViewController:(UIViewController *)centerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController{ + return [self initWithCenterViewController:centerViewController leftDrawerViewController:nil rightDrawerViewController:rightDrawerViewController]; +} + +-(void)commonSetup{ + [self setMaximumLeftDrawerWidth:MMDrawerDefaultWidth]; + [self setMaximumRightDrawerWidth:MMDrawerDefaultWidth]; + + [self setAnimationVelocity:MMDrawerDefaultAnimationVelocity]; + + [self setShowsShadow:YES]; + [self setShouldStretchDrawer:YES]; + + [self setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeNone]; + [self setCloseDrawerGestureModeMask:MMCloseDrawerGestureModeNone]; + [self setCenterHiddenInteractionMode:MMDrawerOpenCenterInteractionModeNavigationBarOnly]; + + // set shadow related default values + [self setShadowOpacity:MMDrawerDefaultShadowOpacity]; + [self setShadowRadius:MMDrawerDefaultShadowRadius]; + [self setShadowOffset:CGSizeMake(0, -3)]; + [self setShadowColor:[UIColor blackColor]]; + + // set default bezel range for panGestureReconizer + [self setBezelPanningCenterViewRange:MMDrawerBezelRange]; + + // set defualt panVelocityXAnimationThreshold + [self setPanVelocityXAnimationThreshold:MMDrawerPanVelocityXAnimationThreshold]; +} + +#pragma mark - State Restoration +- (void)encodeRestorableStateWithCoder:(NSCoder *)coder{ + [super encodeRestorableStateWithCoder:coder]; + if (self.leftDrawerViewController){ + [coder encodeObject:self.leftDrawerViewController forKey:MMDrawerLeftDrawerKey]; + } + + if (self.rightDrawerViewController){ + [coder encodeObject:self.rightDrawerViewController forKey:MMDrawerRightDrawerKey]; + } + + if (self.centerViewController){ + [coder encodeObject:self.centerViewController forKey:MMDrawerCenterKey]; + } + + [coder encodeInteger:self.openSide forKey:MMDrawerOpenSideKey]; +} + +- (void)decodeRestorableStateWithCoder:(NSCoder *)coder{ + UIViewController *controller; + MMDrawerSide openside; + + [super decodeRestorableStateWithCoder:coder]; + + if ((controller = [coder decodeObjectForKey:MMDrawerLeftDrawerKey])){ + self.leftDrawerViewController = controller; + } + + if ((controller = [coder decodeObjectForKey:MMDrawerRightDrawerKey])){ + self.rightDrawerViewController = controller; + } + + if ((controller = [coder decodeObjectForKey:MMDrawerCenterKey])){ + self.centerViewController = controller; + } + + if ((openside = [coder decodeIntegerForKey:MMDrawerOpenSideKey])){ + [self openDrawerSide:openside animated:false completion:nil]; + } +} +#pragma mark - Open/Close methods +-(void)toggleDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ + NSParameterAssert(drawerSide!=MMDrawerSideNone); + if(self.openSide == MMDrawerSideNone){ + [self openDrawerSide:drawerSide animated:animated completion:completion]; + } + else { + if((drawerSide == MMDrawerSideLeft && + self.openSide == MMDrawerSideLeft) || + (drawerSide == MMDrawerSideRight && + self.openSide == MMDrawerSideRight)){ + [self closeDrawerAnimated:animated completion:completion]; + } + else if(completion){ + completion(NO); + } + } +} + +-(void)closeDrawerAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ + [self closeDrawerAnimated:animated velocity:self.animationVelocity animationOptions:UIViewAnimationOptionCurveEaseInOut completion:completion]; +} + +-(void)closeDrawerAnimated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion{ + if(self.isAnimatingDrawer){ + if(completion){ + completion(NO); + } + } + else { + [self setAnimatingDrawer:animated]; + CGRect newFrame = self.childControllerContainerView.bounds; + + CGFloat distance = ABS(CGRectGetMinX(self.centerContainerView.frame)); + NSTimeInterval duration = MAX(distance/ABS(velocity),MMDrawerMinimumAnimationDuration); + + BOOL leftDrawerVisible = CGRectGetMinX(self.centerContainerView.frame) > 0; + BOOL rightDrawerVisible = CGRectGetMinX(self.centerContainerView.frame) < 0; + + MMDrawerSide visibleSide = MMDrawerSideNone; + CGFloat percentVisble = 0.0; + + if(leftDrawerVisible){ + CGFloat visibleDrawerPoints = CGRectGetMinX(self.centerContainerView.frame); + percentVisble = MAX(0.0,visibleDrawerPoints/self.maximumLeftDrawerWidth); + visibleSide = MMDrawerSideLeft; + } + else if(rightDrawerVisible){ + CGFloat visibleDrawerPoints = CGRectGetWidth(self.centerContainerView.frame)-CGRectGetMaxX(self.centerContainerView.frame); + percentVisble = MAX(0.0,visibleDrawerPoints/self.maximumRightDrawerWidth); + visibleSide = MMDrawerSideRight; + } + + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:visibleSide]; + + [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:percentVisble]; + + [sideDrawerViewController beginAppearanceTransition:NO animated:animated]; + + [UIView + animateWithDuration:(animated?duration:0.0) + delay:0.0 + options:options + animations:^{ + [self setNeedsStatusBarAppearanceUpdateIfSupported]; + [self.centerContainerView setFrame:newFrame]; + [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:0.0]; + } + completion:^(BOOL finished) { + [sideDrawerViewController endAppearanceTransition]; + [self setOpenSide:MMDrawerSideNone]; + [self resetDrawerVisualStateForDrawerSide:visibleSide]; + [self setAnimatingDrawer:NO]; + if(completion){ + completion(finished); + } + }]; + } +} + +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ + NSParameterAssert(drawerSide != MMDrawerSideNone); + + [self openDrawerSide:drawerSide animated:animated velocity:self.animationVelocity animationOptions:UIViewAnimationOptionCurveEaseInOut completion:completion]; +} + +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion{ + NSParameterAssert(drawerSide != MMDrawerSideNone); + if (self.isAnimatingDrawer) { + if(completion){ + completion(NO); + } + } + else { + [self setAnimatingDrawer:animated]; + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + if (self.openSide != drawerSide) { + [self prepareToPresentDrawer:drawerSide animated:animated]; + } + + if(sideDrawerViewController){ + CGRect newFrame; + CGRect oldFrame = self.centerContainerView.frame; + if(drawerSide == MMDrawerSideLeft){ + newFrame = self.centerContainerView.frame; + newFrame.origin.x = self.maximumLeftDrawerWidth; + } + else { + newFrame = self.centerContainerView.frame; + newFrame.origin.x = 0-self.maximumRightDrawerWidth; + } + + CGFloat distance = ABS(CGRectGetMinX(oldFrame)-newFrame.origin.x); + NSTimeInterval duration = MAX(distance/ABS(velocity),MMDrawerMinimumAnimationDuration); + + [UIView + animateWithDuration:(animated?duration:0.0) + delay:0.0 + options:options + animations:^{ + [self setNeedsStatusBarAppearanceUpdateIfSupported]; + [self.centerContainerView setFrame:newFrame]; + [self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0]; + } + completion:^(BOOL finished) { + //End the appearance transition if it already wasn't open. + if(drawerSide != self.openSide){ + [sideDrawerViewController endAppearanceTransition]; + } + [self setOpenSide:drawerSide]; + + [self resetDrawerVisualStateForDrawerSide:drawerSide]; + [self setAnimatingDrawer:NO]; + if(completion){ + completion(finished); + } + }]; + } + } +} + +#pragma mark - Updating the Center View Controller +//If animated is NO, then we need to handle all the appearance calls within this method. Otherwise, +//let the method calling this one handle proper appearance methods since they will have more context +-(void)setCenterViewController:(UIViewController *)centerViewController animated:(BOOL)animated{ + if ([self.centerViewController isEqual:centerViewController]) { + return; + } + + if (_centerContainerView == nil) { + //This is related to Issue #152 (https://github.com/mutualmobile/MMDrawerController/issues/152) + // also fixed below in the getter for `childControllerContainerView`. Turns out we have + // two center container views getting added to the view during init, + // because the first request self.centerContainerView.bounds was kicking off a + // viewDidLoad, which caused us to be able to fall through this check twice. + // + //The fix is to grab the bounds, and then check again that the child container view has + //not been created. + + CGRect centerFrame = self.childControllerContainerView.bounds; + if(_centerContainerView == nil){ + _centerContainerView = [[MMDrawerCenterContainerView alloc] initWithFrame:centerFrame]; + [self.centerContainerView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; + [self.centerContainerView setBackgroundColor:[UIColor clearColor]]; + [self.centerContainerView setOpenSide:self.openSide]; + [self.centerContainerView setCenterInteractionMode:self.centerHiddenInteractionMode]; + [self.childControllerContainerView addSubview:self.centerContainerView]; + } + } + + UIViewController * oldCenterViewController = self.centerViewController; + if(oldCenterViewController){ + [oldCenterViewController willMoveToParentViewController:nil]; + if(animated == NO){ + [oldCenterViewController beginAppearanceTransition:NO animated:NO]; + } + [oldCenterViewController.view removeFromSuperview]; + if(animated == NO){ + [oldCenterViewController endAppearanceTransition]; + } + [oldCenterViewController removeFromParentViewController]; + } + + _centerViewController = centerViewController; + + [self addChildViewController:self.centerViewController]; + [self.centerViewController.view setFrame:self.childControllerContainerView.bounds]; + [self.centerContainerView addSubview:self.centerViewController.view]; + [self.childControllerContainerView bringSubviewToFront:self.centerContainerView]; + [self.centerViewController.view setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; + [self updateShadowForCenterView]; + + if(animated == NO){ + // If drawer is offscreen, then viewWillAppear: will take care of this + if(self.view.window) { + [self.centerViewController beginAppearanceTransition:YES animated:NO]; + [self.centerViewController endAppearanceTransition]; + } + [self.centerViewController didMoveToParentViewController:self]; + } +} + +-(void)setCenterViewController:(UIViewController *)newCenterViewController withCloseAnimation:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + + if(self.openSide == MMDrawerSideNone){ + //If a side drawer isn't open, there is nothing to animate... + animated = NO; + } + + BOOL forwardAppearanceMethodsToCenterViewController = ([self.centerViewController isEqual:newCenterViewController] == NO); + + UIViewController * oldCenterViewController = self.centerViewController; + // This is related to issue 363 (https://github.com/novkostya/MMDrawerController/pull/363) + // This needs to be refactored so the appearance logic is easier + // to follow across the multiple close/setter methods + if (animated && forwardAppearanceMethodsToCenterViewController) { + [oldCenterViewController beginAppearanceTransition:NO animated:NO]; + } + + [self setCenterViewController:newCenterViewController animated:animated]; + + // Related to note above. + if (animated && forwardAppearanceMethodsToCenterViewController) { + [oldCenterViewController endAppearanceTransition]; + } + + if(animated){ + [self updateDrawerVisualStateForDrawerSide:self.openSide percentVisible:1.0]; + if (forwardAppearanceMethodsToCenterViewController) { + [self.centerViewController beginAppearanceTransition:YES animated:animated]; + } + [self + closeDrawerAnimated:animated + completion:^(BOOL finished) { + if (forwardAppearanceMethodsToCenterViewController) { + [self.centerViewController endAppearanceTransition]; + [self.centerViewController didMoveToParentViewController:self]; + } + if(completion){ + completion(finished); + } + }]; + } + else { + if(completion) { + completion(YES); + } + } +} + +-(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + if(self.openSide != MMDrawerSideNone && + animated){ + + BOOL forwardAppearanceMethodsToCenterViewController = ([self.centerViewController isEqual:newCenterViewController] == NO); + + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:self.openSide]; + + CGFloat targetClosePoint = 0.0f; + if(self.openSide == MMDrawerSideRight){ + targetClosePoint = -CGRectGetWidth(self.childControllerContainerView.bounds); + } + else if(self.openSide == MMDrawerSideLeft) { + targetClosePoint = CGRectGetWidth(self.childControllerContainerView.bounds); + } + + CGFloat distance = ABS(self.centerContainerView.frame.origin.x-targetClosePoint); + NSTimeInterval firstDuration = [self animationDurationForAnimationDistance:distance]; + + CGRect newCenterRect = self.centerContainerView.frame; + + [self setAnimatingDrawer:animated]; + + UIViewController * oldCenterViewController = self.centerViewController; + if(forwardAppearanceMethodsToCenterViewController ){ + [oldCenterViewController beginAppearanceTransition:NO animated:animated]; + } + newCenterRect.origin.x = targetClosePoint; + [UIView + animateWithDuration:firstDuration + delay:0.0 + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.centerContainerView setFrame:newCenterRect]; + [sideDrawerViewController.view setFrame:self.childControllerContainerView.bounds]; + } + completion:^(BOOL finished) { + + CGRect oldCenterRect = self.centerContainerView.frame; + [self setCenterViewController:newCenterViewController animated:animated]; + [self.centerContainerView setFrame:oldCenterRect]; + [self updateDrawerVisualStateForDrawerSide:self.openSide percentVisible:1.0]; + if(forwardAppearanceMethodsToCenterViewController) { + [oldCenterViewController endAppearanceTransition]; + [self.centerViewController beginAppearanceTransition:YES animated:animated]; + } + [sideDrawerViewController beginAppearanceTransition:NO animated:animated]; + [UIView + animateWithDuration:[self animationDurationForAnimationDistance:CGRectGetWidth(self.childControllerContainerView.bounds)] + delay:MMDrawerDefaultFullAnimationDelay + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.centerContainerView setFrame:self.childControllerContainerView.bounds]; + [self updateDrawerVisualStateForDrawerSide:self.openSide percentVisible:0.0]; + } + completion:^(BOOL finished) { + if (forwardAppearanceMethodsToCenterViewController) { + [self.centerViewController endAppearanceTransition]; + [self.centerViewController didMoveToParentViewController:self]; + } + [sideDrawerViewController endAppearanceTransition]; + [self resetDrawerVisualStateForDrawerSide:self.openSide]; + + [sideDrawerViewController.view setFrame:sideDrawerViewController.mm_visibleDrawerFrame]; + + [self setOpenSide:MMDrawerSideNone]; + [self setAnimatingDrawer:NO]; + if(completion){ + completion(finished); + } + }]; + }]; + } + else { + [self setCenterViewController:newCenterViewController animated:animated]; + if(self.openSide != MMDrawerSideNone){ + [self closeDrawerAnimated:animated completion:completion]; + } + else if(completion){ + completion(YES); + } + } +} + +#pragma mark - Size Methods +-(void)setMaximumLeftDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + [self setMaximumDrawerWidth:width forSide:MMDrawerSideLeft animated:animated completion:completion]; +} + +-(void)setMaximumRightDrawerWidth:(CGFloat)width animated:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + [self setMaximumDrawerWidth:width forSide:MMDrawerSideRight animated:animated completion:completion]; +} + +- (void)setMaximumDrawerWidth:(CGFloat)width forSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + NSParameterAssert(width > 0); + NSParameterAssert(drawerSide != MMDrawerSideNone); + + UIViewController *sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + CGFloat oldWidth = 0.f; + NSInteger drawerSideOriginCorrection = 1; + if (drawerSide == MMDrawerSideLeft) { + oldWidth = _maximumLeftDrawerWidth; + _maximumLeftDrawerWidth = width; + } + else if(drawerSide == MMDrawerSideRight){ + oldWidth = _maximumRightDrawerWidth; + _maximumRightDrawerWidth = width; + drawerSideOriginCorrection = -1; + } + + CGFloat distance = ABS(width-oldWidth); + NSTimeInterval duration = [self animationDurationForAnimationDistance:distance]; + + if(self.openSide == drawerSide){ + CGRect newCenterRect = self.centerContainerView.frame; + newCenterRect.origin.x = drawerSideOriginCorrection*width; + [UIView + animateWithDuration:(animated?duration:0) + delay:0.0 + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self.centerContainerView setFrame:newCenterRect]; + [sideDrawerViewController.view setFrame:sideDrawerViewController.mm_visibleDrawerFrame]; + } + completion:^(BOOL finished) { + if(completion != nil){ + completion(finished); + } + }]; + } + else{ + [sideDrawerViewController.view setFrame:sideDrawerViewController.mm_visibleDrawerFrame]; + if(completion != nil){ + completion(YES); + } + } +} + +#pragma mark - Bounce Methods +-(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide completion:(void(^)(BOOL finished))completion{ + NSParameterAssert(drawerSide!=MMDrawerSideNone); + [self bouncePreviewForDrawerSide:drawerSide distance:MMDrawerDefaultBounceDistance completion:completion]; +} + +-(void)bouncePreviewForDrawerSide:(MMDrawerSide)drawerSide distance:(CGFloat)distance completion:(void(^)(BOOL finished))completion{ + NSParameterAssert(drawerSide!=MMDrawerSideNone); + + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + + if(sideDrawerViewController == nil || + self.openSide != MMDrawerSideNone){ + if(completion){ + completion(NO); + } + return; + } + else { + [self prepareToPresentDrawer:drawerSide animated:YES]; + + [self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0]; + + [CATransaction begin]; + [CATransaction + setCompletionBlock:^{ + [sideDrawerViewController endAppearanceTransition]; + [sideDrawerViewController beginAppearanceTransition:NO animated:NO]; + [sideDrawerViewController endAppearanceTransition]; + if(completion){ + completion(YES); + } + }]; + + CGFloat modifier = ((drawerSide == MMDrawerSideLeft)?1.0:-1.0); + CAKeyframeAnimation *animation = bounceKeyFrameAnimationForDistanceOnView(distance*modifier,self.centerContainerView); + [self.centerContainerView.layer addAnimation:animation forKey:@"bouncing"]; + + [CATransaction commit]; + } +} + +#pragma mark - Setting Drawer Visual State +-(void)setDrawerVisualStateBlock:(void (^)(MMDrawerController *, MMDrawerSide, CGFloat))drawerVisualStateBlock{ + [self setDrawerVisualState:drawerVisualStateBlock]; +} + +#pragma mark - Setting Custom Gesture Handler Block +-(void)setGestureShouldRecognizeTouchBlock:(BOOL (^)(MMDrawerController *, UIGestureRecognizer *, UITouch *))gestureShouldRecognizeTouchBlock{ + [self setGestureShouldRecognizeTouch:gestureShouldRecognizeTouchBlock]; +} + +#pragma mark - Setting the Gesture Completion Block +-(void)setGestureCompletionBlock:(void (^)(MMDrawerController *, UIGestureRecognizer *))gestureCompletionBlock{ + [self setGestureCompletion:gestureCompletionBlock]; +} + +#pragma mark - Subclass Methods +-(BOOL)shouldAutomaticallyForwardAppearanceMethods{ + return NO; +} + +-(BOOL)shouldAutomaticallyForwardRotationMethods{ + return NO; +} + +-(BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{ + return NO; +} + +#pragma mark - View Lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self.view setBackgroundColor:[UIColor blackColor]]; + + [self setupGestureRecognizers]; +} + +-(void)viewWillAppear:(BOOL)animated{ + [super viewWillAppear:animated]; + [self.centerViewController beginAppearanceTransition:YES animated:animated]; + + if(self.openSide == MMDrawerSideLeft) { + [self.leftDrawerViewController beginAppearanceTransition:YES animated:animated]; + } + else if(self.openSide == MMDrawerSideRight) { + [self.rightDrawerViewController beginAppearanceTransition:YES animated:animated]; + } +} + +-(void)viewDidAppear:(BOOL)animated{ + [super viewDidAppear:animated]; + [self updateShadowForCenterView]; + [self.centerViewController endAppearanceTransition]; + + if(self.openSide == MMDrawerSideLeft) { + [self.leftDrawerViewController endAppearanceTransition]; + } + else if(self.openSide == MMDrawerSideRight) { + [self.rightDrawerViewController endAppearanceTransition]; + } +} + +-(void)viewWillDisappear:(BOOL)animated{ + [super viewWillDisappear:animated]; + [self.centerViewController beginAppearanceTransition:NO animated:animated]; + if(self.openSide == MMDrawerSideLeft) { + [self.leftDrawerViewController beginAppearanceTransition:NO animated:animated]; + } + else if (self.openSide == MMDrawerSideRight) { + [self.rightDrawerViewController beginAppearanceTransition:NO animated:animated]; + } +} + +-(void)viewDidDisappear:(BOOL)animated{ + [super viewDidDisappear:animated]; + [self.centerViewController endAppearanceTransition]; + if(self.openSide == MMDrawerSideLeft) { + [self.leftDrawerViewController endAppearanceTransition]; + } + else if (self.openSide == MMDrawerSideRight) { + [self.rightDrawerViewController endAppearanceTransition]; + } +} + +#pragma mark Rotation + +-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ + [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; + //If a rotation begins, we are going to cancel the current gesture and reset transform and anchor points so everything works correctly + BOOL gestureInProgress = NO; + for(UIGestureRecognizer * gesture in self.view.gestureRecognizers){ + if(gesture.state == UIGestureRecognizerStateChanged){ + [gesture setEnabled:NO]; + [gesture setEnabled:YES]; + gestureInProgress = YES; + } + if (gestureInProgress) { + [self resetDrawerVisualStateForDrawerSide:self.openSide]; + } + } + if ([self needsManualForwardingOfRotationEvents]){ + for(UIViewController * childViewController in self.childViewControllers){ + [childViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; + } + } + +} +-(void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ + [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; + //We need to support the shadow path rotation animation + //Inspired from here: http://blog.radi.ws/post/8348898129/calayers-shadowpath-and-uiview-autoresizing + if(self.showsShadow){ + CGPathRef oldShadowPath = self.centerContainerView.layer.shadowPath; + if(oldShadowPath){ + CFRetain(oldShadowPath); + } + + [self updateShadowForCenterView]; + + if (oldShadowPath) { + [self.centerContainerView.layer addAnimation:((^ { + CABasicAnimation *transition = [CABasicAnimation animationWithKeyPath:@"shadowPath"]; + transition.fromValue = (__bridge id)oldShadowPath; + transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; + transition.duration = duration; + return transition; + })()) forKey:@"transition"]; + CFRelease(oldShadowPath); + } + } + + if ([self needsManualForwardingOfRotationEvents]){ + for(UIViewController * childViewController in self.childViewControllers){ + [childViewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; + } + } +} + +-(BOOL)shouldAutorotate{ + return YES; +} + +-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{ + [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; + if ([self needsManualForwardingOfRotationEvents]){ + for(UIViewController * childViewController in self.childViewControllers){ + [childViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation]; + } + } +} + +#pragma mark - Setters +-(void)setRightDrawerViewController:(UIViewController *)rightDrawerViewController{ + [self setDrawerViewController:rightDrawerViewController forSide:MMDrawerSideRight]; +} + +-(void)setLeftDrawerViewController:(UIViewController *)leftDrawerViewController{ + [self setDrawerViewController:leftDrawerViewController forSide:MMDrawerSideLeft]; +} + +- (void)setDrawerViewController:(UIViewController *)viewController forSide:(MMDrawerSide)drawerSide{ + NSParameterAssert(drawerSide != MMDrawerSideNone); + + UIViewController *currentSideViewController = [self sideDrawerViewControllerForSide:drawerSide]; + + if (currentSideViewController == viewController) { + return; + } + + if (currentSideViewController != nil) { + [currentSideViewController beginAppearanceTransition:NO animated:NO]; + [currentSideViewController.view removeFromSuperview]; + [currentSideViewController endAppearanceTransition]; + [currentSideViewController willMoveToParentViewController:nil]; + [currentSideViewController removeFromParentViewController]; + } + + UIViewAutoresizing autoResizingMask = 0; + if (drawerSide == MMDrawerSideLeft) { + _leftDrawerViewController = viewController; + autoResizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleHeight; + + } + else if(drawerSide == MMDrawerSideRight){ + _rightDrawerViewController = viewController; + autoResizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight; + } + + if(viewController){ + [self addChildViewController:viewController]; + + if((self.openSide == drawerSide) && + [self.childControllerContainerView.subviews containsObject:self.centerContainerView]){ + [self.childControllerContainerView insertSubview:viewController.view belowSubview:self.centerContainerView]; + [viewController beginAppearanceTransition:YES animated:NO]; + [viewController endAppearanceTransition]; + } + else{ + [self.childControllerContainerView addSubview:viewController.view]; + [self.childControllerContainerView sendSubviewToBack:viewController.view]; + [viewController.view setHidden:YES]; + } + [viewController didMoveToParentViewController:self]; + [viewController.view setAutoresizingMask:autoResizingMask]; + [viewController.view setFrame:viewController.mm_visibleDrawerFrame]; + } +} + +-(void)setCenterViewController:(UIViewController *)centerViewController{ + [self setCenterViewController:centerViewController animated:NO]; +} + +-(void)setShowsShadow:(BOOL)showsShadow{ + _showsShadow = showsShadow; + [self updateShadowForCenterView]; +} + +- (void)setShadowRadius:(CGFloat)shadowRadius{ + _shadowRadius = shadowRadius; + [self updateShadowForCenterView]; +} + +- (void)setShadowOpacity:(CGFloat)shadowOpacity{ + _shadowOpacity = shadowOpacity; + [self updateShadowForCenterView]; +} + +- (void)setShadowOffset:(CGSize)shadowOffset{ + _shadowOffset = shadowOffset; + [self updateShadowForCenterView]; +} + +- (void)setShadowColor:(UIColor *)shadowColor{ + _shadowColor = shadowColor; + [self updateShadowForCenterView]; +} + +-(void)setOpenSide:(MMDrawerSide)openSide{ + if(_openSide != openSide){ + _openSide = openSide; + [self.centerContainerView setOpenSide:openSide]; + if(openSide == MMDrawerSideNone){ + [self.leftDrawerViewController.view setHidden:YES]; + [self.rightDrawerViewController.view setHidden:YES]; + } + [self setNeedsStatusBarAppearanceUpdateIfSupported]; + } +} + +-(void)setCenterHiddenInteractionMode:(MMDrawerOpenCenterInteractionMode)centerHiddenInteractionMode{ + if(_centerHiddenInteractionMode!=centerHiddenInteractionMode){ + _centerHiddenInteractionMode = centerHiddenInteractionMode; + [self.centerContainerView setCenterInteractionMode:centerHiddenInteractionMode]; + } +} + +-(void)setMaximumLeftDrawerWidth:(CGFloat)maximumLeftDrawerWidth{ + [self setMaximumLeftDrawerWidth:maximumLeftDrawerWidth animated:NO completion:nil]; +} + +-(void)setMaximumRightDrawerWidth:(CGFloat)maximumRightDrawerWidth{ + [self setMaximumRightDrawerWidth:maximumRightDrawerWidth animated:NO completion:nil]; +} + +-(void)setShowsStatusBarBackgroundView:(BOOL)showsDummyStatusBar{ + if(showsDummyStatusBar!=_showsStatusBarBackgroundView){ + _showsStatusBarBackgroundView = showsDummyStatusBar; + CGRect frame = self.childControllerContainerView.frame; + if(_showsStatusBarBackgroundView){ + frame.origin.y = 20; + frame.size.height = CGRectGetHeight(self.view.bounds)-20; + } + else { + frame.origin.y = 0; + frame.size.height = CGRectGetHeight(self.view.bounds); + } + [self.childControllerContainerView setFrame:frame]; + [self.dummyStatusBarView setHidden:!showsDummyStatusBar]; + } +} + +-(void)setStatusBarViewBackgroundColor:(UIColor *)dummyStatusBarColor{ + _statusBarViewBackgroundColor = dummyStatusBarColor; + [self.dummyStatusBarView setBackgroundColor:_statusBarViewBackgroundColor]; +} + +-(void)setAnimatingDrawer:(BOOL)animatingDrawer{ + _animatingDrawer = animatingDrawer; + [self.view setUserInteractionEnabled:!animatingDrawer]; +} + +#pragma mark - Getters +-(CGFloat)maximumLeftDrawerWidth{ + if(self.leftDrawerViewController){ + return _maximumLeftDrawerWidth; + } + else{ + return 0; + } +} + +-(CGFloat)maximumRightDrawerWidth{ + if(self.rightDrawerViewController){ + return _maximumRightDrawerWidth; + } + else { + return 0; + } +} + +-(CGFloat)visibleLeftDrawerWidth{ + return MAX(0.0,CGRectGetMinX(self.centerContainerView.frame)); +} + +-(CGFloat)visibleRightDrawerWidth{ + if(CGRectGetMinX(self.centerContainerView.frame)<0){ + return CGRectGetWidth(self.childControllerContainerView.bounds)-CGRectGetMaxX(self.centerContainerView.frame); + } + else { + return 0.0f; + } +} + +-(UIView*)childControllerContainerView{ + if(_childControllerContainerView == nil){ + //Issue #152 (https://github.com/mutualmobile/MMDrawerController/issues/152) + //Turns out we have two child container views getting added to the view during init, + //because the first request self.view.bounds was kicking off a viewDidLoad, which + //caused us to be able to fall through this check twice. + // + //The fix is to grab the bounds, and then check again that the child container view has + //not been created. + CGRect childContainerViewFrame = self.view.bounds; + if(_childControllerContainerView == nil){ + _childControllerContainerView = [[UIView alloc] initWithFrame:childContainerViewFrame]; + [_childControllerContainerView setBackgroundColor:[UIColor clearColor]]; + [_childControllerContainerView setAutoresizingMask:UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth]; + [self.view addSubview:_childControllerContainerView]; + } + + } + return _childControllerContainerView; +} + +-(UIView*)dummyStatusBarView{ + if(_dummyStatusBarView==nil){ + _dummyStatusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 20)]; + [_dummyStatusBarView setAutoresizingMask:UIViewAutoresizingFlexibleWidth]; + [_dummyStatusBarView setBackgroundColor:self.statusBarViewBackgroundColor]; + [_dummyStatusBarView setHidden:!_showsStatusBarBackgroundView]; + [self.view addSubview:_dummyStatusBarView]; + } + return _dummyStatusBarView; +} + +-(UIColor*)statusBarViewBackgroundColor{ + if(_statusBarViewBackgroundColor == nil){ + _statusBarViewBackgroundColor = [UIColor blackColor]; + } + return _statusBarViewBackgroundColor; +} + +#pragma mark - Gesture Handlers + +-(void)tapGestureCallback:(UITapGestureRecognizer *)tapGesture{ + if(self.openSide != MMDrawerSideNone && + self.isAnimatingDrawer == NO){ + [self closeDrawerAnimated:YES completion:^(BOOL finished) { + if(self.gestureCompletion){ + self.gestureCompletion(self, tapGesture); + } + }]; + } +} + +-(void)panGestureCallback:(UIPanGestureRecognizer *)panGesture{ + switch (panGesture.state) { + case UIGestureRecognizerStateBegan:{ + if(self.animatingDrawer){ + [panGesture setEnabled:NO]; + break; + } + else { + self.startingPanRect = self.centerContainerView.frame; + } + } + case UIGestureRecognizerStateChanged:{ + self.view.userInteractionEnabled = NO; + CGRect newFrame = self.startingPanRect; + CGPoint translatedPoint = [panGesture translationInView:self.centerContainerView]; + newFrame.origin.x = [self roundedOriginXForDrawerConstriants:CGRectGetMinX(self.startingPanRect)+translatedPoint.x]; + newFrame = CGRectIntegral(newFrame); + CGFloat xOffset = newFrame.origin.x; + + MMDrawerSide visibleSide = MMDrawerSideNone; + CGFloat percentVisible = 0.0; + if(xOffset > 0){ + visibleSide = MMDrawerSideLeft; + percentVisible = xOffset/self.maximumLeftDrawerWidth; + } + else if(xOffset < 0){ + visibleSide = MMDrawerSideRight; + percentVisible = ABS(xOffset)/self.maximumRightDrawerWidth; + } + UIViewController * visibleSideDrawerViewController = [self sideDrawerViewControllerForSide:visibleSide]; + + if(self.openSide != visibleSide){ + //Handle disappearing the visible drawer + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:self.openSide]; + [sideDrawerViewController beginAppearanceTransition:NO animated:NO]; + [sideDrawerViewController endAppearanceTransition]; + + //Drawer is about to become visible + [self prepareToPresentDrawer:visibleSide animated:NO]; + [visibleSideDrawerViewController endAppearanceTransition]; + [self setOpenSide:visibleSide]; + } + else if(visibleSide == MMDrawerSideNone){ + [self setOpenSide:MMDrawerSideNone]; + } + + [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:percentVisible]; + + [self.centerContainerView setCenter:CGPointMake(CGRectGetMidX(newFrame), CGRectGetMidY(newFrame))]; + + newFrame = self.centerContainerView.frame; + newFrame.origin.x = floor(newFrame.origin.x); + newFrame.origin.y = floor(newFrame.origin.y); + self.centerContainerView.frame = newFrame; + + break; + } + case UIGestureRecognizerStateEnded: + case UIGestureRecognizerStateCancelled: { + self.startingPanRect = CGRectNull; + CGPoint velocity = [panGesture velocityInView:self.childControllerContainerView]; + [self finishAnimationForPanGestureWithXVelocity:velocity.x completion:^(BOOL finished) { + if(self.gestureCompletion){ + self.gestureCompletion(self, panGesture); + } + }]; + self.view.userInteractionEnabled = YES; + break; + } + default: + break; + } +} + +#pragma mark - iOS 7 Status Bar Helpers +-(UIViewController*)childViewControllerForStatusBarStyle{ + return [self childViewControllerForSide:self.openSide]; +} + +-(UIViewController*)childViewControllerForStatusBarHidden{ + return [self childViewControllerForSide:self.openSide]; +} + +-(void)setNeedsStatusBarAppearanceUpdateIfSupported{ + if([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]){ + [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)]; + } +} + +#pragma mark - iOS 8 Rotation Helpers +- (BOOL)needsManualForwardingOfRotationEvents{ + BOOL isIOS8 = (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1); + return !isIOS8; +} + +#pragma mark - Animation helpers +-(void)finishAnimationForPanGestureWithXVelocity:(CGFloat)xVelocity completion:(void(^)(BOOL finished))completion{ + CGFloat currentOriginX = CGRectGetMinX(self.centerContainerView.frame); + + CGFloat animationVelocity = MAX(ABS(xVelocity),self.panVelocityXAnimationThreshold*2); + + if(self.openSide == MMDrawerSideLeft) { + CGFloat midPoint = self.maximumLeftDrawerWidth / 2.0; + if(xVelocity > self.panVelocityXAnimationThreshold){ + [self openDrawerSide:MMDrawerSideLeft animated:YES velocity:animationVelocity animationOptions:UIViewAnimationOptionCurveEaseOut completion:completion]; + } + else if(xVelocity < -self.panVelocityXAnimationThreshold){ + [self closeDrawerAnimated:YES velocity:animationVelocity animationOptions:UIViewAnimationOptionCurveEaseOut completion:completion]; + } + else if(currentOriginX < midPoint){ + [self closeDrawerAnimated:YES completion:completion]; + } + else { + [self openDrawerSide:MMDrawerSideLeft animated:YES completion:completion]; + } + } + else if(self.openSide == MMDrawerSideRight){ + currentOriginX = CGRectGetMaxX(self.centerContainerView.frame); + CGFloat midPoint = (CGRectGetWidth(self.childControllerContainerView.bounds)-self.maximumRightDrawerWidth) + (self.maximumRightDrawerWidth / 2.0); + if(xVelocity > self.panVelocityXAnimationThreshold){ + [self closeDrawerAnimated:YES velocity:animationVelocity animationOptions:UIViewAnimationOptionCurveEaseOut completion:completion]; + } + else if (xVelocity < -self.panVelocityXAnimationThreshold){ + [self openDrawerSide:MMDrawerSideRight animated:YES velocity:animationVelocity animationOptions:UIViewAnimationOptionCurveEaseOut completion:completion]; + } + else if(currentOriginX > midPoint){ + [self closeDrawerAnimated:YES completion:completion]; + } + else { + [self openDrawerSide:MMDrawerSideRight animated:YES completion:completion]; + } + } + else { + if(completion){ + completion(NO); + } + } +} + +-(void)updateDrawerVisualStateForDrawerSide:(MMDrawerSide)drawerSide percentVisible:(CGFloat)percentVisible{ + if(self.drawerVisualState){ + self.drawerVisualState(self,drawerSide,percentVisible); + } + else if(self.shouldStretchDrawer){ + [self applyOvershootScaleTransformForDrawerSide:drawerSide percentVisible:percentVisible]; + } +} + +- (void)applyOvershootScaleTransformForDrawerSide:(MMDrawerSide)drawerSide percentVisible:(CGFloat)percentVisible{ + + if (percentVisible >= 1.f) { + CATransform3D transform = CATransform3DIdentity; + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + if(drawerSide == MMDrawerSideLeft) { + transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + transform = CATransform3DTranslate(transform, self.maximumLeftDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); + } + else if(drawerSide == MMDrawerSideRight){ + transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + transform = CATransform3DTranslate(transform, -self.maximumRightDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); + } + sideDrawerViewController.view.layer.transform = transform; + } +} + +-(void)resetDrawerVisualStateForDrawerSide:(MMDrawerSide)drawerSide{ + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + + [sideDrawerViewController.view.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)]; + [sideDrawerViewController.view.layer setTransform:CATransform3DIdentity]; + [sideDrawerViewController.view setAlpha:1.0]; +} + +-(CGFloat)roundedOriginXForDrawerConstriants:(CGFloat)originX{ + + if (originX < -self.maximumRightDrawerWidth) { + if (self.shouldStretchDrawer && + self.rightDrawerViewController) { + CGFloat maxOvershoot = (CGRectGetWidth(self.centerContainerView.frame)-self.maximumRightDrawerWidth)*MMDrawerOvershootPercentage; + return originXForDrawerOriginAndTargetOriginOffset(originX, -self.maximumRightDrawerWidth, maxOvershoot); + } + else{ + return -self.maximumRightDrawerWidth; + } + } + else if(originX > self.maximumLeftDrawerWidth){ + if (self.shouldStretchDrawer && + self.leftDrawerViewController) { + CGFloat maxOvershoot = (CGRectGetWidth(self.centerContainerView.frame)-self.maximumLeftDrawerWidth)*MMDrawerOvershootPercentage; + return originXForDrawerOriginAndTargetOriginOffset(originX, self.maximumLeftDrawerWidth, maxOvershoot); + } + else{ + return self.maximumLeftDrawerWidth; + } + } + + return originX; +} + +static inline CGFloat originXForDrawerOriginAndTargetOriginOffset(CGFloat originX, CGFloat targetOffset, CGFloat maxOvershoot){ + CGFloat delta = ABS(originX - targetOffset); + CGFloat maxLinearPercentage = MMDrawerOvershootLinearRangePercentage; + CGFloat nonLinearRange = maxOvershoot * maxLinearPercentage; + CGFloat nonLinearScalingDelta = (delta - nonLinearRange); + CGFloat overshoot = nonLinearRange + nonLinearScalingDelta * nonLinearRange/sqrt(pow(nonLinearScalingDelta,2.f) + 15000); + + if (delta < nonLinearRange) { + return originX; + } + else if (targetOffset < 0) { + return targetOffset - round(overshoot); + } + else{ + return targetOffset + round(overshoot); + } +} + +#pragma mark - Helpers +-(void)setupGestureRecognizers{ + UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureCallback:)]; + [pan setDelegate:self]; + [self.view addGestureRecognizer:pan]; + + UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureCallback:)]; + [tap setDelegate:self]; + [self.view addGestureRecognizer:tap]; +} + +-(void)prepareToPresentDrawer:(MMDrawerSide)drawer animated:(BOOL)animated{ + MMDrawerSide drawerToHide = MMDrawerSideNone; + if(drawer == MMDrawerSideLeft){ + drawerToHide = MMDrawerSideRight; + } + else if(drawer == MMDrawerSideRight){ + drawerToHide = MMDrawerSideLeft; + } + + UIViewController * sideDrawerViewControllerToPresent = [self sideDrawerViewControllerForSide:drawer]; + UIViewController * sideDrawerViewControllerToHide = [self sideDrawerViewControllerForSide:drawerToHide]; + + [self.childControllerContainerView sendSubviewToBack:sideDrawerViewControllerToHide.view]; + [sideDrawerViewControllerToHide.view setHidden:YES]; + [sideDrawerViewControllerToPresent.view setHidden:NO]; + [self resetDrawerVisualStateForDrawerSide:drawer]; + [sideDrawerViewControllerToPresent.view setFrame:sideDrawerViewControllerToPresent.mm_visibleDrawerFrame]; + [self updateDrawerVisualStateForDrawerSide:drawer percentVisible:0.0]; + [sideDrawerViewControllerToPresent beginAppearanceTransition:YES animated:animated]; +} + +-(void)updateShadowForCenterView{ + UIView * centerView = self.centerContainerView; + if(self.showsShadow){ + centerView.layer.masksToBounds = NO; + centerView.layer.shadowRadius = self.shadowRadius; + centerView.layer.shadowOpacity = self.shadowOpacity; + centerView.layer.shadowOffset = self.shadowOffset; + centerView.layer.shadowColor = [self.shadowColor CGColor]; + + /** In the event this gets called a lot, we won't update the shadowPath + unless it needs to be updated (like during rotation) */ + if (centerView.layer.shadowPath == NULL) { + centerView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:self.centerContainerView.bounds] CGPath]; + } + else{ + CGRect currentPath = CGPathGetPathBoundingBox(centerView.layer.shadowPath); + if (CGRectEqualToRect(currentPath, centerView.bounds) == NO){ + centerView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:self.centerContainerView.bounds] CGPath]; + } + } + } + else if (centerView.layer.shadowPath != NULL) { + centerView.layer.shadowRadius = 0.f; + centerView.layer.shadowOpacity = 0.f; + centerView.layer.shadowOffset = CGSizeMake(0, -3); + centerView.layer.shadowPath = NULL; + centerView.layer.masksToBounds = YES; + } +} + +-(NSTimeInterval)animationDurationForAnimationDistance:(CGFloat)distance{ + NSTimeInterval duration = MAX(distance/self.animationVelocity,MMDrawerMinimumAnimationDuration); + return duration; +} + +-(UIViewController*)sideDrawerViewControllerForSide:(MMDrawerSide)drawerSide{ + UIViewController * sideDrawerViewController = nil; + if(drawerSide != MMDrawerSideNone){ + sideDrawerViewController = [self childViewControllerForSide:drawerSide]; + } + return sideDrawerViewController; +} + +-(UIViewController*)childViewControllerForSide:(MMDrawerSide)drawerSide{ + UIViewController * childViewController = nil; + switch (drawerSide) { + case MMDrawerSideLeft: + childViewController = self.leftDrawerViewController; + break; + case MMDrawerSideRight: + childViewController = self.rightDrawerViewController; + break; + case MMDrawerSideNone: + childViewController = self.centerViewController; + break; + } + return childViewController; +} + +#pragma mark - UIGestureRecognizerDelegate +-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{ + + if(self.openSide == MMDrawerSideNone){ + MMOpenDrawerGestureMode possibleOpenGestureModes = [self possibleOpenGestureModesForGestureRecognizer:gestureRecognizer + withTouch:touch]; + return ((self.openDrawerGestureModeMask & possibleOpenGestureModes)>0); + } + else{ + MMCloseDrawerGestureMode possibleCloseGestureModes = [self possibleCloseGestureModesForGestureRecognizer:gestureRecognizer + withTouch:touch]; + return ((self.closeDrawerGestureModeMask & possibleCloseGestureModes)>0); + } +} + +#pragma mark Gesture Recogizner Delegate Helpers +-(MMCloseDrawerGestureMode)possibleCloseGestureModesForGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer withTouch:(UITouch*)touch{ + CGPoint point = [touch locationInView:self.childControllerContainerView]; + MMCloseDrawerGestureMode possibleCloseGestureModes = MMCloseDrawerGestureModeNone; + if([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){ + if([self isPointContainedWithinNavigationRect:point]){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeTapNavigationBar; + } + if([self isPointContainedWithinCenterViewContentRect:point]){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeTapCenterView; + } + } + else if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]){ + if([self isPointContainedWithinNavigationRect:point]){ + possibleCloseGestureModes |= MMCloseDrawerGestureModePanningNavigationBar; + } + if([self isPointContainedWithinCenterViewContentRect:point]){ + possibleCloseGestureModes |= MMCloseDrawerGestureModePanningCenterView; + } + if([self isPointContainedWithinRightBezelRect:point] && + self.openSide == MMDrawerSideLeft){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeBezelPanningCenterView; + } + if([self isPointContainedWithinLeftBezelRect:point] && + self.openSide == MMDrawerSideRight){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeBezelPanningCenterView; + } + if([self isPointContainedWithinCenterViewContentRect:point] == NO && + [self isPointContainedWithinNavigationRect:point] == NO){ + possibleCloseGestureModes |= MMCloseDrawerGestureModePanningDrawerView; + } + } + if((self.closeDrawerGestureModeMask & MMCloseDrawerGestureModeCustom) > 0 && + self.gestureShouldRecognizeTouch){ + if(self.gestureShouldRecognizeTouch(self,gestureRecognizer,touch)){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeCustom; + } + } + return possibleCloseGestureModes; +} + +-(MMOpenDrawerGestureMode)possibleOpenGestureModesForGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer withTouch:(UITouch*)touch{ + CGPoint point = [touch locationInView:self.childControllerContainerView]; + MMOpenDrawerGestureMode possibleOpenGestureModes = MMOpenDrawerGestureModeNone; + if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]){ + if([self isPointContainedWithinNavigationRect:point]){ + possibleOpenGestureModes |= MMOpenDrawerGestureModePanningNavigationBar; + } + if([self isPointContainedWithinCenterViewContentRect:point]){ + possibleOpenGestureModes |= MMOpenDrawerGestureModePanningCenterView; + } + if([self isPointContainedWithinLeftBezelRect:point] && + self.leftDrawerViewController){ + possibleOpenGestureModes |= MMOpenDrawerGestureModeBezelPanningCenterView; + } + if([self isPointContainedWithinRightBezelRect:point] && + self.rightDrawerViewController){ + possibleOpenGestureModes |= MMOpenDrawerGestureModeBezelPanningCenterView; + } + } + if((self.openDrawerGestureModeMask & MMOpenDrawerGestureModeCustom) > 0 && + self.gestureShouldRecognizeTouch){ + if(self.gestureShouldRecognizeTouch(self,gestureRecognizer,touch)){ + possibleOpenGestureModes |= MMOpenDrawerGestureModeCustom; + } + } + return possibleOpenGestureModes; +} + +-(BOOL)isPointContainedWithinNavigationRect:(CGPoint)point{ + CGRect navigationBarRect = CGRectNull; + if([self.centerViewController isKindOfClass:[UINavigationController class]]){ + UINavigationBar * navBar = [(UINavigationController*)self.centerViewController navigationBar]; + navigationBarRect = [navBar convertRect:navBar.bounds toView:self.childControllerContainerView]; + navigationBarRect = CGRectIntersection(navigationBarRect,self.childControllerContainerView.bounds); + } + return CGRectContainsPoint(navigationBarRect,point); +} + +-(BOOL)isPointContainedWithinCenterViewContentRect:(CGPoint)point{ + CGRect centerViewContentRect = self.centerContainerView.frame; + centerViewContentRect = CGRectIntersection(centerViewContentRect,self.childControllerContainerView.bounds); + return (CGRectContainsPoint(centerViewContentRect, point) && + [self isPointContainedWithinNavigationRect:point] == NO); +} + +-(BOOL)isPointContainedWithinLeftBezelRect:(CGPoint)point{ + CGRect leftBezelRect = CGRectNull; + CGRect tempRect; + CGRectDivide(self.childControllerContainerView.bounds, &leftBezelRect, &tempRect, self.bezelPanningCenterViewRange, CGRectMinXEdge); + return (CGRectContainsPoint(leftBezelRect, point) && + [self isPointContainedWithinCenterViewContentRect:point]); +} + +-(BOOL)isPointContainedWithinRightBezelRect:(CGPoint)point{ + CGRect rightBezelRect = CGRectNull; + CGRect tempRect; + CGRectDivide(self.childControllerContainerView.bounds, &rightBezelRect, &tempRect, self.bezelPanningCenterViewRange, CGRectMaxXEdge); + + return (CGRectContainsPoint(rightBezelRect, point) && + [self isPointContainedWithinCenterViewContentRect:point]); +} +@end diff --git a/MMDrawerController/MMDrawerVisualState.h b/MMDrawerController/MMDrawerVisualState.h new file mode 100755 index 0000000..7cb8fff --- /dev/null +++ b/MMDrawerController/MMDrawerVisualState.h @@ -0,0 +1,61 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 +#import "MMDrawerController.h" + +/** + `MMDrawerVisualState` provides prebuilt visual state update blocks that can be used in the callback block of `MMDrawerController` for drawer animation. + */ + +@interface MMDrawerVisualState : NSObject + +/** + Creates a slide and scale visual state block that gives an experience similar to Mailbox.app. It scales from 90% to 100%, and translates 50 pixels in the x direction. In addition, it also sets alpha from 0.0 to 1.0. + + @return The visual state block. + */ ++(MMDrawerControllerDrawerVisualStateBlock)slideAndScaleVisualStateBlock; + +/** + Creates a slide visual state block that gives the user an experience that slides at the same speed of the center view controller during animation. This is equal to calling `parallaxVisualStateBlockWithParallaxFactor:` with a parallax factor of 1.0. + + @return The visual state block. + */ ++(MMDrawerControllerDrawerVisualStateBlock)slideVisualStateBlock; + +/** + Creates a swinging door visual state block that gives the user an experience that animates the drawer in along the hinge. + + @return The visual state block. + */ ++(MMDrawerControllerDrawerVisualStateBlock)swingingDoorVisualStateBlock; + +/** + Creates a parallax experience that slides the side drawer view controller at a different rate than the center view controller during animation. For every parallaxFactor of points moved by the center view controller, the side drawer view controller will move 1 point. Passing in 1.0 is the equivalent of a applying a sliding animation, while passing in MAX_FLOAT is the equivalent of having no animation at all. + + @param parallaxFactor The amount of parallax applied to the side drawer conroller. This value must be greater than 1.0. The closer the value is to 1.0, the faster the side drawer view controller will be parallaxing. + + @return The visual state block. + */ ++(MMDrawerControllerDrawerVisualStateBlock)parallaxVisualStateBlockWithParallaxFactor:(CGFloat)parallaxFactor; + +@end diff --git a/MMDrawerController/MMDrawerVisualState.m b/MMDrawerController/MMDrawerVisualState.m new file mode 100755 index 0000000..013d019 --- /dev/null +++ b/MMDrawerController/MMDrawerVisualState.m @@ -0,0 +1,150 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "MMDrawerVisualState.h" +#import + +@implementation MMDrawerVisualState ++(MMDrawerControllerDrawerVisualStateBlock)slideAndScaleVisualStateBlock{ + MMDrawerControllerDrawerVisualStateBlock visualStateBlock = + ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ + CGFloat minScale = .90; + CGFloat scale = minScale + (percentVisible*(1.0-minScale)); + CATransform3D scaleTransform = CATransform3DMakeScale(scale, scale, scale); + + CGFloat maxDistance = 50; + CGFloat distance = maxDistance * percentVisible; + CATransform3D translateTransform = CATransform3DIdentity; + UIViewController * sideDrawerViewController; + if(drawerSide == MMDrawerSideLeft) { + sideDrawerViewController = drawerController.leftDrawerViewController; + translateTransform = CATransform3DMakeTranslation((maxDistance-distance), 0.0, 0.0); + } + else if(drawerSide == MMDrawerSideRight){ + sideDrawerViewController = drawerController.rightDrawerViewController; + translateTransform = CATransform3DMakeTranslation(-(maxDistance-distance), 0.0, 0.0); + } + + [sideDrawerViewController.view.layer setTransform:CATransform3DConcat(scaleTransform, translateTransform)]; + [sideDrawerViewController.view setAlpha:percentVisible]; + }; + return visualStateBlock; +} + ++(MMDrawerControllerDrawerVisualStateBlock)slideVisualStateBlock{ + return [self parallaxVisualStateBlockWithParallaxFactor:1.0]; +} + + ++(MMDrawerControllerDrawerVisualStateBlock)swingingDoorVisualStateBlock{ + MMDrawerControllerDrawerVisualStateBlock visualStateBlock = + ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ + UIViewController * sideDrawerViewController; + CGPoint anchorPoint; + CGFloat maxDrawerWidth = 0.0; + CGFloat xOffset; + CGFloat angle = 0.0; + + if(drawerSide==MMDrawerSideLeft){ + + sideDrawerViewController = drawerController.leftDrawerViewController; + anchorPoint = CGPointMake(1.0, .5); + maxDrawerWidth = MAX(drawerController.maximumLeftDrawerWidth,drawerController.visibleLeftDrawerWidth); + xOffset = -(maxDrawerWidth/2.0) + (maxDrawerWidth)*percentVisible; + angle = -M_PI_2+(percentVisible*M_PI_2); + } + else { + sideDrawerViewController = drawerController.rightDrawerViewController; + anchorPoint = CGPointMake(0.0, .5); + maxDrawerWidth = MAX(drawerController.maximumRightDrawerWidth,drawerController.visibleRightDrawerWidth); + xOffset = (maxDrawerWidth/2.0) - (maxDrawerWidth)*percentVisible; + angle = M_PI_2-(percentVisible*M_PI_2); + } + + [sideDrawerViewController.view.layer setAnchorPoint:anchorPoint]; + [sideDrawerViewController.view.layer setShouldRasterize:YES]; + [sideDrawerViewController.view.layer setRasterizationScale:[[UIScreen mainScreen] scale]]; + + CATransform3D swingingDoorTransform = CATransform3DIdentity; + if (percentVisible <= 1.f) { + + CATransform3D identity = CATransform3DIdentity; + identity.m34 = -1.0/1000.0; + CATransform3D rotateTransform = CATransform3DRotate(identity, angle, 0.0, 1.0, 0.0); + + CATransform3D translateTransform = CATransform3DMakeTranslation(xOffset, 0.0, 0.0); + + CATransform3D concatTransform = CATransform3DConcat(rotateTransform, translateTransform); + + swingingDoorTransform = concatTransform; + } + else{ + CATransform3D overshootTransform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + + NSInteger scalingModifier = 1.f; + if (drawerSide == MMDrawerSideRight) { + scalingModifier = -1.f; + } + + overshootTransform = CATransform3DTranslate(overshootTransform, scalingModifier*maxDrawerWidth/2, 0.f, 0.f); + swingingDoorTransform = overshootTransform; + } + + [sideDrawerViewController.view.layer setTransform:swingingDoorTransform]; + }; + return visualStateBlock; +} + ++(MMDrawerControllerDrawerVisualStateBlock)parallaxVisualStateBlockWithParallaxFactor:(CGFloat)parallaxFactor{ + MMDrawerControllerDrawerVisualStateBlock visualStateBlock = + ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ + NSParameterAssert(parallaxFactor >= 1.0); + CATransform3D transform = CATransform3DIdentity; + UIViewController * sideDrawerViewController; + if(drawerSide == MMDrawerSideLeft) { + sideDrawerViewController = drawerController.leftDrawerViewController; + CGFloat distance = MAX(drawerController.maximumLeftDrawerWidth,drawerController.visibleLeftDrawerWidth); + if (percentVisible <= 1.f) { + transform = CATransform3DMakeTranslation((-distance)/parallaxFactor+(distance*percentVisible/parallaxFactor), 0.0, 0.0); + } + else{ + transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + transform = CATransform3DTranslate(transform, drawerController.maximumLeftDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); + } + } + else if(drawerSide == MMDrawerSideRight){ + sideDrawerViewController = drawerController.rightDrawerViewController; + CGFloat distance = MAX(drawerController.maximumRightDrawerWidth,drawerController.visibleRightDrawerWidth); + if(percentVisible <= 1.f){ + transform = CATransform3DMakeTranslation((distance)/parallaxFactor-(distance*percentVisible)/parallaxFactor, 0.0, 0.0); + } + else{ + transform = CATransform3DMakeScale(percentVisible, 1.f, 1.f); + transform = CATransform3DTranslate(transform, -drawerController.maximumRightDrawerWidth*(percentVisible-1.f)/2, 0.f, 0.f); + } + } + + [sideDrawerViewController.view.layer setTransform:transform]; + }; + return visualStateBlock; +} + +@end diff --git a/MMDrawerController/UIViewController+MMDrawerController.h b/MMDrawerController/UIViewController+MMDrawerController.h new file mode 100755 index 0000000..1bf05ef --- /dev/null +++ b/MMDrawerController/UIViewController+MMDrawerController.h @@ -0,0 +1,45 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 + +#import "MMDrawerController.h" + +/** + A helper category on `UIViewController` that exposes the parent drawer controller, the visible side drawer frame, and a `mm_drawerWillAppear` method that is called when the drawer is about to appear. + */ + +@interface UIViewController (MMDrawerController) + +///--------------------------------------- +/// @name Accessing Drawer View Controller Properties +///--------------------------------------- + +/** + The `MMDrawerController` that the view controller is contained within. If the view controller is not contained within a `MMDrawerController`, this property is nil. Note that if the view controller is contained within a `UINavigationController`, that navigation controller is contained within a `MMDrawerController`, this property will return a refernce to the `MMDrawerController`, despite the fact that it is not the direct parent of the view controller. + */ +@property(nonatomic, strong, readonly) MMDrawerController *mm_drawerController; + +/** + The visible rect of the side drawer view controller in the drawer controller coordinate space. If the view controller is not a drawer in a `MMDrawerController`, then this property returns `CGRectNull` + */ +@property(nonatomic, assign, readonly) CGRect mm_visibleDrawerFrame; + +@end diff --git a/MMDrawerController/UIViewController+MMDrawerController.m b/MMDrawerController/UIViewController+MMDrawerController.m new file mode 100755 index 0000000..b1f8662 --- /dev/null +++ b/MMDrawerController/UIViewController+MMDrawerController.m @@ -0,0 +1,64 @@ +// Copyright (c) 2013 Mutual Mobile (http://mutualmobile.com/) +// +// 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 "UIViewController+MMDrawerController.h" + +@implementation UIViewController (MMDrawerController) + + +-(MMDrawerController*)mm_drawerController{ + UIViewController *parentViewController = self.parentViewController; + while (parentViewController != nil) { + if([parentViewController isKindOfClass:[MMDrawerController class]]){ + return (MMDrawerController *)parentViewController; + } + parentViewController = parentViewController.parentViewController; + } + return nil; +} + +-(CGRect)mm_visibleDrawerFrame{ + if([self isEqual:self.mm_drawerController.leftDrawerViewController] || + [self.navigationController isEqual:self.mm_drawerController.leftDrawerViewController]){ + CGRect rect = self.mm_drawerController.view.bounds; + rect.size.width = self.mm_drawerController.maximumLeftDrawerWidth; + if (self.mm_drawerController.showsStatusBarBackgroundView) { + rect.size.height -= 20.0f; + } + return rect; + + } + else if([self isEqual:self.mm_drawerController.rightDrawerViewController] || + [self.navigationController isEqual:self.mm_drawerController.rightDrawerViewController]){ + CGRect rect = self.mm_drawerController.view.bounds; + rect.size.width = self.mm_drawerController.maximumRightDrawerWidth; + rect.origin.x = CGRectGetWidth(self.mm_drawerController.view.bounds)-rect.size.width; + if (self.mm_drawerController.showsStatusBarBackgroundView) { + rect.size.height -= 20.0f; + } + return rect; + } + else { + return CGRectNull; + } +} + +@end diff --git a/TableVC.swift b/TableVC.swift old mode 100644 new mode 100755 index 62224b0..c4c969d --- a/TableVC.swift +++ b/TableVC.swift @@ -8,9 +8,18 @@ import UIKit import CoreData +import GoogleMobileAds class TableVC: UITableViewController, NSFetchedResultsControllerDelegate { + + @IBAction func menuButton(sender: AnyObject) { + + let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate + appDelegate.centerContainer!.toggleDrawerSide(MMDrawerSide.Left, animated: true, completion: nil) + } + @IBOutlet weak var admobBanner: GADBannerView! + let context : NSManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext var frc: NSFetchedResultsController = NSFetchedResultsController() @@ -21,6 +30,7 @@ class TableVC: UITableViewController, NSFetchedResultsControllerDelegate { } func listFetchRequest() -> NSFetchRequest{ + let fetchRequest = NSFetchRequest(entityName: "ShopList") let sortDescriptor = NSSortDescriptor(key:"listname",ascending:true) @@ -35,6 +45,16 @@ class TableVC: UITableViewController, NSFetchedResultsControllerDelegate { // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem() + //Display mobile ads here + admobBanner.adUnitID = "ca-app-pub-3000953524335319/6882684788" + admobBanner.rootViewController = self + self.view.addSubview(admobBanner) + let adRequest: GADRequest = GADRequest() + adRequest.testDevices = [""] + admobBanner.loadRequest(adRequest) + //self.navigationController?.navigationBarHidden = true; + + navigationController?.setNavigationBarHidden(navigationController?.navigationBarHidden == true, animated: true) frc = getFechedResultsController() frc.delegate = self @@ -46,6 +66,14 @@ class TableVC: UITableViewController, NSFetchedResultsControllerDelegate { } } + override func viewWillAppear(animated: Bool) { + // self.navigationController!.navigationBarHidden = false; + //self.navigationController?.setNavigationBarHidden(false, animated: true) + navigationController?.setNavigationBarHidden(navigationController?.navigationBarHidden == true, animated: true) + } + override func viewDidAppear(animated: Bool) { + navigationController?.setNavigationBarHidden(navigationController?.navigationBarHidden == true, animated: true) + } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() @@ -157,6 +185,14 @@ class TableVC: UITableViewController, NSFetchedResultsControllerDelegate { } + override func unwindForSegue(unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) { + if unwindSegue.identifier == "back"{ + + navigationController?.setNavigationBarHidden(navigationController?.navigationBarHidden == true, animated: true) + } + + } + // MARK: - Table view delegate func colorForIndex(index: Int) -> UIColor { diff --git a/reminderViewController.swift b/reminderViewController.swift new file mode 100644 index 0000000..3148e58 --- /dev/null +++ b/reminderViewController.swift @@ -0,0 +1,124 @@ +// +// reminderViewController.swift +// ListApp +// +// Created by Connie on 22/11/2015. +// + +import UIKit +import EventKit + +class reminderViewController: UIViewController { + + @IBOutlet var reminderText: UITextField! + @IBOutlet var myDatePicker: UIDatePicker! + var appDelegate: AppDelegate? + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + } + + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + + @IBAction func setReminder(sender: AnyObject) { + + appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate + + if appDelegate!.eventStore == nil { + appDelegate!.eventStore = EKEventStore() + appDelegate!.eventStore!.requestAccessToEntityType(EKEntityType.Reminder, completion: + {(granted, error) in + if !granted + { + print("Access to store not granted") + print(error!.localizedDescription) + } + else + { + print("Access granted") + } + }) + + } + + if (appDelegate!.eventStore != nil) { + do { try self.createReminder() + } catch { + + print(error) + + } + } + + + } + + + func createReminder() throws { + + guard let eventStore = appDelegate?.eventStore else { + let error: NSError = NSError(domain: "com.mycompany.myapp", code: 1, userInfo: [ + NSLocalizedDescriptionKey: "Unable to get event store from app delegate" + ]) + throw error + } + + let reminder = EKReminder(eventStore: eventStore) + + reminder.title = reminderText.text! + reminder.calendar = eventStore.defaultCalendarForNewReminders() + let date = myDatePicker.date + let alarm = EKAlarm(absoluteDate: date) + + reminder.addAlarm(alarm) + + try eventStore.saveReminder(reminder, commit: true) + + } + +// func createReminder() { +// +// let reminder = EKReminder(eventStore: appDelegate!.eventStore!) +// +// reminder.title = reminderText.text! +// reminder.calendar = +// appDelegate!.eventStore!.defaultCalendarForNewReminders() +// let date = myDatePicker.date +// let alarm = EKAlarm(absoluteDate: date) +// +// reminder.addAlarm(alarm) +// +// var error: NSError? +// appDelegate!.eventStore!.saveReminder(reminder, +// commit: true, error: error) +// +// if error != nil { +// print("Reminder failed with error \(error?.localizedDescription)") +// } +// } + + +// // Hiding the Keyboard +// func touchesBegan(touches: Set, withEvent event: UIEvent) { +// reminderText.endEditing(true) +// } +// + + /* + // MARK: - Navigation + + // In a storyboard-based application, you will often want to do a little preparation before navigation + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + // Get the new view controller using segue.destinationViewController. + // Pass the selected object to the new view controller. + } + */ + +}