I'm using ECSlidingViewController in my application to display left menu.
Is there are any way to make main view controller 'darker' when it's moved and left menu is displayed?
Thanks.
I solved this issue.
Just get some classes from example and changed it:
CPSlidingAnimationController:
#import "CPSlidingAnimationController.h"
#import "ECSlidingAnimationController.h"
#import "ECSlidingConstants.h"
#interface CPSlidingAnimationController ()
#property (nonatomic, copy) void (^coordinatorAnimations)(id<UIViewControllerTransitionCoordinatorContext>context);
#property (nonatomic, copy) void (^coordinatorCompletion)(id<UIViewControllerTransitionCoordinatorContext>context);
#end
#implementation CPSlidingAnimationController
#synthesize transition, animationFinishCallback;
#pragma mark - UIViewControllerAnimatedTransitioning
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.25;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
UIViewController *topViewController = [transitionContext viewControllerForKey:ECTransitionContextTopViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
CGRect topViewInitialFrame = [transitionContext initialFrameForViewController:topViewController];
CGRect topViewFinalFrame = [transitionContext finalFrameForViewController:topViewController];
topViewController.view.frame = topViewInitialFrame;
if (topViewController != toViewController) {
CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toViewController];
toViewController.view.frame = toViewFinalFrame;
[containerView insertSubview:toViewController.view belowSubview:topViewController.view];
}
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration
animations:^{
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
if (self.coordinatorAnimations) self.coordinatorAnimations((id<UIViewControllerTransitionCoordinatorContext>)transitionContext);
topViewController.view.frame = topViewFinalFrame;
} completion:^(BOOL finished) {
if ([transitionContext transitionWasCancelled]) {
topViewController.view.frame = [transitionContext initialFrameForViewController:topViewController];
}
if (self.coordinatorCompletion) self.coordinatorCompletion((id<UIViewControllerTransitionCoordinatorContext>)transitionContext);
[transitionContext completeTransition:finished];
if (transition) {
if ([(NSObject*) transition respondsToSelector:animationFinishCallback]) {
[transition performSelector:animationFinishCallback];
}
}
}];
}
#end
MEDynamicTransition.h:
#import "MEDynamicTransition.h"
#import "CPSlidingAnimationController.h"
#interface MEDynamicTransition () {
UIVisualEffectView * effectView;
}
#property (nonatomic, strong) CPSlidingAnimationController * defaultAnimationController;
#property (nonatomic, strong) NSMutableArray *leftEdgeQueue;
#property (nonatomic, assign) id<UIViewControllerContextTransitioning> transitionContext;
#property (nonatomic, strong) UIDynamicAnimator *animator;
#property (nonatomic, strong) UICollisionBehavior *collisionBehavior;
#property (nonatomic, strong) UIGravityBehavior *gravityBehavior;
#property (nonatomic, strong) UIPushBehavior *pushBehavior;
#property (nonatomic, strong) UIDynamicItemBehavior *topViewBehavior;
#property (nonatomic, strong) UIDynamicBehavior *compositeBehavior;
#property (nonatomic, assign) BOOL positiveLeftToRight;
#property (nonatomic, assign) BOOL isPanningRight;
#property (nonatomic, assign) BOOL isInteractive;
#property (nonatomic, assign) CGFloat fullWidth;
#property (nonatomic, assign) CGRect initialTopViewFrame;
#end
#implementation MEDynamicTransition
#pragma mark - ECSlidingViewControllerDelegate
- (id<UIViewControllerAnimatedTransitioning>)slidingViewController:(ECSlidingViewController *)slidingViewController
animationControllerForOperation:(ECSlidingViewControllerOperation)operation
topViewController:(UIViewController *)topViewController {
return self.defaultAnimationController;
}
- (id<UIViewControllerInteractiveTransitioning>)slidingViewController:(ECSlidingViewController *)slidingViewController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController {
self.slidingViewController = slidingViewController;
return self;
}
#pragma mark - Properties
- (CPSlidingAnimationController *)defaultAnimationController {
if (_defaultAnimationController) return _defaultAnimationController;
_defaultAnimationController = [[CPSlidingAnimationController alloc] init];
_defaultAnimationController.transition = self;
_defaultAnimationController.animationFinishCallback = #selector(blurControl);
return _defaultAnimationController;
}
- (NSMutableArray *)leftEdgeQueue {
if (_leftEdgeQueue) return _leftEdgeQueue;
_leftEdgeQueue = [NSMutableArray arrayWithCapacity:5];
return _leftEdgeQueue;
}
- (UIDynamicAnimator *)animator {
if (_animator) return _animator;
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.slidingViewController.view];
_animator.delegate = self;
[_animator updateItemUsingCurrentState:self.slidingViewController.topViewController.view];
return _animator;
}
- (UICollisionBehavior *)collisionBehavior {
if (_collisionBehavior) return _collisionBehavior;
_collisionBehavior = [[UICollisionBehavior alloc] initWithItems:#[self.slidingViewController.topViewController.view]];
CGFloat containerHeight = self.slidingViewController.view.bounds.size.height;
CGFloat containerWidth = self.slidingViewController.view.bounds.size.width;
CGFloat revealAmount = self.slidingViewController.anchorRightRevealAmount;
[_collisionBehavior addBoundaryWithIdentifier:#"LeftEdge" fromPoint:CGPointMake(-1, 0) toPoint:CGPointMake(-1, containerHeight)];
[_collisionBehavior addBoundaryWithIdentifier:#"RightEdge" fromPoint:CGPointMake(revealAmount + containerWidth + 1, 0) toPoint:CGPointMake(revealAmount + containerWidth + 1, containerHeight)];
return _collisionBehavior;
}
- (UIGravityBehavior *)gravityBehavior {
if (_gravityBehavior) return _gravityBehavior;
_gravityBehavior = [[UIGravityBehavior alloc] initWithItems:#[self.slidingViewController.topViewController.view]];
return _gravityBehavior;
}
- (UIPushBehavior *)pushBehavior {
if (_pushBehavior) return _pushBehavior;
_pushBehavior = [[UIPushBehavior alloc] initWithItems:#[self.slidingViewController.topViewController.view] mode:UIPushBehaviorModeInstantaneous];
return _pushBehavior;
}
- (UIDynamicItemBehavior *)topViewBehavior {
if (_topViewBehavior) return _topViewBehavior;
UIView *topView = self.slidingViewController.topViewController.view;
_topViewBehavior = [[UIDynamicItemBehavior alloc] initWithItems:#[topView]];
// the density ranges from 1 to 5 for iPad to iPhone
_topViewBehavior.density = 908800 / (topView.bounds.size.width * topView.bounds.size.height);
_topViewBehavior.elasticity = 0;
_topViewBehavior.resistance = 1;
return _topViewBehavior;
}
- (UIDynamicBehavior *)compositeBehavior {
if (_compositeBehavior) return _compositeBehavior;
_compositeBehavior = [[UIDynamicBehavior alloc] init];
[_compositeBehavior addChildBehavior:self.collisionBehavior];
[_compositeBehavior addChildBehavior:self.gravityBehavior];
[_compositeBehavior addChildBehavior:self.pushBehavior];
[_compositeBehavior addChildBehavior:self.topViewBehavior];
__weak typeof(self)weakSelf = self;
_compositeBehavior.action = ^{
// stop the dynamic animation when the value of the left edge is the same 5 times in a row.
NSNumber *leftEdge = [NSNumber numberWithFloat:weakSelf.slidingViewController.topViewController.view.frame.origin.x];
[weakSelf.leftEdgeQueue insertObject:leftEdge atIndex:0];
if (weakSelf.leftEdgeQueue.count == 6) [weakSelf.leftEdgeQueue removeLastObject];
if (weakSelf.leftEdgeQueue.count == 5 &&
((NSArray *)[weakSelf.leftEdgeQueue valueForKeyPath:#"#distinctUnionOfObjects.self"]).count == 1) {
[weakSelf.animator removeAllBehaviors];
}
};
return _compositeBehavior;
}
#pragma mark - UIViewControllerInteractiveTransitioning
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
self.transitionContext = transitionContext;
UIViewController *topViewController = [transitionContext viewControllerForKey:ECTransitionContextTopViewControllerKey];
topViewController.view.userInteractionEnabled = NO;
if (_isInteractive) {
UIViewController *underViewController = [transitionContext viewControllerForKey:ECTransitionContextUnderLeftControllerKey];
CGRect underViewInitialFrame = [transitionContext initialFrameForViewController:underViewController];
CGRect underViewFinalFrame = [transitionContext finalFrameForViewController:underViewController];
UIView *containerView = [transitionContext containerView];
CGFloat finalLeftEdge = CGRectGetMinX([transitionContext finalFrameForViewController:topViewController]);
CGFloat initialLeftEdge = CGRectGetMinX([transitionContext initialFrameForViewController:topViewController]);
CGFloat fullWidth = fabs(finalLeftEdge - initialLeftEdge);
CGRect underViewFrame;
if (CGRectIsEmpty(underViewInitialFrame)) {
underViewFrame = underViewFinalFrame;
} else {
underViewFrame = underViewInitialFrame;
}
underViewController.view.frame = underViewFrame;
[containerView insertSubview:underViewController.view belowSubview:topViewController.view];
self.positiveLeftToRight = initialLeftEdge < finalLeftEdge;
self.fullWidth = fullWidth;
} else {
[self.defaultAnimationController animateTransition:transitionContext];
}
}
#pragma mark - UIPanGestureRecognizer action
- (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer {
if ([self.animator isRunning]) return;
UIView *topView = self.slidingViewController.topViewController.view;
CGFloat translationX = [recognizer translationInView:self.slidingViewController.view].x;
CGFloat velocityX = [recognizer velocityInView:self.slidingViewController.view].x;
// Blur effect
[self blurControl];
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
BOOL isMovingRight = velocityX > 0;
CALayer *presentationLayer = (CALayer *)topView.layer.presentationLayer;
self.initialTopViewFrame = presentationLayer.frame;
_isInteractive = YES;
if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionCentered && isMovingRight && self.slidingViewController.underLeftViewController) {
[self.slidingViewController anchorTopViewToRightAnimated:YES];
} else if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionCentered && !isMovingRight && self.slidingViewController.underRightViewController) {
[self.slidingViewController anchorTopViewToLeftAnimated:YES];
} else if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionAnchoredLeft) {
[self.slidingViewController resetTopViewAnimated:YES];
} else if (self.slidingViewController.currentTopViewPosition == ECSlidingViewControllerTopViewPositionAnchoredRight) {
[self.slidingViewController resetTopViewAnimated:YES];
} else {
_isInteractive = NO;
}
break;
}
case UIGestureRecognizerStateChanged: {
if (!_isInteractive) return;
CGRect topViewInitialFrame = self.initialTopViewFrame;
CGFloat newLeftEdge = topViewInitialFrame.origin.x + translationX;
if (newLeftEdge < 0) {
newLeftEdge = 0;
} else if (newLeftEdge > self.slidingViewController.anchorRightRevealAmount) {
newLeftEdge = self.slidingViewController.anchorRightRevealAmount;
}
topViewInitialFrame.origin.x = newLeftEdge;
topView.frame = topViewInitialFrame;
if (!self.positiveLeftToRight) translationX = translationX * -1.0;
CGFloat percentComplete = (translationX / self.fullWidth);
if (percentComplete < 0) percentComplete = 0;
if (percentComplete > 100) percentComplete = 100;
[self.transitionContext updateInteractiveTransition:percentComplete];
break;
}
case UIGestureRecognizerStateEnded:
{
[self blurControl];
};
case UIGestureRecognizerStateCancelled: {
if (!_isInteractive) return;
_isInteractive = NO;
self.isPanningRight = velocityX > 0;
if (self.isPanningRight) {
[self blurControl];
} else {
[self disableBlur];
}
self.gravityBehavior.gravityDirection = self.isPanningRight ? CGVectorMake(2, 0) : CGVectorMake(-2, 0);
self.pushBehavior.angle = 0; // velocity may be negative
self.pushBehavior.magnitude = velocityX;
self.pushBehavior.active = YES;
[self.animator addBehavior:self.compositeBehavior];
break;
}
default:
break;
}
}
#pragma mark - UIDynamicAnimatorDelegate
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator*)animator {
[self.animator removeAllBehaviors];
_collisionBehavior = nil;
_topViewBehavior = nil;
_pushBehavior = nil;
_gravityBehavior = nil;
_compositeBehavior = nil;
_animator = nil;
self.slidingViewController.topViewController.view.userInteractionEnabled = YES;
UIViewController *topViewController = [self.transitionContext viewControllerForKey:ECTransitionContextTopViewControllerKey];
if ((self.isPanningRight && self.positiveLeftToRight) || (!self.isPanningRight && !self.positiveLeftToRight)) {
topViewController.view.frame = [self.transitionContext finalFrameForViewController:topViewController];
[self.transitionContext finishInteractiveTransition];
} else if ((self.isPanningRight && !self.positiveLeftToRight) || (!self.isPanningRight && self.positiveLeftToRight)) {
topViewController.view.frame = [self.transitionContext initialFrameForViewController:topViewController];
[self.transitionContext cancelInteractiveTransition];
}
[self.transitionContext completeTransition:YES];
}
#pragma mark - Blur
- (void) setBlurOffset:(CGFloat) offset {
UIView *topView = self.slidingViewController.topViewController.view;
if (!effectView) {
UIBlurEffect * blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
effectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
effectView.frame = topView.bounds;
effectView.alpha = 0.1f;
[topView addSubview:effectView];
[effectView setTranslatesAutoresizingMaskIntoConstraints:false];
[topView addConstraint:[NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
[topView addConstraint:[NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
[topView addConstraint:[NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeLeading multiplier:1 constant:0]];
[topView addConstraint:[NSLayoutConstraint constraintWithItem:effectView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:topView attribute:NSLayoutAttributeTrailing multiplier:1 constant:0]];
}
CGFloat c = offset / 100;
if (c < 0.1f) c = 0.1f; else if (c > 0.5f) c = 0.5f;
effectView.alpha = c;
}
- (void) disableBlur{
if (effectView != NULL) {
[effectView removeFromSuperview];
effectView = NULL;
}
}
- (void) blurControl{
UIView *topView = self.slidingViewController.topViewController.view;
NSLog(#"X: %f", topView.frame.origin.x);
if (topView.frame.origin.x >= 15) {
[self setBlurOffset:topView.frame.origin.x];
} else {
[self disableBlur];
}
}
#end
Related
I need to write a scanner on the ios/react-native app. Before this project has used another scanner, but that now doesn't work. I have UIView and UIViewController. In the built app, the camera is showing, but when I aim the camera on the barcode, nothing happens, captureResult does not call. So I am new to IOS and don't know how to fix it.
MyView.h
#import <UIKit/UIView.h>
#import <ZXingObjC/ZXingObjC.h>
#import <React/RCTComponent.h>
#import "MyViewController.h"
#interface MyView: UIView <ZXCaptureDelegate>
#property (nonatomic, strong) MyViewController *scannerController;
#property (nonatomic, copy) RCTBubblingEventBlock onClose;
#property (nonatomic, copy) RCTBubblingEventBlock onCaptureVin;
#property (nonatomic, copy) RCTBubblingEventBlock onEnterVinManuallyPress;
#end
MyView.m
#import "MyView.h"
#import "MyViewController.h"
#import <UIKit/UIKit.h>
#import <AVKit/AVKit.h>
#import <sys/utsname.h>
UIColor *fillColor;
UIButton *flashButton;
#implementation MyView
int additionalHeightIphoneX = 34;
//===============//
// INITIALIZE SDK
//===============//
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
self.scannerController = [[MyViewController alloc] initWithNibName: nil bundle: nil];
[self.scannerController.capture setDelegate: self];
[self.scannerController.capture start];
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self.subviews makeObjectsPerformSelector: #selector(removeFromSuperview)];
[self addSubview:self.scannerController.view];
self.scannerController.view.frame = self.bounds;
fillColor = [UIColor colorWithRed:29/255.0f green:41/255.0f blue:50/255.0f alpha:0.8];
[self createHeader];
[self createTarget];
[self createManualVinEntryButton];
[self createCancelButton];
}
- (void)captureResult:(ZXCapture *)capture result:(ZXResult *)result {
UILabel* label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:36];
label.text = result.text;
label.textColor = [UIColor yellowColor];
label.backgroundColor =
[[UIColor blackColor] colorWithAlphaComponent:0.5];
[label sizeToFit];
label.center = CGPointMake(self.bounds.size.width/2,
self.bounds.size.height/2);
[self addSubview:label];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationDelegate:label];
[UIView setAnimationDidStopSelector:#selector(removeFromSuperview)];
label.alpha = 0;
[UIView commitAnimations];
[self sendVinCode: result.text]
}
//========================//
// REACT CALLBACK FUNCTION
//========================//
- (void) sendVinCode: (NSString *)code {
if (!self.onCaptureVin) {
return;
}
self.onCaptureVin(#{#"vin": code});
}
- (void) cancelVinScan {
if (!self.onClose) {
return;
}
self.onClose(#{});
}
- (void) requestManualVinEntry {
if (!self.onEnterVinManuallyPress) {
return;
}
self.onEnterVinManuallyPress(#{});
}
//================//
// BUTTON HANDLERS
//================//
- (void) manualVinEntryButtonHandler:(UIButton *)button {
[self requestManualVinEntry];
}
- (void) cancelButtonHandler:(UIButton *)button {
[self cancelVinScan];
}
//==========//
// CUSTOM UI
//==========//
- (void) createHeader {
UIView *headerContainer = [[UIView alloc] init];
headerContainer.translatesAutoresizingMaskIntoConstraints = NO;
[headerContainer setBackgroundColor: [UIColor colorWithWhite:1 alpha:0.7]];
UILabel *scanVinMessage = [[UILabel alloc] initWithFrame:CGRectMake(22.2, 18.0, 183.5, 21)];
[scanVinMessage setText:#"Scan VIN"];
[scanVinMessage setTextAlignment:NSTextAlignmentCenter];
[scanVinMessage setTextColor:[UIColor darkGrayColor]];
[scanVinMessage setBackgroundColor:[UIColor clearColor]];
[scanVinMessage setFont:[UIFont systemFontOfSize:17]];
[scanVinMessage setCenter:CGPointMake(self.frame.size.width / 2, 27.5)];
[headerContainer addSubview:scanVinMessage];
[self addSubview:headerContainer];
// [headerContainer.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:0.0].active = YES;
// [headerContainer.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:0.0].active = YES;
// [headerContainer.heightAnchor constraintEqualToConstant:55.0].active = YES;
}
- (void) createTarget {
UIView *scanLine = [[UIView alloc] init];
scanLine.translatesAutoresizingMaskIntoConstraints = NO;
[scanLine setBackgroundColor:[UIColor redColor]];
[self addSubview:scanLine];
// [scanLine.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES;
// [scanLine.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:25.0].active = YES;
// [scanLine.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-25.0].active = YES;
// [scanLine.heightAnchor constraintEqualToConstant:4.0].active = YES;
UIView *topLeftTop = [[UIView alloc] init];
topLeftTop.translatesAutoresizingMaskIntoConstraints = NO;
[topLeftTop setBackgroundColor:[UIColor redColor]];
[self addSubview:topLeftTop];
// [topLeftTop.bottomAnchor constraintEqualToAnchor:self.centerYAnchor constant:-34.0].active = YES;
// [topLeftTop.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:15.0].active = YES;
// [topLeftTop.heightAnchor constraintEqualToConstant:4.0].active = YES;
// [topLeftTop.widthAnchor constraintEqualToConstant:24.0].active = YES;
UIView *topLeftLeft = [[UIView alloc] init];
topLeftLeft.translatesAutoresizingMaskIntoConstraints = NO;
[topLeftLeft setBackgroundColor:[UIColor redColor]];
[self addSubview:topLeftLeft];
// [topLeftLeft.bottomAnchor constraintEqualToAnchor:self.centerYAnchor constant:-14.0].active = YES;
// [topLeftLeft.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:15.0].active = YES;
// [topLeftLeft.heightAnchor constraintEqualToConstant:20.0].active = YES;
// [topLeftLeft.widthAnchor constraintEqualToConstant:4.0].active = YES;
UIView *topRightTop = [[UIView alloc] init];
topRightTop.translatesAutoresizingMaskIntoConstraints = NO;
[topRightTop setBackgroundColor:[UIColor redColor]];
[self addSubview:topRightTop];
// [topRightTop.bottomAnchor constraintEqualToAnchor:self.centerYAnchor constant:-34.0].active = YES;
// [topRightTop.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-15.0].active = YES;
// [topRightTop.heightAnchor constraintEqualToConstant:4.0].active = YES;
// [topRightTop.widthAnchor constraintEqualToConstant:24.0].active = YES;
UIView *topRightRight = [[UIView alloc] init];
topRightRight.translatesAutoresizingMaskIntoConstraints = NO;
[topRightRight setBackgroundColor:[UIColor redColor]];
[self addSubview:topRightRight];
// [topRightRight.bottomAnchor constraintEqualToAnchor:self.centerYAnchor constant:-14.0].active = YES;
// [topRightRight.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-15.0].active = YES;
// [topRightRight.heightAnchor constraintEqualToConstant:20.0].active = YES;
// [topRightRight.widthAnchor constraintEqualToConstant:4.0].active = YES;
UIView *bottomLeftBottom = [[UIView alloc] init];
bottomLeftBottom.translatesAutoresizingMaskIntoConstraints = NO;
[bottomLeftBottom setBackgroundColor:[UIColor redColor]];
[self addSubview:bottomLeftBottom];
// [bottomLeftBottom.topAnchor constraintEqualToAnchor:self.centerYAnchor constant:34.0].active = YES;
// [bottomLeftBottom.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:15.0].active = YES;
// [bottomLeftBottom.heightAnchor constraintEqualToConstant:4.0].active = YES;
// [bottomLeftBottom.widthAnchor constraintEqualToConstant:24.0].active = YES;
UIView *bottomLeftLeft = [[UIView alloc] init];
bottomLeftLeft.translatesAutoresizingMaskIntoConstraints = NO;
[bottomLeftLeft setBackgroundColor:[UIColor redColor]];
[self addSubview:bottomLeftLeft];
// [bottomLeftLeft.topAnchor constraintEqualToAnchor:self.centerYAnchor constant:14.0].active = YES;
// [bottomLeftLeft.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:15.0].active = YES;
// [bottomLeftLeft.heightAnchor constraintEqualToConstant:20.0].active = YES;
// [bottomLeftLeft.widthAnchor constraintEqualToConstant:4.0].active = YES;
UIView *bottomRightBottom = [[UIView alloc] init];
bottomRightBottom.translatesAutoresizingMaskIntoConstraints = NO;
[bottomRightBottom setBackgroundColor:[UIColor redColor]];
[self addSubview:bottomRightBottom];
// [bottomRightBottom.topAnchor constraintEqualToAnchor:self.centerYAnchor constant:34.0].active = YES;
// [bottomRightBottom.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-15.0].active = YES;
// [bottomRightBottom.heightAnchor constraintEqualToConstant:4.0].active = YES;
// [bottomRightBottom.widthAnchor constraintEqualToConstant:24.0].active = YES;
UIView *bottomRightRight = [[UIView alloc] init];
bottomRightRight.translatesAutoresizingMaskIntoConstraints = NO;
[bottomRightRight setBackgroundColor:[UIColor redColor]];
[self addSubview:bottomRightRight];
// [bottomRightRight.topAnchor constraintEqualToAnchor:self.centerYAnchor constant:14.0].active = YES;
// [bottomRightRight.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-15.0].active = YES;
// [bottomRightRight.heightAnchor constraintEqualToConstant:24.0].active = YES;
// [bottomRightRight.widthAnchor constraintEqualToConstant:4.0].active = YES;
}
- (void) createCancelButton {
CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
UIButton *cancelButton = [[UIButton alloc] init];
[cancelButton addTarget:self action:#selector(cancelButtonHandler:) forControlEvents:UIControlEventTouchUpInside];
float buttonSize = 43.0;
float margin = 20.0;
[cancelButton setFrame:CGRectMake(screenWidth - buttonSize - margin, screenHeight - buttonSize - margin, buttonSize, buttonSize)];
[cancelButton setBackgroundImage:[UIImage imageNamed:#"vin-scan-cancel"]
forState:UIControlStateNormal];
[self addSubview:cancelButton];
}
- (void) createManualVinEntryButton {
CGFloat screenHeight = [[UIScreen mainScreen] bounds].size.height;
UIButton *manualVinEntryButton = [[UIButton alloc] init];
[manualVinEntryButton addTarget:self action:#selector(manualVinEntryButtonHandler:) forControlEvents:UIControlEventTouchUpInside];
[manualVinEntryButton setTitle:#"Enter VIN Manually" forState:UIControlStateNormal];
[manualVinEntryButton sizeToFit];
float buttonHeight = 43.0;
float margin = 20.0;
[manualVinEntryButton setFrame:CGRectMake(margin, screenHeight - buttonHeight - margin, 200, buttonHeight)];
[manualVinEntryButton setTitleColor:[UIColor darkGrayColor] forState:UIControlStateNormal];
[[manualVinEntryButton layer] setCornerRadius:21];
[[manualVinEntryButton layer]setBackgroundColor: [[UIColor colorWithWhite:1 alpha:.7] CGColor]];
[self addSubview:manualVinEntryButton];
}
//========//
// UTILITY
//========//
- (BOOL) isIphoneX {
struct utsname systemInfo;
uname(&systemInfo);
NSString* deviceModel = [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
return [deviceModel isEqualToString:#"iPhone10,3"] ||
[deviceModel isEqualToString:#"iPhone10,6"] ||
[deviceModel isEqualToString:#"iPhone11,2"] ||
[deviceModel isEqualToString:#"iPhone11,8"] ||
[deviceModel isEqualToString:#"iPhone11,4"] ||
[deviceModel isEqualToString:#"iPhone12,1"] ||
[deviceModel isEqualToString:#"iPhone12,3"] ||
[deviceModel isEqualToString:#"iPhone12,5"] ||
[deviceModel isEqualToString:#"iPad8,5"];
};
- (void)applyOrientation {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
float scanRectRotation;
float captureRotation;
switch (orientation) {
case UIInterfaceOrientationPortrait:
captureRotation = 0;
scanRectRotation = 90;
break;
case UIInterfaceOrientationLandscapeLeft:
captureRotation = 90;
scanRectRotation = 180;
break;
case UIInterfaceOrientationLandscapeRight:
captureRotation = 270;
scanRectRotation = 0;
break;
case UIInterfaceOrientationPortraitUpsideDown:
captureRotation = 180;
scanRectRotation = 270;
break;
default:
captureRotation = 0;
scanRectRotation = 90;
break;
}
self.scannerController.capture.layer.frame = self.frame;
CGAffineTransform transform = CGAffineTransformMakeRotation((CGFloat) (captureRotation / 180 * M_PI));
[self.scannerController.capture setTransform:transform];
[self.scannerController.capture setRotation:scanRectRotation];
[self.scannerController applyRectOfInterest:orientation];
}
#end
MyViewController.h
#import <UIKit/UIViewController.h>
#import <ZXingObjC/ZXingObjC.h>
#interface MyViewController: UIViewController
#property (nonatomic, strong) ZXCapture *capture;
#property (nonatomic, weak) IBOutlet UIView *scanRectView;
#property (nonatomic, weak) IBOutlet UILabel *decodedLabel;
#property (nonatomic) BOOL scanning;
#property (nonatomic) BOOL isFirstApplyOrientation;
#property (nonatomic) CGAffineTransform _captureSizeTransform;
- (void)applyRectOfInterest:(UIInterfaceOrientation)orientation;
#end
MyViewController.m
#import "MyViewController.h"
#interface MyViewController()
#end
#implementation MyViewController
//===============//
// INITIALIZE SDK
//===============//
- (instancetype)init {
self = [super init];
if (self) {
// Custom initialization
self.capture = [[ZXCapture alloc] init];
self.capture.sessionPreset = AVCaptureSessionPreset1920x1080;
self.capture.camera = self.capture.back;
self.capture.focusMode = AVCaptureFocusModeContinuousAutoFocus;
self.scanning = NO;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// self.capture = [[ZXCapture alloc] init];
// self.capture.sessionPreset = AVCaptureSessionPreset1920x1080;
// self.capture.camera = self.capture.back;
// self.capture.focusMode = AVCaptureFocusModeContinuousAutoFocus;
// self.scanning = YES;
// [self.capture start];
[self.view.layer addSublayer:self.capture.layer];
// [self.view bringSubviewToFront:self.scanRectView];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (self.isFirstApplyOrientation) return;
self.isFirstApplyOrientation = YES;
[self applyOrientation];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
//========//
// CLEANUP
//========//
- (void)dealloc {
[self.capture.layer removeFromSuperlayer];
}
//=======================//
// ROTATION AND TRANSFORM
//=======================//
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
}
- (BOOL)shouldAutorotate {
return YES;
}
- (void)applyRectOfInterest:(UIInterfaceOrientation)orientation {
CGFloat scaleVideoX, scaleVideoY;
CGFloat videoSizeX, videoSizeY;
CGRect transformedVideoRect = self.scanRectView.frame;
if([self.capture.sessionPreset isEqualToString:AVCaptureSessionPreset1920x1080]) {
videoSizeX = 1080;
videoSizeY = 1920;
} else {
videoSizeX = 720;
videoSizeY = 1280;
}
if(UIInterfaceOrientationIsPortrait(orientation)) {
scaleVideoX = self.capture.layer.frame.size.width / videoSizeX;
scaleVideoY = self.capture.layer.frame.size.height / videoSizeY;
// Convert CGPoint under portrait mode to map with orientation of image
// because the image will be cropped before rotate
// reference: https://github.com/TheLevelUp/ZXingObjC/issues/222
CGFloat realX = transformedVideoRect.origin.y;
CGFloat realY = self.capture.layer.frame.size.width - transformedVideoRect.size.width - transformedVideoRect.origin.x;
CGFloat realWidth = transformedVideoRect.size.height;
CGFloat realHeight = transformedVideoRect.size.width;
transformedVideoRect = CGRectMake(realX, realY, realWidth, realHeight);
} else {
scaleVideoX = self.capture.layer.frame.size.width / videoSizeY;
scaleVideoY = self.capture.layer.frame.size.height / videoSizeX;
}
// _captureSizeTransform = CGAffineTransformMakeScale(1.0/scaleVideoX, 1.0/scaleVideoY);
self.capture.scanRect = CGRectApplyAffineTransform(transformedVideoRect, CGAffineTransformMakeScale(1.0/scaleVideoX, 1.0/scaleVideoY));
}
//========//
// UTILITY
//========//
- (NSString *)barcodeFormatToString:(ZXBarcodeFormat)format {
switch (format) {
case kBarcodeFormatAztec:
return #"Aztec";
case kBarcodeFormatCodabar:
return #"CODABAR";
case kBarcodeFormatCode39:
return #"Code 39";
case kBarcodeFormatCode93:
return #"Code 93";
case kBarcodeFormatCode128:
return #"Code 128";
case kBarcodeFormatDataMatrix:
return #"Data Matrix";
case kBarcodeFormatEan8:
return #"EAN-8";
case kBarcodeFormatEan13:
return #"EAN-13";
case kBarcodeFormatITF:
return #"ITF";
case kBarcodeFormatPDF417:
return #"PDF417";
case kBarcodeFormatQRCode:
return #"QR Code";
case kBarcodeFormatRSS14:
return #"RSS 14";
case kBarcodeFormatRSSExpanded:
return #"RSS Expanded";
case kBarcodeFormatUPCA:
return #"UPCA";
case kBarcodeFormatUPCE:
return #"UPCE";
case kBarcodeFormatUPCEANExtension:
return #"UPC/EAN extension";
default:
return #"Unknown";
}
}
- (void)captureCameraIsReady:(ZXCapture *)capture {
self.scanning = YES;
}
- (void)applyOrientation {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
float scanRectRotation;
float captureRotation;
switch (orientation) {
case UIInterfaceOrientationPortrait:
captureRotation = 0;
scanRectRotation = 90;
break;
case UIInterfaceOrientationLandscapeLeft:
captureRotation = 90;
scanRectRotation = 180;
break;
case UIInterfaceOrientationLandscapeRight:
captureRotation = 270;
scanRectRotation = 0;
break;
case UIInterfaceOrientationPortraitUpsideDown:
captureRotation = 180;
scanRectRotation = 270;
break;
default:
captureRotation = 0;
scanRectRotation = 90;
break;
}
self.capture.layer.frame = self.view.frame;
CGAffineTransform transform = CGAffineTransformMakeRotation((CGFloat) (captureRotation / 180 * M_PI));
[self.capture setTransform:transform];
[self.capture setRotation:scanRectRotation];
[self applyRectOfInterest:orientation];
}
#end
I created a custom UIButton subclassing from UIControl (It is called "OZCustomButton"). It works fine in Storyboard, however when I was trying to use it to replace the leftBarButtonItem back button programmatically, it had a problem with its layout.
Here is the code I use to replace the leftBarButtonItem.
OZCustomButton *customBackButton = [[OZCustomButton alloc] initWithFrame:CGRectZero];
customBackButton.buttonImage = [UIImage imageNamed:#"BackArrow"];
customBackButton.buttonText = #"Back";
[customBackButton sizeToFit];
NSLog(#"this size: %#", NSStringFromCGRect(customBackButton.frame));
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:customBackButton];
self.navigationItem.leftBarButtonItem = item;
But nothing is showing on the left side of the navigation bar.
Navigation bar:
And the Dubug View Hierarchy tool shows these warning messages:
It seems the customBackButton is in the View Hierarchy, but the layout is not correct.
This is the code for my OZCustomButton.
OZCustomButton.h:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
IB_DESIGNABLE
#interface OZCustomButton : UIControl <NSCoding>
#property (assign, nonatomic) IBInspectable CGFloat borderWidth;
#property (assign, nonatomic) IBInspectable CGFloat borderRadius;
#property (strong, nonatomic) IBInspectable UIColor *borderColor;
#property (strong, nonatomic) IBInspectable UIColor *fillColor;
#property (strong, nonatomic) IBInspectable UIColor *tintColor;
#property (strong, nonatomic) IBInspectable NSString *buttonText;
#property (assign, nonatomic) IBInspectable CGFloat textSize;
#property (assign, nonatomic) IBInspectable BOOL isTextBold;
#property (strong, nonatomic) UIFont *textFont;
#property (nullable, strong, nonatomic) IBInspectable UIImage *buttonImage;
#property (nullable, strong, nonatomic) NSArray *gradientColors;
#end
NS_ASSUME_NONNULL_END
OZCustomButton.m
#import "OZCustomButton.h"
#import "UIColor+Custom.h"
#import "CAGradientLayer+Utilities.h"
#interface OZCustomButton ()
#property (strong, nonatomic) UILabel *buttonLabel;
#property (nullable, strong, nonatomic) UIImageView *buttonImageView;
#property (nullable, strong, nonatomic) CAGradientLayer *gradientLayer;
#property (nullable, strong, nonatomic) UIStackView *stackView;
#end
#implementation OZCustomButton
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupDefaults];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setupDefaults];
}
return self;
}
- (void)layoutLabelAndImageView {
_buttonLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_buttonLabel.numberOfLines = 1; // need to set to 1
_buttonLabel.textAlignment = NSTextAlignmentCenter;
//_buttonLabel.backgroundColor = [UIColor redColor];
_buttonImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
_buttonImageView.contentMode = UIViewContentModeScaleAspectFit;
//_buttonImageView.backgroundColor = [UIColor redColor];
_stackView = [[UIStackView alloc] init];
_stackView.axis = UILayoutConstraintAxisHorizontal;
_stackView.alignment = UIStackViewAlignmentCenter;
_stackView.distribution = UIStackViewDistributionFillProportionally;
_stackView.spacing = 8;
_stackView.userInteractionEnabled = NO;
[_stackView addArrangedSubview:_buttonImageView];
[_stackView addArrangedSubview:_buttonLabel];
_stackView.translatesAutoresizingMaskIntoConstraints = false;
[self addSubview:_stackView];
[[_stackView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor] setActive:YES];
[[_stackView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor] setActive:YES];
}
- (void)layoutGradientLayer {
_gradientLayer = [CAGradientLayer createGradientLayerWithBounds:self.bounds
colors:nil
direction:GradientFromLeftTopToRightBottom
locations:#[#0.0, #1.0]];
_gradientLayer.anchorPoint = CGPointMake(0, 0);
[self.layer insertSublayer:_gradientLayer below:_stackView.layer];
}
- (void)setupDefaults {
_borderWidth = 0.0f;
_borderRadius = 0.0f;
_borderColor = [UIColor blackColor];
_fillColor = [UIColor whiteColor];
_buttonText = #"Button";
_tintColor = [UIColor blackColor];
_textSize = 17.0f;
_isTextBold = false;
_textFont = _isTextBold ? [UIFont fontWithName:#"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:#"AlteHaasGrotesk" size:_textSize];
_gradientColors = nil;
_buttonImage = nil;
[self layoutLabelAndImageView];
[self updateView];
}
- (void)updateView {
self.layer.borderColor = _borderColor.CGColor;
self.layer.borderWidth = _borderWidth;
self.layer.cornerRadius = _borderRadius;
self.layer.masksToBounds = true;
self.layer.backgroundColor = _fillColor.CGColor;
// update button text label
_buttonLabel.text = _buttonText;
_buttonLabel.textColor = _tintColor;
_textFont = _isTextBold ? [UIFont fontWithName:#"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:#"AlteHaasGrotesk" size:_textSize];
_buttonLabel.font = _textFont;
_buttonLabel.textAlignment = NSTextAlignmentCenter;
[_buttonLabel sizeToFit];
// update button image
if (_buttonImage != nil) {
_buttonImageView.hidden = NO;
_buttonImageView.image = [_buttonImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
_buttonImageView.tintColor = _tintColor;
[_buttonImageView sizeToFit];
} else {
_buttonImageView.image = nil;
_buttonImageView.hidden = YES;
[_buttonImageView sizeToFit];
}
// update gradient layer
if (_gradientColors != nil) {
// if gradient layer is not initialized, call layoutGradientLayer()
if (_gradientLayer == nil) {
[self layoutGradientLayer];
}
// transform the UIColor to CGColorRef
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:_gradientColors.count];
for (UIColor *color in _gradientColors) {
[colors addObject:(id)[color CGColor]];
}
if (colors.count > 0) {
_gradientLayer.colors = [colors copy];
}
}
}
#pragma mark - setters
- (void)setTextSize:(CGFloat)textSize {
if (_textSize != textSize) {
_textSize = textSize;
_textFont = _isTextBold ? [UIFont fontWithName:#"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:#"AlteHaasGrotesk" size:_textSize];
[self updateView];
}
}
- (void)setBorderColor:(UIColor *)borderColor {
if (_borderColor != borderColor) {
_borderColor = borderColor;
[self updateView];
}
}
- (void)setBorderWidth:(CGFloat)borderWidth {
if (_borderWidth != borderWidth) {
_borderWidth = borderWidth;
[self updateView];
}
}
- (void)setBorderRadius:(CGFloat)borderRadius {
if (_borderRadius != borderRadius) {
_borderRadius = borderRadius;
[self updateView];
}
}
- (void)setFillColor:(UIColor *)fillColor {
if (_fillColor != fillColor) {
_fillColor = fillColor;
[self updateView];
}
}
- (void)setTintColor:(UIColor *)tintColor {
if (_tintColor != tintColor) {
_tintColor = tintColor;
[self updateView];
}
}
- (void)setButtonText:(NSString *)buttonText {
if (_buttonText != buttonText) {
_buttonText = buttonText;
[self updateView];
}
}
- (void)setTextFont:(UIFont *)textFont {
if (_textFont != textFont) {
_textFont = textFont;
[self updateView];
}
}
- (void)setGradientColors:(NSArray *)gradientColors {
if (_gradientColors != gradientColors) {
_gradientColors = gradientColors;
[self updateView];
}
}
- (void)setButtonImage:(UIImage *)buttonImage {
if (_buttonImage != buttonImage) {
_buttonImage = buttonImage;
[self updateView];
}
}
- (void)setIsTextBold:(BOOL)isTextBold {
if (_isTextBold != isTextBold) {
_isTextBold = isTextBold;
[self updateView];
}
}
#pragma mark - UIControl actions
- (void)setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
fadeAnimation.duration = 0.2f;
if (highlighted) {
fadeAnimation.toValue = #0.6f;
} else {
fadeAnimation.toValue = #1.0f;
}
self.buttonLabel.layer.opacity = [fadeAnimation.toValue floatValue];
self.layer.opacity = [fadeAnimation.toValue floatValue];
[self.buttonLabel.layer addAnimation:fadeAnimation forKey:#"textFadeAnimation"];
[self.layer addAnimation:fadeAnimation forKey:#"backgroundFadeAnimation"];
}
- (void)setEnabled:(BOOL)enabled {
[super setEnabled:enabled];
if (enabled) {
self.layer.backgroundColor = self.fillColor.CGColor;
self.buttonLabel.textColor = self.tintColor;
} else {
self.layer.backgroundColor = [UIColor lighterColorForColor:self.fillColor].CGColor;
self.buttonLabel.textColor = [UIColor lightGrayColor];
}
}
#pragma mark - Override functions
- (void)layoutSubviews {
[super layoutSubviews];
[self updateView];
}
- (CGSize)sizeThatFits:(CGSize)size {
CGFloat minWidth = _buttonImageView.frame.size.width + 8 + _buttonLabel.frame.size.width;
CGFloat minHeight = MAX(_buttonImageView.frame.size.height, _buttonLabel.frame.size.height);
return CGSizeMake(minWidth + 6, minHeight + 4);
}
#end
I found one solution. Instead of setting the centenX anchor and centerY anchor, I change it to using left, right, top and bottom anchors:
[[_stackView.topAnchor constraintEqualToAnchor:_stackView.superview.topAnchor] setActive:YES];
[[_stackView.bottomAnchor constraintEqualToAnchor:_stackView.superview.bottomAnchor] setActive:YES];
[[_stackView.leftAnchor constraintEqualToAnchor:_stackView.superview.leftAnchor] setActive:YES];
[[_stackView.rightAnchor constraintEqualToAnchor:_stackView.superview.rightAnchor] setActive:YES];
Then it got the layout correctly.
If you have any other methods to solve the problem, please let me know.
I have added a custom cell as follows for stepper progress. UI perspective, it looks what I want, but I could not able to figure out how I could able to determine whether or not button has been clicked.
I have inspired via https://github.com/yenbekbay/AYStepperView, but this one has PageViewController which I could not able to add it in the tableCell.
#import "StepperProgressTableViewCell.h"
#import "AYStepperView.h"
static CGFloat const kFormStepperViewHeight = 80;
#interface StepperProgressTableViewCell ()
#property (nonatomic) AYStepperView *stepperView;
#property (nonatomic) NSUInteger currentIndex;
#property (nonatomic) NSUInteger currentStep;
#end
#implementation StepperProgressTableViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
[self setUpViews];
self.currentIndex = 0;
self.currentStep = 0;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
}
#pragma mark Private
- (void)setUpViews {
self.stepperView = [[AYStepperView alloc]initWithFrame:CGRectMake(0, 40 , self.frame.size.width, kFormStepperViewHeight)
titles:#[NSLocalizedString(#"Start", nil),
NSLocalizedString(#"Cooking", nil),
NSLocalizedString(#"Ready", nil)]];
self.stepperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
self.stepperView.userInteractionEnabled = YES;
[self addSubview:self.stepperView];
self.containerView = [[UIView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.stepperView.frame), CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds) - CGRectGetMaxY(self.stepperView.frame))];
[self addSubview:self.containerView];
}
#end
AYStepperView.m
#import "AYStepperView.h"
#import <pop/POP.h>
static UIEdgeInsets const kStepperViewPadding = {
15, 0, 15, 0
};
static CGFloat const kStepperLabelsSpacing = 10;
static CGFloat const kStepperPipeHeight = 5;
#interface AYStepperView ()
#property (nonatomic) UIView *pipeView;
#property (nonatomic) UIView *labelsView;
#property (nonatomic) UIView *pipeBackgroundView;
#property (nonatomic) UIView *pipeFillView;
#property (nonatomic) NSMutableArray *stepLabels;
#end
#implementation AYStepperView
#pragma mark Initialization
- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray *)titles {
self = [super initWithFrame:frame];
if (!self) {
return nil;
}
_titles = titles;
self.backgroundColor = [UIColor colorWithRed:0.98f green:0.98f blue:0.98f alpha:1];
self.tintColor = [UIColor colorWithRed:0.2f green:0.29f blue:0.37f alpha:1];
self.pipeView = [[UIView alloc] initWithFrame:CGRectMake(kStepperViewPadding.left, kStepperViewPadding.top, CGRectGetWidth(self.bounds) - kStepperViewPadding.left - kStepperViewPadding.right, CGRectGetHeight(self.bounds) / 2 - kStepperViewPadding.top)];
[self addSubview:self.pipeView];
self.labelsView = [[UIView alloc] initWithFrame:CGRectMake(kStepperViewPadding.left, CGRectGetMaxY(self.pipeView.frame) + kStepperViewPadding.top, CGRectGetWidth(self.bounds) - kStepperViewPadding.left - kStepperViewPadding.right, CGRectGetHeight(self.bounds) / 2 - kStepperViewPadding.top - kStepperViewPadding.bottom)];
[self addSubview:self.labelsView];
self.pipeBackgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, (CGRectGetHeight(self.pipeView.bounds) - kStepperPipeHeight) / 2, CGRectGetWidth(self.pipeView.bounds), kStepperPipeHeight)];
self.pipeBackgroundView.backgroundColor = [UIColor lightGrayColor];
[self.pipeView addSubview:self.pipeBackgroundView];
CGRect pipeFillViewFrame = self.pipeBackgroundView.frame;
pipeFillViewFrame.size.width = 0;
self.pipeFillView = [[UIView alloc] initWithFrame:pipeFillViewFrame];
self.pipeFillView.backgroundColor = self.tintColor;
[self.pipeView addSubview:self.pipeFillView];
_stepButtons = [NSMutableArray new];
_stepLabels = [NSMutableArray new];
for (NSUInteger i = 0; i < titles.count; i++) {
UIButton *stepButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, CGRectGetHeight(self.pipeView.bounds), CGRectGetHeight(self.pipeView.bounds))];
stepButton.center = CGPointMake(CGRectGetWidth(self.pipeView.bounds) * (i + 0.5f) / titles.count, stepButton.center.y);
stepButton.clipsToBounds = YES;
stepButton.tag = i;
stepButton.layer.cornerRadius = CGRectGetHeight(stepButton.bounds) / 2;
stepButton.backgroundColor = [UIColor lightGrayColor];
[self.pipeView addSubview:stepButton];
[self.stepButtons addObject:stepButton];
UILabel *stepLabel = [UILabel new];
stepLabel.font = [UIFont systemFontOfSize:[UIFont smallSystemFontSize]];
stepLabel.textColor = self.tintColor;
stepLabel.textAlignment = NSTextAlignmentCenter;
stepLabel.text = titles[i];
stepLabel.numberOfLines = 0;
stepLabel.frame = (CGRect) {
stepLabel.frame.origin, [stepLabel sizeThatFits:CGSizeMake(CGRectGetWidth(self.pipeView.bounds) / titles.count - kStepperLabelsSpacing, 0)]
};
stepLabel.center = CGPointMake(CGRectGetWidth(self.labelsView.bounds) * (i + 0.5f) / titles.count, CGRectGetHeight(self.labelsView.bounds) / 2);
[self.labelsView addSubview:stepLabel];
[self.stepLabels addObject:stepLabel];
}
_currentStepIndex = 0;
[self completeStepAtIndex:0 until:1 completionBlock:nil];
return self;
}
#pragma mark Public
- (void)updateCurrentStepIndex:(NSUInteger)currentStepIndex completionBlock:(void (^)())completionBlock {
if (currentStepIndex >= self.titles.count || currentStepIndex == self.currentStepIndex) {
if (completionBlock) {
completionBlock();
}
} else {
NSUInteger previousStepIndex = self.currentStepIndex;
_currentStepIndex = currentStepIndex;
if ((NSInteger)currentStepIndex - (NSInteger)previousStepIndex > 0) {
[self completeStepAtIndex:previousStepIndex + 1 until:currentStepIndex + 1 completionBlock:completionBlock];
} else {
[self uncompleteStepAtIndex:previousStepIndex until:currentStepIndex - 1 completionBlock:completionBlock];
}
}
}
#pragma mark Setters
- (void)setTintColor:(UIColor *)tintColor {
_tintColor = tintColor;
self.pipeFillView.backgroundColor = tintColor;
for (UILabel *label in self.stepLabels) {
label.textColor = tintColor;
}
[self.stepButtons[self.currentStepIndex] setBackgroundColor:tintColor];
}
#pragma mark Private
- (void)completeStepAtIndex:(NSUInteger)index until:(NSUInteger)until completionBlock:(void (^)())completionBlock {
if (index == until) {
if (completionBlock) {
completionBlock();
}
} else {
[UIView animateWithDuration:0.2f animations:^{
CGRect pipeFillViewFrame = self.pipeFillView.frame;
NSLog(#"%lu, %lu",until, index);
if(index == _titles.count - 1)
{
pipeFillViewFrame.size.width = CGRectGetWidth(self.pipeBackgroundView.bounds) * (index + 1.0f) / self.titles.count;
}
else
{
pipeFillViewFrame.size.width = CGRectGetWidth(self.pipeBackgroundView.bounds) * (index + 0.5f) / self.titles.count;
}
self.pipeFillView.frame = pipeFillViewFrame;
} completion:^(BOOL finishedWidthAnimation) {
[self completeStepAtIndex:index + 1 until:until completionBlock:completionBlock];
UIView *stepButton = self.stepButtons[index];
stepButton.backgroundColor = self.tintColor;
POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
scaleAnimation.velocity = [NSValue valueWithCGSize:CGSizeMake(3.f, 3.f)];
scaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(1.f, 1.f)];
scaleAnimation.springBounciness = 5.f;
[stepButton.layer pop_addAnimation:scaleAnimation forKey:#"scaleAnimation"];
}];
}
}
- (void)uncompleteStepAtIndex:(NSUInteger)index until:(NSUInteger)until completionBlock:(void (^)())completionBlock {
if (index == until) {
if (completionBlock) {
completionBlock();
}
} else {
if (index > until + 1) {
UIView *stepButton = self.stepButtons[index];
stepButton.backgroundColor = [UIColor lightGrayColor];
POPSpringAnimation *scaleAnimation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerScaleXY];
scaleAnimation.velocity = [NSValue valueWithCGSize:CGSizeMake(3.f, 3.f)];
scaleAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(1.f, 1.f)];
scaleAnimation.springBounciness = 5.f;
[stepButton.layer pop_addAnimation:scaleAnimation forKey:#"scaleAnimation"];
}
[UIView animateWithDuration:0.2f animations:^{
CGRect pipeFillViewFrame = self.pipeFillView.frame;
pipeFillViewFrame.size.width = CGRectGetWidth(self.pipeBackgroundView.bounds) * (index + 0.5f) / self.titles.count;
self.pipeFillView.frame = pipeFillViewFrame;
} completion:^(BOOL finishedWidthAnimation) {
[self uncompleteStepAtIndex:index - 1 until:until completionBlock:completionBlock];
}];
}
}
#end
If you want to catch events from a cell to the view controller, the simplest way is to create a protocol and set the view controller as the cell's delegate. I'm sure there are many similar questions here that can help you like this one for example.
I have a view controller presenting another view controller with modalPresentationStyle = UIModalPresentationCustom. Things are set up so that part of the presenting view controller's view shows up underneath the presented view controller's view. In this state, the presenting view controller still handles auto-rotation correctly, and I handle rotation for the presented view controller using autolayout.
I'm now trying to implement interactively dismissing the presented view controller using iOS 7's custom view controller transitioning API. It works except that, when the interactive dismissal is canceled, handling of auto-rotation stops working. (It works again after the presented view controller is dismissed later.) Why is this happening, and how can I fix this?
EDIT: Here is code you can run to demonstrate the problem. A view pops up from below, and you can dismiss it by swiping it down. If you cancel dismissal by not swiping it all the way down, the presenting view controller's view no longer responds to rotations, and the presented view controller's view has messed-up layout.
EDIT: Here is the link to the code below as an Xcode project:
https://drive.google.com/file/d/0BwcBqUuDfCG2YlhVWE1QekhUWlk/edit?usp=sharing
Sorry for the massive code dump, but I don't know what I'm doing wrong. Here's a sketch of what is going on: ViewController1 presents ViewController2. ViewController1 implements UIViewControllerTransitioningDelegate, so it is returning the animation/interactive controllers for the transitions. ViewController2 has a pan gesture recognizer that drives the interactive dismissal; it implements UIViewControllerInteractiveTransitioning to serve as the interactive controller for dismissal. It also keeps a reference to the animation controller for dismissal to finish the transition if the user drags the view down far enough. Finally, there are two animation controller objects. PresentAnimationController sets up the autolayout constraints to handle rotations for the presented view controller's view, and DismissAnimationController finishes up the dismissal.
ViewController1.h
#import <UIKit/UIKit.h>
#interface ViewController1 : UIViewController <UIViewControllerTransitioningDelegate>
#end
ViewController1.m
#import "ViewController1.h"
#import "ViewController2.h"
#import "PresentAnimationController.h"
#import "DismissAnimationController.h"
#implementation ViewController1
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.title = #"View 1";
self.navigationItem.prompt = #"Press “Present” and then swipe down to dismiss.";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Present" style:UIBarButtonItemStylePlain target:self action:#selector(pressedPresentButton:)];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// Some subview just to check if layout is working.
UIView * someSubview = [[UIView alloc] initWithFrame:self.view.bounds];
someSubview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
someSubview.backgroundColor = [UIColor orangeColor];
someSubview.layer.borderColor = [UIColor redColor].CGColor;
someSubview.layer.borderWidth = 2;
[self.view addSubview:someSubview];
}
// --------------------
- (void)pressedPresentButton:(id)sender
{
ViewController2 * presentedVC = [[ViewController2 alloc] initWithNibName:nil bundle:nil];
presentedVC.modalPresentationStyle = UIModalPresentationCustom;
presentedVC.transitioningDelegate = self;
[self presentViewController:presentedVC animated:YES completion:nil];
}
// --------------------
// View Controller Transitioning Delegate Methods.
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return [[PresentAnimationController alloc] init];;
}
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
DismissAnimationController * animationController = [[DismissAnimationController alloc] init];
ViewController2 * presentedVC = (ViewController2 *)self.presentedViewController;
if (presentedVC.dismissalIsInteractive) {
presentedVC.dismissAnimationController = animationController;
}
return animationController;
}
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator
{
return nil;
}
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator
{
ViewController2 * presentedVC = (ViewController2 *)self.presentedViewController;
if (presentedVC.dismissalIsInteractive) {
return presentedVC;
}
else {
return nil;
}
}
#end
ViewController2.h
#import <UIKit/UIKit.h>
#import "DismissAnimationController.h"
#interface ViewController2 : UIViewController <UIViewControllerInteractiveTransitioning>
#property (weak, nonatomic) UIView * contentView;
#property (nonatomic, readonly) BOOL dismissalIsInteractive;
#property (strong, nonatomic) DismissAnimationController * dismissAnimationController;
#end
ViewController2.m
#import "ViewController2.h"
#interface ViewController2 ()
#property (strong, nonatomic) id<UIViewControllerContextTransitioning> transitionContext;
#end
#implementation ViewController2
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_dismissalIsInteractive = NO;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
// Set up content view.
CGRect frame = UIEdgeInsetsInsetRect(self.view.bounds, UIEdgeInsetsMake(15, 15, 15, 15));
UIView * contentView = [[UIView alloc] initWithFrame:frame];
self.contentView = contentView;
contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
contentView.backgroundColor = [UIColor cyanColor];
contentView.layer.borderColor = [UIColor blueColor].CGColor;
contentView.layer.borderWidth = 2;
[self.view addSubview:contentView];
// Set up pan dismissal gesture recognizer.
UIPanGestureRecognizer * panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(dismissalPan:)];
[self.view addGestureRecognizer:panGesture];
}
// --------------------
- (void)dismissalPan:(UIPanGestureRecognizer *)panGesture
{
switch (panGesture.state) {
case UIGestureRecognizerStateBegan: {
_dismissalIsInteractive = YES;
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
break;
}
case UIGestureRecognizerStateChanged: {
CGPoint translation = [panGesture translationInView:self.view];
CGFloat percent;
if (translation.y > 0) {
percent = translation.y / self.view.bounds.size.height;
percent = MIN(percent, 1.0);
}
else {
percent = 0;
}
// Swiping content view down.
CGPoint center;
center.x = CGRectGetMidX(self.view.bounds);
center.y = CGRectGetMidY(self.view.bounds);
if (translation.y > 0) {
center.y += translation.y; // Only allow swiping down.
}
self.contentView.center = center;
self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:(0.5 * (1.0 - percent))];
[self.transitionContext updateInteractiveTransition:percent];
break;
}
case UIGestureRecognizerStateEnded: // Fall through.
case UIGestureRecognizerStateCancelled: {
_dismissalIsInteractive = NO;
id<UIViewControllerContextTransitioning> transitionContext = self.transitionContext;
self.transitionContext = nil;
DismissAnimationController * dismissAnimationController = self.dismissAnimationController;
self.dismissAnimationController = nil;
CGPoint translation = [panGesture translationInView:self.view];
if (translation.y > 100) {
// Complete dismissal.
[dismissAnimationController animateTransition:transitionContext];
}
else {
// Cancel dismissal.
void (^animations)() = ^() {
CGPoint center;
center.x = CGRectGetMidX(self.view.bounds);
center.y = CGRectGetMidY(self.view.bounds);
self.contentView.center = center;
self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
};
void (^completion)(BOOL) = ^(BOOL finished) {
[transitionContext cancelInteractiveTransition];
[transitionContext completeTransition:NO];
};
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:animations completion:completion];
}
break;
}
default: {
break;
}
}
}
// --------------------
// View Controller Interactive Transitioning Methods.
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
self.transitionContext = transitionContext;
}
#end
PresentAnimationController.h
#import <Foundation/Foundation.h>
#interface PresentAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
#end
PresentAnimationController.m
#import "PresentAnimationController.h"
#import "ViewController2.h"
#implementation PresentAnimationController
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
UIViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ViewController2 * toVC = (ViewController2 *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView * containerView = [transitionContext containerView];
CGPoint toCenter = fromVC.view.center;
CGRect toBounds = fromVC.view.bounds;
toVC.view.center = toCenter;
toVC.view.bounds = toBounds;
[toVC.view layoutIfNeeded];
[containerView addSubview:fromVC.view];
[containerView addSubview:toVC.view];
CGRect contentViewEndFrame = toVC.contentView.frame;
CGRect contentViewStartFrame = contentViewEndFrame;
contentViewStartFrame.origin.y += contentViewStartFrame.size.height;
toVC.contentView.frame = contentViewStartFrame;
UIColor * endBackgroundColor = toVC.view.backgroundColor;
toVC.view.backgroundColor = [UIColor clearColor];
void (^animations)() = ^() {
toVC.contentView.frame = contentViewEndFrame;
toVC.view.backgroundColor = endBackgroundColor;
};
void (^completion)(BOOL) = ^(BOOL finished) {
toVC.view.autoresizingMask = UIViewAutoresizingNone;
toVC.view.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint * centerXConstraint = [NSLayoutConstraint constraintWithItem:toVC.view
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:fromVC.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
NSLayoutConstraint * centerYConstraint = [NSLayoutConstraint constraintWithItem:toVC.view
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:fromVC.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
NSLayoutConstraint * widthConstraint = [NSLayoutConstraint constraintWithItem:toVC.view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:fromVC.view
attribute:NSLayoutAttributeWidth
multiplier:1
constant:0];
NSLayoutConstraint * heightConstraint = [NSLayoutConstraint constraintWithItem:toVC.view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:fromVC.view
attribute:NSLayoutAttributeHeight
multiplier:1
constant:0];
[containerView addConstraint:centerXConstraint];
[containerView addConstraint:centerYConstraint];
[containerView addConstraint:widthConstraint];
[containerView addConstraint:heightConstraint];
[transitionContext completeTransition:YES];
};
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:animations completion:completion];
}
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.5;
}
#end
DismissAnimationController.h
#import <Foundation/Foundation.h>
#interface DismissAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
#end
DismissAnimationController.m
#import "DismissAnimationController.h"
#import "ViewController2.h"
#implementation DismissAnimationController
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
ViewController2 * fromVC = (ViewController2 *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController * toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView * containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
[containerView addSubview:fromVC.view];
void (^animations)() = ^() {
CGRect contentViewEndFrame = fromVC.contentView.frame;
contentViewEndFrame.origin.y = CGRectGetMaxY(fromVC.view.bounds) + 15;
fromVC.contentView.frame = contentViewEndFrame;
fromVC.view.backgroundColor = [UIColor clearColor];
};
void (^completion)(BOOL) = ^(BOOL finished) {
if ([transitionContext isInteractive]) {
[transitionContext finishInteractiveTransition];
}
[transitionContext completeTransition:YES];
};
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveLinear animations:animations completion:completion];
}
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.5;
}
#end
AppDelegate.m
#import "AppDelegate.h"
#import "ViewController1.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
ViewController1 * vc = [[ViewController1 alloc] initWithNibName:nil bundle:nil];
UINavigationController * nav = [[UINavigationController alloc] initWithRootViewController:vc];
self.window.rootViewController = nav;
[self.window makeKeyAndVisible];
return YES;
}
#end
I think I found your problem. in your PresentAnimationController.m you specify toVC.view.translatesAutoresizingMaskIntoConstraints = NO; and you set all of your constraints in the completion block you set in
- (void)animateTransition:
Comment that line out and all of the constraints and addConstraint: calls and it should work
EDIT:
Just saw it worked only when the gesture was cancelled and not when the view is initially displayed. Comment out everything in the completion block except for
[transitionContext completeTransition:YES];
I have created a SlidingViewController which has a Main, Right, and Left view controllers. Now I want to add a new function/method that is called whenever one of the RightViewController's table view cell is clicked to switch the main view controller to what ever view controller is linked with that table view cell at indexpath.row. The new view controller should still be able to have this right and left view controller available. Anyone have any experience creating their own SlidingViewController or MenuViewController that can help out on how I should approach this? Any help will be gratefully appreciated. Thank you in advance.
SlidingViewController.h
#interface SlidingViewController : UIViewController <UIGestureRecognizerDelegate>
#property (retain) UIViewController *mainViewController;
#property (retain) UIViewController *leftViewController;
#property (retain) UIViewController *rightViewController;
#property (retain) NSNumber *leftSwipeEnabled;
#property (assign) BOOL leftDrawerVisible;
#property (retain) NSNumber *rightSwipeEnabled;
#property (assign) BOOL rightDrawerVisible;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil mainViewController:(UIViewController *)main leftViewController:(UIViewController *)left andRightViewController:(UIViewController *)right;
-(void)toggleLeftDrawer;
-(void)toggleRightDrawer;
#end
SlidingViewController.m
#implementation SlidingViewController
#synthesize mainViewController, leftViewController, rightViewController;
#synthesize leftDrawerVisible, rightDrawerVisible;
#synthesize leftSwipeEnabled, rightSwipeEnabled;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil mainViewController:(UIViewController *)main leftViewController:(UIViewController *)left andRightViewController:(UIViewController *)right
{
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])
{
mainViewController = main;
leftViewController = left;
rightViewController = right;
leftSwipeEnabled = [NSNumber numberWithBool:NO];
rightSwipeEnabled = [NSNumber numberWithBool:NO];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self addChildViewController:self.mainViewController];
[self.view addSubview:[self.mainViewController view]];
self.mainViewController.view.frame = self.view.frame;
[self.mainViewController didMoveToParentViewController:self];
self.mainViewController.view.layer.shadowColor = [UIColor blackColor].CGColor;
self.mainViewController.view.layer.shadowOpacity = 0.2f;
self.mainViewController.view.layer.shadowRadius = 5.0f;
CGPathRef path = [UIBezierPath bezierPathWithRect:self.mainViewController.view.bounds].CGPath;
self.mainViewController.view.layer.shadowPath = path;
if(self.leftViewController != nil)
{
UISwipeGestureRecognizer *leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
leftSwipeRecognizer.delegate = self;
[self.mainViewController.view addGestureRecognizer:leftSwipeRecognizer];
}
if(self.rightViewController != nil)
{
UISwipeGestureRecognizer *rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(handleSwipe:)];
rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
rightSwipeRecognizer.delegate = self;
[self.mainViewController.view addGestureRecognizer:rightSwipeRecognizer];
}
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self layoutShadowWithDuration:0];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
if(self.leftDrawerVisible)
[self toggleLeftDrawer];
else if(self.rightDrawerVisible)
[self toggleRightDrawer];
}
-(BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
-(void)handleSwipe:(UISwipeGestureRecognizer *)recognizer
{
if(recognizer.state == UIGestureRecognizerStateEnded)
{
if(recognizer.direction == UISwipeGestureRecognizerDirectionRight && [self.leftSwipeEnabled boolValue])
[self toggleLeftDrawer];
else if(recognizer.direction == UISwipeGestureRecognizerDirectionLeft && [self.rightSwipeEnabled boolValue])
[self toggleRightDrawer];
}
}
-(void)toggleLeftDrawer
{
if (self.rightDrawerVisible)
{
return;
}
if(self.leftDrawerVisible)
{
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.mainViewController.view.frame = CGRectMake(0.0, 0.0, self.mainViewController.view.frame.size.width, self.mainViewController.view.frame.size.height);}
completion:^(BOOL finished)
{
[self.leftViewController.view removeFromSuperview];
[self.leftViewController willMoveToParentViewController:nil];
[self.leftViewController removeFromParentViewController];
}];
self.leftDrawerVisible = NO;
self.mainViewController.view.userInteractionEnabled = YES;
}
else
{
[self addChildViewController:self.leftViewController];
[self.view insertSubview:[self.leftViewController view] belowSubview:[self.mainViewController view]];
[self.leftViewController didMoveToParentViewController:self];
CGPathRef path = [UIBezierPath bezierPathWithRect:self.mainViewController.view.bounds].CGPath;
self.mainViewController.view.layer.shadowPath = path;
self.mainViewController.view.layer.shadowOffset = CGSizeMake(-3, 0);
NSInteger width = 260;
if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
width = 320;
self.leftViewController.view.frame = CGRectMake(0, 0, width, self.view.bounds.size.height);
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{ self.mainViewController.view.frame = CGRectMake(width, 0, self.mainViewController.view.frame.size.width, self.mainViewController.view.frame.size.height); }
completion:^(BOOL finished) { self.leftViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin; }];
self.leftDrawerVisible = YES;
self.mainViewController.view.userInteractionEnabled = NO;
}
}
-(void)toggleRightDrawer
{
if(self.leftDrawerVisible)
return;
if(self.rightDrawerVisible)
{
[UIView animateWithDuration:0.2
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.mainViewController.view.frame = CGRectMake(0.0, 0.0, self.mainViewController.view.frame.size.width, self.mainViewController.view.frame.size.height);
}
completion:^(BOOL finished){
[self.rightViewController.view removeFromSuperview];
[self.rightViewController willMoveToParentViewController:nil];
[self.rightViewController removeFromParentViewController];
}];
self.rightDrawerVisible = NO;
self.mainViewController.view.userInteractionEnabled = YES;
}
else
{
[self addChildViewController:self.rightViewController];
[self.view insertSubview:[self.rightViewController view] belowSubview:[self.mainViewController view]];
[self.rightViewController didMoveToParentViewController:self];
CGPathRef path = [UIBezierPath bezierPathWithRect:self.mainViewController.view.bounds].CGPath;
self.mainViewController.view.layer.shadowPath = path;
self.mainViewController.view.layer.shadowOffset = CGSizeMake(3, 0);
NSInteger width = 260;
if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
width = 320;
self.rightViewController.view.frame = CGRectMake(self.view.bounds.size.width- width, 0, width, self.view.bounds.size.height);
[UIView animateWithDuration:0.2 delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.mainViewController.view.frame = CGRectMake(-width, 0, self.mainViewController.view.frame.size.width, self.mainViewController.view.frame.size.height);
}
completion:^(BOOL finished){
self.rightViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin;
}];
self.rightDrawerVisible = YES;
self.mainViewController.view.userInteractionEnabled = NO;
}
}
-(void) layoutShadowWithDuration:(NSTimeInterval)duration
{
CGPathRef oldShadowPath = self.mainViewController.view.layer.shadowPath;
if (oldShadowPath)
{
CFRetain(oldShadowPath);
}
// Update shadow path for the view
CGPathRef path = [UIBezierPath bezierPathWithRect:self.mainViewController.view.bounds].CGPath;
self.mainViewController.view.layer.shadowPath = path;
// You would think setting duration to 0 would cause the animation added below to not animate. You would be wrong.
if (duration != 0)
{
if (oldShadowPath)
{
[self.mainViewController.view.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);
}
else
if (oldShadowPath)
CFRelease(oldShadowPath);
}
else
if (oldShadowPath)
CFRelease(oldShadowPath);
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self layoutShadowWithDuration:duration];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
You can simply do this by "Delegation Design Pattern"
In RightViewController.h add the following lines.
#protocol RightViewControllerDelegate <NSObject>
#required
- (void)somethingClicked : (id)theObject;
#end
#interface RightViewController : UIViewController
#property (nonatomic, assign) id<RightViewControllerDelegate> delegate;
#end
In RightViewController.m add the following lines.
- (void)somethingClicked : (id)theObject
{
[_delegate somethingClicked:theObject];
}
Update MainViewController.h with the following code.
#interface MainViewController : UIViewController <RightViewControllerDelegate>
And Finally add the following code in the MainViewController.m
- (void)somethingClicked : (id)theObject
{
// Perform Your Task
}