I have a custom alterviewcontroller which subclasses UIViewController. It works great in apps that are portrait, but in landscape, it's running into issues with the frame orientation. I've tried a bunch of fixes, but none seem to be getting it right.
In the current version, it displays correctly but the main view is the wrong size, so the buttons that are outside of that view are not clickable.
In this image, the Next Turn button is not Clickable, the Go to Black button is. This is landscape left.
.h file
#import <UIKit/UIKit.h>
#import "SPRTitleFontLabel.h"
#protocol SPRAlertViewControllerDelegate;
#interface SPRAlertViewController : UIViewController
#property (nonatomic, weak) id <SPRAlertViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UIView *alertview;
#property (weak, nonatomic) IBOutlet SPRTitleFontLabel *titleLabel;
#property (weak, nonatomic) IBOutlet UITextView *messageLabel;
#property (weak, nonatomic) IBOutlet UIButton *yesButton;
#property (weak, nonatomic) IBOutlet UIButton *noButton;
#property (weak, nonatomic) IBOutlet UIButton *okButton;
#property int tag;
-(void)setTitle:(NSString *)title message:(NSString *)message andButtonCount:(int)buttonCount;
- (IBAction)doDismiss:(id)sender;
- (void)show;
#end
#protocol SPRAlertViewControllerDelegate <NSObject>
- (void)alert:(SPRAlertViewController *)alert didDismissWithButtonClick:(int)buttonIndex;
#end
.m file
#import "SPRAlertViewController.h"
#import "SPRAudioHelper.h"
#interface SPRAlertViewController ()
{
UIWindow *alertWindow;
}
#end
#implementation SPRAlertViewController
+(instancetype)new
{
SPRAlertViewController *alvc = [super new];
return alvc;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
alertWindow = [self windowWithLevel:UIWindowLevelAlert];
if (!alertWindow)
{
//rotate the window frame to get the right orientation
CGRect tempFrame = [UIScreen mainScreen].bounds;
CGRect tempFrame2 = tempFrame;
tempFrame2.size.height = tempFrame.size.width;
tempFrame2.size.width = tempFrame.size.height;
alertWindow = [[UIWindow alloc] initWithFrame:tempFrame2];
alertWindow.windowLevel = UIWindowLevelAlert;
[alertWindow makeKeyAndVisible];
NSLog(#"window frame %#", NSStringFromCGRect(alertWindow.frame));
}
alertWindow.rootViewController = self;
}
return self;
}
- (void)setTitle:(NSString *)title message:(NSString *)message andButtonCount:(int)buttonCount
{
self.titleLabel.text = title;
self.messageLabel.text = message;
if (buttonCount == 1)
{
self.yesButton.hidden = YES;
self.noButton.hidden = YES;
self.okButton.hidden = NO;
}
else
{
self.yesButton.hidden = NO;
self.noButton.hidden = NO;
self.okButton.hidden = YES;
}
CGRect frame = self.messageLabel.frame;
CGSize size = [self.messageLabel sizeThatFits:CGSizeMake(self.messageLabel.frame.size.width, FLT_MAX)];
frame.size.height = size.height;
self.messageLabel.frame = frame;
[self.messageLabel setNeedsDisplay];
float buttonTop = frame.size.height + frame.origin.y;
self.yesButton.frame = CGRectMake(self.yesButton.frame.origin.x, buttonTop, self.yesButton.frame.size.width, self.yesButton.frame.size.height);
self.noButton.frame = CGRectMake(self.noButton.frame.origin.x, buttonTop, self.noButton.frame.size.width, self.noButton.frame.size.height);
self.okButton.frame = CGRectMake(self.okButton.frame.origin.x, buttonTop, self.okButton.frame.size.width, self.okButton.frame.size.height);
self.alertview.frame = CGRectMake(self.alertview.frame.origin.x, self.alertview.frame.origin.y, self.alertview.frame.size.width, buttonTop+55);
NSLog(#"parent frame %#", NSStringFromCGRect(self.parentViewController.view.frame));
NSLog(#"view frame %#", NSStringFromCGRect(self.view.frame));
NSLog(#"alert frame %#", NSStringFromCGRect(self.alertview.frame));
NSLog(#"button frame %#", NSStringFromCGRect(self.yesButton.frame));
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
UIInterpolatingMotionEffect* m1 =
[[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"center.x"
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
m1.maximumRelativeValue = #0.0;
m1.minimumRelativeValue = #0.0;
UIInterpolatingMotionEffect* m2 =
[[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"center.y"
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
m2.maximumRelativeValue = #20.0;
m2.minimumRelativeValue = #-20.0;
UIMotionEffectGroup* g = [UIMotionEffectGroup new];
g.motionEffects = #[m1,m2];
[self.alertview addMotionEffect:g];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)show
{
CGRect tempFrame = [UIScreen mainScreen].bounds;
CGRect tempFrame2 = tempFrame;
tempFrame2.size.height = tempFrame.size.width;
tempFrame2.size.width = tempFrame.size.height;
self.alertview.center = CGPointMake(CGRectGetMidX(tempFrame2), CGRectGetMidY(tempFrame2));
self.alertview.transform = CGAffineTransformMakeScale(1,1.6);
self.alertview.alpha = 0;
self.view.alpha = 0;
[UIView animateWithDuration:0.1 animations:^{
self.view.alpha = 1;
}
completion:
^(BOOL finished){
[UIView animateWithDuration:0.25 animations:^{
self.alertview.alpha = 1;
self.alertview.transform = CGAffineTransformIdentity;
}];
}
];
}
- (UIWindow *)windowWithLevel:(UIWindowLevel)windowLevel
{
NSArray *windows = [[UIApplication sharedApplication] windows];
for (UIWindow *window in windows) {
if (window.windowLevel == windowLevel) {
return window;
}
}
return nil;
}
- (IBAction)doDismiss:(id)sender
{
// [[SPRAudioHelper sharedHelper] playSoundFromTag:lightClick];
NSLog(#"check");
[UIView animateWithDuration:0.25 animations:^{
self.alertview.transform = CGAffineTransformScale(self.alertview.transform, 1,0.5);
self.alertview.alpha = 0;
}completion:^(BOOL finished)
{
[UIView animateWithDuration:0.1 animations:^{
self.view.alpha = 0;
}completion:^(BOOL finished)
{
[alertWindow removeFromSuperview];
alertWindow = nil;
if (sender == self.yesButton)
[self.delegate alert:self didDismissWithButtonClick:1];
else
[self.delegate alert:self didDismissWithButtonClick:0];
}];
}];
}
#end
Here's the NSLog results:
2014-07-10 10:59:18.603 Damage Report Timer[5166:60b] window frame {{0, 0}, {568, 320}}
2014-07-10 10:59:18.720 Damage Report Timer[5166:60b] parent frame {{0, 0}, {0, 0}}
2014-07-10 10:59:18.722 Damage Report Timer[5166:60b] view frame {{0, 0}, {320, 568}}
2014-07-10 10:59:18.724 Damage Report Timer[5166:60b] alert frame {{0,51}, {568, 146.5}}
2014-07-10 10:59:18.726 Damage Report Timer[5166:60b] button frame {{101, 91.5}, {150, 50}}
I figured it out after doing more and more digging. I was really making the whole thing way more complicated than it is. 2 functions needed to be fixed, here's the updated functions:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
alertWindow = [self windowWithLevel:UIWindowLevelAlert];
if (!alertWindow)
{
alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
alertWindow.windowLevel = UIWindowLevelAlert;
[alertWindow makeKeyAndVisible];
}
alertWindow.rootViewController = self;
}
return self;
}
and
- (void)show
{
CGRect frame = self.view.bounds;
self.alertview.center = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
self.alertview.transform = CGAffineTransformMakeScale(1,1.6);
self.alertview.alpha = 0;
self.view.alpha = 0;
[UIView animateWithDuration:0.1 animations:^{
self.view.alpha = 1;
}
completion:
^(BOOL finished){
[UIView animateWithDuration:0.25 animations:^{
self.alertview.alpha = 1;
self.alertview.transform = CGAffineTransformIdentity;
}];
}
];
}
Originally since show was centering the view on mainscreen bounds, it was putting it in the wrong place, since mainscreen bounds doesn't get updated on rotation. Then I made the mistake of resizing the window to accommodate for that. Which made buttons no longer be in the window.
Related
I have a UIImageView and a UIView (filled with smaller imageviews) all placed in a UIScrollView (for zooming).
To better help you understand, I have a map (ImageView) with fog(UIView with smaller imageviews) in the scrollview so I can zoom the map.
If I add the mapview and fogview to the self.view then the application works as it should, touching the fog at a point removes that fog image.
However, when I add the map and fog views to the scrollview nothing responds to touches.
I ultimately want the default behavior to be touching fog view will remove the fog at one point. Unless the user presses a button to zoom/pinch the view, which will zoom both the mapview and fogview.
The problem below is no touches are processed when I add the mapview and fogview to the scrollview. Currently there is no ability to press a button to pinch/zoom as I still don't have the default behavior I want.
Any help?
#interface ViewController () <UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIActionSheetDelegate, UIGestureRecognizerDelegate, UIScrollViewDelegate>{
UIImage * fogImage;
NSUserDefaults * defaults;
bool canZoom;
}
#property (weak, nonatomic) IBOutlet UIButton *btnTakePicture;
#property (strong, nonatomic) UIButton * btnMenu;
#property (strong, nonatomic) UIButton * btnZoom;
#property (strong, nonatomic) UIScrollView* scrollView;
#property (strong, nonatomic) UIImageView *mapView;
#property (strong, nonatomic) UIView * fogView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
fogImage = [UIImage imageNamed:#"fog1.jpg"];
self.mapView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.fogView = [[UIView alloc] initWithFrame:CGRectZero];
_scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];
_scrollView.delegate = self;
_scrollView.minimumZoomScale = 0.75;
_scrollView.maximumZoomScale = 3.0;
[_scrollView addSubview:self.mapView];
[_scrollView addSubview:self.fogView];
[self.view addSubview:_scrollView];
self.mapView.hidden = YES;
self.fogView.hidden = YES;
self.scrollView.hidden = YES;
defaults = [NSUserDefaults standardUserDefaults];
canZoom = false;
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
self.mapView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
self.fogView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
self.scrollView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}
- (void) zoom:(id)sender{
canZoom = !canZoom;
}
- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *chosenImage = info[UIImagePickerControllerOriginalImage];
self.btnTakePicture.hidden = YES;
self.btnMenu.hidden = NO;
self.btnZoom.hidden = NO;
[picker dismissViewControllerAnimated:YES completion:^(void) {
[self fillScreenWithFog];
self.mapView.image = chosenImage;
self.mapView.contentMode = UIViewContentModeScaleAspectFit;
self.mapView.hidden = NO;
self.fogView.hidden = NO;
_scrollView.hidden = NO;
_scrollView.userInteractionEnabled = YES;
self.scrollView.contentSize = self.mapView.frame.size;
}];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[picker dismissViewControllerAnimated:YES completion:NULL];
}
#pragma mark Fog Maker
- (void) removeFogAtPoint:(CGPoint)point{
NSLog(#"removeFogAtPoint %#", NSStringFromCGPoint(point));
CGRect fingerRect = CGRectMake(point.x - 5, point.y-5, 10, 10);
for(UIImageView *view in self.fogView.subviews){
CGRect subviewFrame = view.frame;
if(CGRectIntersectsRect(fingerRect, subviewFrame)){
[UIView animateWithDuration:1.25
animations:^{
view.alpha = 0;
}
completion:^(BOOL finished){
[view removeFromSuperview];
}];
}
}
}
- (void) fillScreenWithFog {
// do a loop to fill the screen with fog getNewSquare
}
- (UIImageView*) getNewSquare:(CGRect)frame withTag:(int)tag{
UIImageView * imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = fogImage;
imageView.contentMode = UIViewContentModeScaleAspectFill;
return imageView;
}
-(void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(#"Touches Moved");
CGPoint location = [[touches anyObject] locationInView:self.scrollView];
[self removeFogAtPoint:location];
}
#end
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'd like to make MenuView display with animation.
When I call -showSideMenu, MenuView does not appear.
How do I fix it?
MenuView.h
#import <UIKit/UIKit.h>
#interface SettingView : UIView
#end
MenuView.m
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.frame = CGRectMake(320, 0, 240, 568);
self.backgroundColor = [UIColor turquoiseColor];
}
return self;
}
MasterViewController.m
#property (nonatomic, strong) MenuView* sideMenuView;
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem* menuButton = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:#selector(showSideMenu:)];
self.navigationItem.rightBarButtonItem = menuButton;
self.sideMenuView = [[MenuView alloc]initWithFrame:self.view.frame];
[self.view addSubview:self.sideMenuView];
}
- (IBAction)showSideMenu:(id)sender
{
[UIView animateWithDuration:0.3f
delay:0.0f
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.sideMenuView.frame = CGRectMake(80, 0, 240, 568);
}
completion:^(BOOL finished) {
}];
}
Couple of things:
Put an NSLog in the showSideMenu method. Does it actually fire?
You animate the frame. Do you have AutoLayout turned off for this view controller?
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
}
I've edited this question a few times, but can still not get my images to center inside a uiview. I want them to be able to rotate like the photos app and display the correct size when a user brings them up. Here is what I'm working with:
In my PhotoViewController.h
#import <UIKit/UIKit.h>
#protocol PhotoViewControllerDelegate <NSObject>
- (void)toggleChromeDisplay;
#end
#interface PhotoViewController : UIViewController <UIScrollViewDelegate, UIGestureRecognizerDelegate>
#property (nonatomic, strong) UIImage *photo;
#property (nonatomic) NSUInteger num;
//Delegate
#property (nonatomic, strong) id<PhotoViewControllerDelegate> photoViewControllerDelegate;
#property (nonatomic, strong) UIImageView *photoImgView;
#property (nonatomic, strong) UIScrollView *scrollView;
#end
In my PhotoViewController.m:
#import "PhotoViewController.h"
#interface PhotoViewController ()
#end
#implementation PhotoViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
//todo
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect screenBounds = self.view.bounds;
//scroll view
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, screenBounds.size.width, screenBounds.size.height)];
_scrollView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
_scrollView.pagingEnabled = NO;
_scrollView.scrollEnabled = YES;
[_scrollView setBackgroundColor:[UIColor blueColor]];
//Zoom Properties
_scrollView.maximumZoomScale = 6.0;
_scrollView.minimumZoomScale = 1.0;
_scrollView.bouncesZoom = YES;
_scrollView.delegate = self;
_scrollView.zoomScale = 1.0;
_scrollView.contentSize = _photoImgView.bounds.size;
[_scrollView setShowsHorizontalScrollIndicator:NO];
[_scrollView setShowsVerticalScrollIndicator:NO];
[self photoBounds];
[self.view addSubview: _scrollView];
//Add the UIImageView
_photoImgView = [[UIImageView alloc] initWithImage:_photo];
_photoImgView.image = _photo;
_photoImgView.clipsToBounds = YES;
_photoImgView.contentMode = UIViewContentModeScaleAspectFit;
_photoImgView.autoresizingMask = (UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin);
[_photoImgView setUserInteractionEnabled:YES];
[_scrollView addSubview: _photoImgView];
//Set up Gesture Recognizer
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured:)];
UITapGestureRecognizer *dTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dTapGestureCaptured:)];
dTap.numberOfTapsRequired = 2;
[singleTap requireGestureRecognizerToFail:dTap];
//Gesture Methods
[self.scrollView addGestureRecognizer:singleTap];
[self.scrollView addGestureRecognizer : dTap];
}
- (void)photoBounds
{
UIInterfaceOrientation statusbar = [[UIApplication sharedApplication] statusBarOrientation];
CGSize photoBounds = _photo.size;
CGSize scrollBounds = self.view.bounds.size;
CGRect frameToCenter = [_photoImgView frame];
float newHeight = (scrollBounds.width / photoBounds.width) * photoBounds.height;
float newWidth = (scrollBounds.height / photoBounds.height) * photoBounds.width;
float yDist = fabsf(scrollBounds.height - newHeight) / 2;
float xDist = fabsf(scrollBounds.width - newWidth) / 2;
//Width Larger
if (photoBounds.width >=photoBounds.height) {
NSLog(#"portrait width");
_photoImgView.frame = CGRectMake(0, 0, scrollBounds.width, newHeight);
frameToCenter.origin.y = yDist;
}
//Height Larger
else if (photoBounds.height > photoBounds.width) {
NSLog(#"portrait height");
_photoImgView.frame = CGRectMake(0, 0, newWidth, scrollBounds.height);
frameToCenter.origin.x = xDist;
}
//Square
else {
NSLog(#"portrait square");
if ((statusbar == 1) || (statusbar == 2)) {
_photoImgView.frame = CGRectMake(0, 0, scrollBounds.width, newHeight);
frameToCenter.origin.y = yDist;
} else {
_photoImgView.frame = CGRectMake(0, 0, newWidth, scrollBounds.height);
frameToCenter.origin.x = xDist;
}
}
}
//Rotation Magic
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
//later
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self photoBounds];
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
//
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
//Zoom Ability
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return self.photoImgView;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
NSLog(#"scale %f", scale);
NSLog(#"done zooming");
}
//Touches Control
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
//CGPoint touchPoint=[gesture locationInView:_scrollView];
NSLog(#"touched");
NSLog(#"single touch");
[self performSelector:#selector(callingHome) withObject:nil afterDelay:0];
}
- (void)dTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
NSLog(#"double touched");
}
- (void)panGestureCaptured:(UIPanGestureRecognizer *)gesture
{
NSLog(#"pan gesture");
}
- (void)callingHome {}
#end
The overall issue is that I can not get my picture to display correctly and be able to zoom on just it, no space around it and It needs to be the correct dimensions on load. I've been struggling with it for a few days.
Any help?
I have resolved a similar problem using a really simple subclass of UIScrollView of my own. You can take a look at it here - https://gist.github.com/ShadeApps/5a29e1cea3e1dc3df8c8. Works like charm for me.
That's how you init it:
scrollViewMain.delegate = self;
scrollViewMain.minimumZoomScale = 1.0;
scrollViewMain.maximumZoomScale = 3.0;
scrollViewMain.contentSize = imageViewMain.frame.size;
scrollViewMain.backgroundColor = [UIColor blackColor];
scrollViewMain.tileContainerView = imageViewMain;
There is awesome blog post about this problem from Peter Steinberger: http://petersteinberger.com/blog/2013/how-to-center-uiscrollview/