Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update KNSemiModalViewController.podspec #72

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion KNSemiModalViewController.podspec
Expand Up @@ -26,7 +26,7 @@ Pod::Spec.new do |s|
s.summary = 'Replica of the semi-modal view with pushed-back stacked animation found in the beautiful Park Guides by National Geographic app. Custom background view option was added in this fork.'
s.homepage = 'https://github.com/opedge/KNSemiModalViewController'
s.author = { 'Kent Nguyen' => 'nguyen.dmz@gmail.com', 'Oleg Poyaganov' => 'opedge@gmail.com' }
s.source = { :git => 'https://github.com/opedge/KNSemiModalViewController.git', :tag => '0.4' }
s.source = { :git => 'https://github.com/kentnguyen/KNSemiModalViewController.git', :tag => '0.4' }
s.platform = :ios
s.source_files = 'Source'

Expand Down
227 changes: 120 additions & 107 deletions Source/UIViewController+KNSemiModal.m
@@ -1,3 +1,4 @@

//
// KNSemiModalViewController.m
// KNSemiModalViewController
Expand All @@ -11,13 +12,13 @@
#import <objc/runtime.h>

const struct KNSemiModalOptionKeys KNSemiModalOptionKeys = {
.traverseParentHierarchy = @"KNSemiModalOptionTraverseParentHierarchy",
.pushParentBack = @"KNSemiModalOptionPushParentBack",
.animationDuration = @"KNSemiModalOptionAnimationDuration",
.parentAlpha = @"KNSemiModalOptionParentAlpha",
.traverseParentHierarchy = @"KNSemiModalOptionTraverseParentHierarchy",
.pushParentBack = @"KNSemiModalOptionPushParentBack",
.animationDuration = @"KNSemiModalOptionAnimationDuration",
.parentAlpha = @"KNSemiModalOptionParentAlpha",
.parentScale = @"KNSemiModalOptionParentScale",
.shadowOpacity = @"KNSemiModalOptionShadowOpacity",
.transitionStyle = @"KNSemiModalTransitionStyle",
.shadowOpacity = @"KNSemiModalOptionShadowOpacity",
.transitionStyle = @"KNSemiModalTransitionStyle",
.disableCancel = @"KNSemiModalOptionDisableCancel",
.backgroundView = @"KNSemiModelOptionBackgroundView",
};
Expand All @@ -37,33 +38,36 @@ -(CAAnimationGroup*)animationGroupForward:(BOOL)_forward;

@implementation UIViewController (KNSemiModalInternal)

UIDeviceOrientation orientationState; //set in -presentSemiView

-(UIViewController*)kn_parentTargetViewController {
UIViewController * target = self;
if ([[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.traverseParentHierarchy] boolValue]) {
// cover UINav & UITabbar as well
while (target.parentViewController != nil) {
target = target.parentViewController;
}
}
return target;
UIViewController * target = self;
if ([[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.traverseParentHierarchy] boolValue]) {
// cover UINav & UITabbar as well
while (target.parentViewController != nil) {
target = target.parentViewController;
}
}
return target;
}

-(UIView*)parentTarget {
return [self kn_parentTargetViewController].view;
}

#pragma mark Options and defaults

-(void)kn_registerDefaultsAndOptions:(NSDictionary*)options {
[self ym_registerOptions:options defaults:@{
KNSemiModalOptionKeys.traverseParentHierarchy : @(YES),
KNSemiModalOptionKeys.pushParentBack : @(YES),
KNSemiModalOptionKeys.animationDuration : @(0.5),
KNSemiModalOptionKeys.parentAlpha : @(0.5),
KNSemiModalOptionKeys.parentScale : @(0.8),
KNSemiModalOptionKeys.shadowOpacity : @(0.8),
KNSemiModalOptionKeys.transitionStyle : @(KNSemiModalTransitionStyleSlideUp),
KNSemiModalOptionKeys.disableCancel : @(NO),
}];
[self ym_registerOptions:options defaults:@{
KNSemiModalOptionKeys.traverseParentHierarchy : @(YES),
KNSemiModalOptionKeys.pushParentBack : @(YES),
KNSemiModalOptionKeys.animationDuration : @(0.5),
KNSemiModalOptionKeys.parentAlpha : @(0.5),
KNSemiModalOptionKeys.parentScale : @(0.8),
KNSemiModalOptionKeys.shadowOpacity : @(0.8),
KNSemiModalOptionKeys.transitionStyle : @(KNSemiModalTransitionStyleSlideUp),
KNSemiModalOptionKeys.disableCancel : @(NO),
}];
}

#pragma mark Push-back animation group
Expand Down Expand Up @@ -94,7 +98,7 @@ -(CAAnimationGroup*)animationGroupForward:(BOOL)_forward {

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.toValue = [NSValue valueWithCATransform3D:t1];
CFTimeInterval duration = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.animationDuration] doubleValue];
CFTimeInterval duration = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.animationDuration] doubleValue];
animation.duration = duration/2;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
Expand All @@ -116,97 +120,106 @@ -(CAAnimationGroup*)animationGroupForward:(BOOL)_forward {
}

-(void)kn_interfaceOrientationDidChange:(NSNotification*)notification {
UIView *overlay = [[self parentTarget] viewWithTag:kSemiModalOverlayTag];
[self kn_addOrUpdateParentScreenshotInView:overlay];
// if orientation really changed update screen shoot.
// UIDeviceOrientationDidChangeNotification send when display control center or notification panel
UIDeviceOrientation currentOrientationState = [[UIDevice currentDevice] orientation];
if ((orientationState != currentOrientationState) && (currentOrientationState != UIDeviceOrientationUnknown)
&& (currentOrientationState != UIDeviceOrientationFaceUp) && (currentOrientationState != UIDeviceOrientationFaceDown)) {
UIView *overlay = [[self parentTarget] viewWithTag:kSemiModalOverlayTag];
[self kn_addOrUpdateParentScreenshotInView:overlay];
orientationState = currentOrientationState;
}
}

-(UIImageView*)kn_addOrUpdateParentScreenshotInView:(UIView*)screenshotContainer {
UIView *target = [self parentTarget];
UIView *semiView = [target viewWithTag:kSemiModalModalViewTag];
screenshotContainer.hidden = YES; // screenshot without the overlay!
semiView.hidden = YES;
UIGraphicsBeginImageContextWithOptions(target.bounds.size, YES, [[UIScreen mainScreen] scale]);
UIView *target = [self parentTarget];
UIView *semiView = [target viewWithTag:kSemiModalModalViewTag];
screenshotContainer.hidden = YES; // screenshot without the overlay!
semiView.hidden = YES;
UIGraphicsBeginImageContextWithOptions(target.bounds.size, YES, [[UIScreen mainScreen] scale]);
if ([target respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]) {
[target drawViewHierarchyInRect:target.bounds afterScreenUpdates:YES];
} else {
[target.layer renderInContext:UIGraphicsGetCurrentContext()];
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
screenshotContainer.hidden = NO;
semiView.hidden = NO;
UIImageView* screenshot = (id) [screenshotContainer viewWithTag:kSemiModalScreenshotTag];
if (screenshot) {
screenshot.image = image;
}
else {
screenshot = [[UIImageView alloc] initWithImage:image];
screenshot.tag = kSemiModalScreenshotTag;
screenshot.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[screenshotContainer addSubview:screenshot];
}
return screenshot;
screenshotContainer.hidden = NO;
semiView.hidden = NO;
UIImageView* screenshot = (id) [screenshotContainer viewWithTag:kSemiModalScreenshotTag];
if (screenshot) {
screenshot.image = image;
}
else {
screenshot = [[UIImageView alloc] initWithImage:image];
screenshot.tag = kSemiModalScreenshotTag;
screenshot.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[screenshotContainer addSubview:screenshot];
}
return screenshot;
}

@end

@implementation UIViewController (KNSemiModal)

-(void)presentSemiViewController:(UIViewController*)vc {
[self presentSemiViewController:vc withOptions:nil completion:nil dismissBlock:nil];
[self presentSemiViewController:vc withOptions:nil completion:nil dismissBlock:nil];
}
-(void)presentSemiViewController:(UIViewController*)vc
withOptions:(NSDictionary*)options {
withOptions:(NSDictionary*)options {
[self presentSemiViewController:vc withOptions:options completion:nil dismissBlock:nil];
}
-(void)presentSemiViewController:(UIViewController*)vc
withOptions:(NSDictionary*)options
completion:(KNTransitionCompletionBlock)completion
dismissBlock:(KNTransitionCompletionBlock)dismissBlock {
withOptions:(NSDictionary*)options
completion:(KNTransitionCompletionBlock)completion
dismissBlock:(KNTransitionCompletionBlock)dismissBlock {
[self kn_registerDefaultsAndOptions:options]; // re-registering is OK
UIViewController *targetParentVC = [self kn_parentTargetViewController];

// implement view controller containment for the semi-modal view controller
[targetParentVC addChildViewController:vc];
if ([vc respondsToSelector:@selector(beginAppearanceTransition:animated:)]) {
[vc beginAppearanceTransition:YES animated:YES]; // iOS 6
}
objc_setAssociatedObject(self, kSemiModalViewController, vc, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, kSemiModalDismissBlock, dismissBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self presentSemiView:vc.view withOptions:options completion:^{
[vc didMoveToParentViewController:targetParentVC];
if ([vc respondsToSelector:@selector(endAppearanceTransition)]) {
[vc endAppearanceTransition]; // iOS 6
}
if (completion) {
completion();
}
}];
UIViewController *targetParentVC = [self kn_parentTargetViewController];
// implement view controller containment for the semi-modal view controller
[targetParentVC addChildViewController:vc];
if ([vc respondsToSelector:@selector(beginAppearanceTransition:animated:)]) {
[vc beginAppearanceTransition:YES animated:YES]; // iOS 6
}
objc_setAssociatedObject(self, kSemiModalViewController, vc, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, kSemiModalDismissBlock, dismissBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self presentSemiView:vc.view withOptions:options completion:^{
[vc didMoveToParentViewController:targetParentVC];
if ([vc respondsToSelector:@selector(endAppearanceTransition)]) {
[vc endAppearanceTransition]; // iOS 6
}
if (completion) {
completion();
}
}];
}

-(void)presentSemiView:(UIView*)view {
[self presentSemiView:view withOptions:nil completion:nil];
[self presentSemiView:view withOptions:nil completion:nil];
}
-(void)presentSemiView:(UIView*)view withOptions:(NSDictionary*)options {
[self presentSemiView:view withOptions:options completion:nil];
[self presentSemiView:view withOptions:options completion:nil];
}
-(void)presentSemiView:(UIView*)view
withOptions:(NSDictionary*)options
completion:(KNTransitionCompletionBlock)completion {
[self kn_registerDefaultsAndOptions:options]; // re-registering is OK
UIView * target = [self parentTarget];
withOptions:(NSDictionary*)options
completion:(KNTransitionCompletionBlock)completion {
[self kn_registerDefaultsAndOptions:options]; // re-registering is OK
UIView * target = [self parentTarget];
if (![target.subviews containsObject:view]) {
orientationState = [[UIDevice currentDevice] orientation];
// Set associative object
objc_setAssociatedObject(view, kSemiModalPresentingViewController, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

// Register for orientation changes, so we can update the presenting controller screenshot
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(kn_interfaceOrientationDidChange:)
name:UIDeviceOrientationDidChangeNotification
object:nil];

// Get transition style
NSUInteger transitionStyle = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.transitionStyle] unsignedIntegerValue];

Expand Down Expand Up @@ -255,10 +268,10 @@ -(void)presentSemiView:(UIView*)view
}

// Begin overlay animation
if ([[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.pushParentBack] boolValue]) {
[ss.layer addAnimation:[self animationGroupForward:YES] forKey:@"pushedBackAnimation"];
}
NSTimeInterval duration = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.animationDuration] doubleValue];
if ([[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.pushParentBack] boolValue]) {
[ss.layer addAnimation:[self animationGroupForward:YES] forKey:@"pushedBackAnimation"];
}
NSTimeInterval duration = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.animationDuration] doubleValue];
[UIView animateWithDuration:duration animations:^{
ss.alpha = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.parentAlpha] floatValue];
}];
Expand Down Expand Up @@ -309,7 +322,7 @@ -(void)updateBackground{
[self kn_addOrUpdateParentScreenshotInView:overlay];
}
-(void)dismissSemiModalView {
[self dismissSemiModalViewWithCompletion:nil];
[self dismissSemiModalViewWithCompletion:nil];
}

-(void)dismissSemiModalViewWithCompletion:(void (^)(void))completion {
Expand All @@ -325,22 +338,22 @@ -(void)dismissSemiModalViewWithCompletion:(void (^)(void))completion {
[presentingController dismissSemiModalViewWithCompletion:completion];
return;
}

// Correct target for dismissal
UIView * target = [self parentTarget];
UIView * modal = [target viewWithTag:kSemiModalModalViewTag];
UIView * overlay = [target viewWithTag:kSemiModalOverlayTag];
NSUInteger transitionStyle = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.transitionStyle] unsignedIntegerValue];
NSTimeInterval duration = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.animationDuration] doubleValue];
UIViewController *vc = objc_getAssociatedObject(self, kSemiModalViewController);
KNTransitionCompletionBlock dismissBlock = objc_getAssociatedObject(self, kSemiModalDismissBlock);
// Child controller containment
[vc willMoveToParentViewController:nil];
if ([vc respondsToSelector:@selector(beginAppearanceTransition:animated:)]) {
[vc beginAppearanceTransition:NO animated:YES]; // iOS 6
}
NSUInteger transitionStyle = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.transitionStyle] unsignedIntegerValue];
NSTimeInterval duration = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.animationDuration] doubleValue];
UIViewController *vc = objc_getAssociatedObject(self, kSemiModalViewController);
KNTransitionCompletionBlock dismissBlock = objc_getAssociatedObject(self, kSemiModalDismissBlock);
// Child controller containment
[vc willMoveToParentViewController:nil];
if ([vc respondsToSelector:@selector(beginAppearanceTransition:animated:)]) {
[vc beginAppearanceTransition:NO animated:YES]; // iOS 6
}
[UIView animateWithDuration:duration animations:^{
if (transitionStyle == KNSemiModalTransitionStyleSlideUp) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad){
Expand Down Expand Up @@ -374,9 +387,9 @@ -(void)dismissSemiModalViewWithCompletion:(void (^)(void))completion {

// Begin overlay animation
UIImageView * ss = (UIImageView*)[overlay.subviews objectAtIndex:0];
if ([[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.pushParentBack] boolValue]) {
[ss.layer addAnimation:[self animationGroupForward:NO] forKey:@"bringForwardAnimation"];
}
if ([[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.pushParentBack] boolValue]) {
[ss.layer addAnimation:[self animationGroupForward:NO] forKey:@"bringForwardAnimation"];
}
[UIView animateWithDuration:duration animations:^{
ss.alpha = 1;
} completion:^(BOOL finished) {
Expand All @@ -401,8 +414,8 @@ - (void)resizeSemiView:(CGSize)newSize {
UIButton * button = (UIButton*)[overlay viewWithTag:kSemiModalDismissButtonTag];
CGRect bf = button.frame;
bf.size.height = overlay.frame.size.height - newSize.height;
NSTimeInterval duration = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.animationDuration] doubleValue];
[UIView animateWithDuration:duration animations:^{
NSTimeInterval duration = [[self ym_optionOrDefaultForKey:KNSemiModalOptionKeys.animationDuration] doubleValue];
[UIView animateWithDuration:duration animations:^{
modal.frame = mf;
button.frame = bf;
} completion:^(BOOL finished) {
Expand Down Expand Up @@ -430,18 +443,18 @@ @implementation NSObject (YMOptionsAndDefaults)
static char const * const kYMStandardDefaultsTableName = "YMStandardDefaultsTableName";

- (void)ym_registerOptions:(NSDictionary *)options
defaults:(NSDictionary *)defaults
defaults:(NSDictionary *)defaults
{
objc_setAssociatedObject(self, kYMStandardOptionsTableName, options, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, kYMStandardDefaultsTableName, defaults, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, kYMStandardOptionsTableName, options, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(self, kYMStandardDefaultsTableName, defaults, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)ym_optionOrDefaultForKey:(NSString*)optionKey
{
NSDictionary *options = objc_getAssociatedObject(self, kYMStandardOptionsTableName);
NSDictionary *defaults = objc_getAssociatedObject(self, kYMStandardDefaultsTableName);
NSAssert(defaults, @"Defaults must have been set when accessing options.");
return options[optionKey] ?: defaults[optionKey];
NSDictionary *options = objc_getAssociatedObject(self, kYMStandardOptionsTableName);
NSDictionary *defaults = objc_getAssociatedObject(self, kYMStandardDefaultsTableName);
NSAssert(defaults, @"Defaults must have been set when accessing options.");
return options[optionKey] ?: defaults[optionKey];
}
@end

Expand Down