From 5dbd1a0c1302271c8dc89dda28407b554766eae1 Mon Sep 17 00:00:00 2001 From: Andy Molloy Date: Tue, 25 Sep 2012 15:20:02 -0400 Subject: [PATCH 1/9] Make compatible with ARC. Also remove some unneeded synthesize statements and all ivars. --- TPMultiLayoutViewController.h | 10 +------ TPMultiLayoutViewController.m | 49 +++++++++++++++-------------------- 2 files changed, 22 insertions(+), 37 deletions(-) diff --git a/TPMultiLayoutViewController.h b/TPMultiLayoutViewController.h index 02c758a..08b410b 100644 --- a/TPMultiLayoutViewController.h +++ b/TPMultiLayoutViewController.h @@ -7,15 +7,7 @@ #import -@interface TPMultiLayoutViewController : UIViewController { - UIView *portraitView; - UIView *landscapeView; - - @private - NSDictionary *portraitAttributes; - NSDictionary *landscapeAttributes; - BOOL viewIsCurrentlyPortrait; -} +@interface TPMultiLayoutViewController : UIViewController // Call directly to use with custom animation (override willRotateToInterfaceOrientation to disable the switch there) - (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation; diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index 9cb993c..00a73ef 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -17,10 +17,14 @@ - (void)applyAttributeTable:(NSDictionary*)table toViewHierarchy:(UIView*)view; - (NSDictionary*)attributesForView:(UIView*)view; - (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view; - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view; + +@property (nonatomic, strong) NSDictionary *portraitAttributes; +@property (nonatomic, strong) NSDictionary *landscapeAttributes; +@property (nonatomic, assign) BOOL viewIsCurrentlyPortrait; + @end @implementation TPMultiLayoutViewController -@synthesize portraitView, landscapeView; #pragma mark - View lifecycle @@ -28,9 +32,9 @@ - (void)viewDidLoad { [super viewDidLoad]; // Construct attribute tables - portraitAttributes = [[self attributeTableForViewHierarchy:portraitView associateWithViewHierarchy:self.view] retain]; - landscapeAttributes = [[self attributeTableForViewHierarchy:landscapeView associateWithViewHierarchy:self.view] retain]; - viewIsCurrentlyPortrait = (self.view == portraitView); + self.portraitAttributes = [self attributeTableForViewHierarchy:self.portraitView associateWithViewHierarchy:self.view]; + self.landscapeAttributes = [self attributeTableForViewHierarchy:self.landscapeView associateWithViewHierarchy:self.view]; + self.viewIsCurrentlyPortrait = (self.view == self.portraitView); // Don't need to retain the original template view hierarchies any more self.portraitView = nil; @@ -39,26 +43,15 @@ - (void)viewDidLoad { - (void)viewDidUnload { [super viewDidUnload]; - - [portraitAttributes release]; - portraitAttributes = nil; - [landscapeAttributes release]; - landscapeAttributes = nil; -} - -- (void)dealloc { - [portraitAttributes release]; - portraitAttributes = nil; - [landscapeAttributes release]; - landscapeAttributes = nil; - - [super dealloc]; + + self.portraitAttributes = nil; + self.landscapeAttributes = nil; } -(void)viewWillAppear:(BOOL)animated { // Display correct layout for orientation - if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !viewIsCurrentlyPortrait) || - (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && viewIsCurrentlyPortrait) ) { + if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !self.viewIsCurrentlyPortrait) || + (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && self.viewIsCurrentlyPortrait) ) { [self applyLayoutForInterfaceOrientation:self.interfaceOrientation]; } } @@ -66,14 +59,14 @@ -(void)viewWillAppear:(BOOL)animated { #pragma mark - Rotation - (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation { - NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? portraitAttributes : landscapeAttributes; + NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? self.portraitAttributes : self.landscapeAttributes; [self applyAttributeTable:table toViewHierarchy:self.view]; - viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); + self.viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); } -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !viewIsCurrentlyPortrait) || - (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && viewIsCurrentlyPortrait) ) { + if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !self.viewIsCurrentlyPortrait) || + (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && self.viewIsCurrentlyPortrait) ) { [self applyLayoutForInterfaceOrientation:toInterfaceOrientation]; } } @@ -87,7 +80,7 @@ - (NSDictionary*)attributeTableForViewHierarchy:(UIView*)rootView associateWithV } - (void)addAttributesForSubviewHierarchy:(UIView*)view associatedWithSubviewHierarchy:(UIView*)associatedView toTable:(NSMutableDictionary*)table { - [table setObject:[self attributesForView:view] forKey:[NSValue valueWithPointer:associatedView]]; + [table setObject:[self attributesForView:view] forKey:[NSValue valueWithPointer:(__bridge const void *)(associatedView)]]; if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; @@ -117,7 +110,7 @@ - (UIView*)findAssociatedViewForView:(UIView*)view amongViews:(NSArray*)views { UIControlEvents controlEvents = [(UIControl*)otherView allControlEvents]; for ( id target in [(UIControl*)otherView allTargets] ) { // Iterate over each bit in the UIControlEvents bitfield - for ( NSInteger i=0; i Date: Tue, 25 Sep 2012 16:34:01 -0400 Subject: [PATCH 2/9] Formatting --- TPMultiLayoutViewController.m | 308 +++++++++++++++++----------------- 1 file changed, 155 insertions(+), 153 deletions(-) diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index 00a73ef..39f7629 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -29,201 +29,203 @@ @implementation TPMultiLayoutViewController #pragma mark - View lifecycle - (void)viewDidLoad { - [super viewDidLoad]; - - // Construct attribute tables - self.portraitAttributes = [self attributeTableForViewHierarchy:self.portraitView associateWithViewHierarchy:self.view]; - self.landscapeAttributes = [self attributeTableForViewHierarchy:self.landscapeView associateWithViewHierarchy:self.view]; - self.viewIsCurrentlyPortrait = (self.view == self.portraitView); - - // Don't need to retain the original template view hierarchies any more - self.portraitView = nil; - self.landscapeView = nil; + [super viewDidLoad]; + + // Construct attribute tables + self.portraitAttributes = [self attributeTableForViewHierarchy:self.portraitView associateWithViewHierarchy:self.view]; + self.landscapeAttributes = [self attributeTableForViewHierarchy:self.landscapeView associateWithViewHierarchy:self.view]; + self.viewIsCurrentlyPortrait = (self.view == self.portraitView); + + // Don't need to retain the original template view hierarchies any more + self.portraitView = nil; + self.landscapeView = nil; } - (void)viewDidUnload { - [super viewDidUnload]; + [super viewDidUnload]; - self.portraitAttributes = nil; - self.landscapeAttributes = nil; + self.portraitAttributes = nil; + self.landscapeAttributes = nil; } -(void)viewWillAppear:(BOOL)animated { - // Display correct layout for orientation - if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !self.viewIsCurrentlyPortrait) || - (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && self.viewIsCurrentlyPortrait) ) { - [self applyLayoutForInterfaceOrientation:self.interfaceOrientation]; - } + // Display correct layout for orientation + if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !self.viewIsCurrentlyPortrait) || + (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && self.viewIsCurrentlyPortrait) ) { + [self applyLayoutForInterfaceOrientation:self.interfaceOrientation]; + } } #pragma mark - Rotation - (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation { - NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? self.portraitAttributes : self.landscapeAttributes; - [self applyAttributeTable:table toViewHierarchy:self.view]; - self.viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); + NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? self.portraitAttributes : self.landscapeAttributes; + [self applyAttributeTable:table toViewHierarchy:self.view]; + self.viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); } -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !self.viewIsCurrentlyPortrait) || - (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && self.viewIsCurrentlyPortrait) ) { - [self applyLayoutForInterfaceOrientation:toInterfaceOrientation]; - } + if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !self.viewIsCurrentlyPortrait) || + (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && self.viewIsCurrentlyPortrait) ) { + [self applyLayoutForInterfaceOrientation:toInterfaceOrientation]; + } } #pragma mark - Helpers - (NSDictionary*)attributeTableForViewHierarchy:(UIView*)rootView associateWithViewHierarchy:(UIView*)associatedRootView { - NSMutableDictionary *table = [NSMutableDictionary dictionary]; - [self addAttributesForSubviewHierarchy:rootView associatedWithSubviewHierarchy:associatedRootView toTable:table]; - return table; + NSMutableDictionary *table = [NSMutableDictionary dictionary]; + [self addAttributesForSubviewHierarchy:rootView associatedWithSubviewHierarchy:associatedRootView toTable:table]; + return table; } - (void)addAttributesForSubviewHierarchy:(UIView*)view associatedWithSubviewHierarchy:(UIView*)associatedView toTable:(NSMutableDictionary*)table { - [table setObject:[self attributesForView:view] forKey:[NSValue valueWithPointer:(__bridge const void *)(associatedView)]]; - - if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; - - for ( UIView *subview in view.subviews ) { - UIView *associatedSubView = (view == associatedView ? subview : [self findAssociatedViewForView:subview amongViews:associatedView.subviews]); - if ( associatedSubView ) { - [self addAttributesForSubviewHierarchy:subview associatedWithSubviewHierarchy:associatedSubView toTable:table]; - } - } + [table setObject:[self attributesForView:view] forKey:[NSValue valueWithPointer:(__bridge const void *)(associatedView)]]; + + if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; + + for ( UIView *subview in view.subviews ) { + UIView *associatedSubView = (view == associatedView ? subview : [self findAssociatedViewForView:subview amongViews:associatedView.subviews]); + if ( associatedSubView ) { + [self addAttributesForSubviewHierarchy:subview associatedWithSubviewHierarchy:associatedSubView toTable:table]; + } + } } - (UIView*)findAssociatedViewForView:(UIView*)view amongViews:(NSArray*)views { - // First try to match tag - if ( view.tag != 0 ) { - UIView *associatedView = [[views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"tag = %d", view.tag]] lastObject]; - if ( associatedView ) return associatedView; - } - - // Next, try to match class, targets and actions, if it's a control - if ( [view isKindOfClass:[UIControl class]] && [[(UIControl*)view allTargets] count] > 0 ) { - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] - && [[(UIControl*)otherView allTargets] isEqualToSet:[(UIControl*)view allTargets]] - && [(UIControl*)otherView allControlEvents] == [(UIControl*)view allControlEvents] ) { - // Try to match all actions and targets for each associated control event - BOOL allActionsMatch = YES; - UIControlEvents controlEvents = [(UIControl*)otherView allControlEvents]; - for ( id target in [(UIControl*)otherView allTargets] ) { - // Iterate over each bit in the UIControlEvents bitfield - for ( NSInteger i=0; i<(NSInteger)sizeof(UIControlEvents)*8; i++ ) { - UIControlEvents event = 1 << i; - if ( !(controlEvents & event) ) continue; - if ( ![[(UIControl*)otherView actionsForTarget:target forControlEvent:event] isEqualToArray:[(UIControl*)view actionsForTarget:target forControlEvent:event]] ) { - allActionsMatch = NO; - break; - } - } - if ( !allActionsMatch ) break; - } - - if ( allActionsMatch ) { - return otherView; - } - } - } - } - - // Next, try to match title or image, if it's a button - if ( [view isKindOfClass:[UIButton class]] ) { - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] && [[(UIButton*)otherView titleForState:UIControlStateNormal] isEqualToString:[(UIButton*)view titleForState:UIControlStateNormal]] ) { - return otherView; - } - } - - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] && [(UIButton*)otherView imageForState:UIControlStateNormal] == [(UIButton*)view imageForState:UIControlStateNormal] ) { - return otherView; - } - } - } - - // Try to match by title if it's a label - if ( [view isKindOfClass:[UILabel class]] ) { - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] && [[(UILabel*)otherView text] isEqualToString:[(UILabel*)view text]] ) { - return otherView; + // First try to match tag + if ( view.tag != 0 ) { + UIView *associatedView = [[views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"tag = %d", view.tag]] lastObject]; + if ( associatedView ) return associatedView; + } + + // Next, try to match class, targets and actions, if it's a control + if ( [view isKindOfClass:[UIControl class]] && [[(UIControl*)view allTargets] count] > 0 ) { + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] + && [[(UIControl*)otherView allTargets] isEqualToSet:[(UIControl*)view allTargets]] + && [(UIControl*)otherView allControlEvents] == [(UIControl*)view allControlEvents] ) { + // Try to match all actions and targets for each associated control event + BOOL allActionsMatch = YES; + UIControlEvents controlEvents = [(UIControl*)otherView allControlEvents]; + for ( id target in [(UIControl*)otherView allTargets] ) { + // Iterate over each bit in the UIControlEvents bitfield + for ( NSInteger i=0; i<(NSInteger)sizeof(UIControlEvents)*8; i++ ) { + UIControlEvents event = 1 << i; + if ( !(controlEvents & event) ) continue; + if ( ![[(UIControl*)otherView actionsForTarget:target forControlEvent:event] isEqualToArray:[(UIControl*)view actionsForTarget:target forControlEvent:event]] ) { + allActionsMatch = NO; + break; + } + } + if ( !allActionsMatch ) break; } - } - } - - // Try to match by text/placeholder if it's a text field - if ( [view isKindOfClass:[UITextField class]] ) { - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] && ([(UITextField*)view text] || [(UITextField*)view placeholder]) && - ((![(UITextField*)view text] && ![(UITextField*)otherView text]) || [[(UITextField*)otherView text] isEqualToString:[(UITextField*)view text]]) && - ((![(UITextField*)view placeholder] && ![(UITextField*)otherView placeholder]) || [[(UITextField*)otherView placeholder] isEqualToString:[(UITextField*)view placeholder]]) ) { - return otherView; + + if ( allActionsMatch ) { + return otherView; } - } - } - - // Finally, try to match by class - NSArray *matches = [views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"class = %@", [view class]]]; - if ( [matches count] == 1 ) return [matches lastObject]; - + } + } + } + + // Next, try to match title or image, if it's a button + if ( [view isKindOfClass:[UIButton class]] ) { + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] && [[(UIButton*)otherView titleForState:UIControlStateNormal] isEqualToString:[(UIButton*)view titleForState:UIControlStateNormal]] ) { + return otherView; + } + } + + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] && [(UIButton*)otherView imageForState:UIControlStateNormal] == [(UIButton*)view imageForState:UIControlStateNormal] ) { + return otherView; + } + } + } + + // Try to match by title if it's a label + if ( [view isKindOfClass:[UILabel class]] ) { + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] && [[(UILabel*)otherView text] isEqualToString:[(UILabel*)view text]] ) { + return otherView; + } + } + } + + // Try to match by text/placeholder if it's a text field + if ( [view isKindOfClass:[UITextField class]] ) { + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] && ([(UITextField*)view text] || [(UITextField*)view placeholder]) && + ((![(UITextField*)view text] && ![(UITextField*)otherView text]) || [[(UITextField*)otherView text] isEqualToString:[(UITextField*)view text]]) && + ((![(UITextField*)view placeholder] && ![(UITextField*)otherView placeholder]) || [[(UITextField*)otherView placeholder] isEqualToString:[(UITextField*)view placeholder]]) ) { + return otherView; + } + } + } + + // Finally, try to match by class + NSArray *matches = [views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"class = %@", [view class]]]; + if ( [matches count] == 1 ) return [matches lastObject]; + #if VERBOSE_MATCH_FAIL - NSMutableString *path = [NSMutableString string]; - for ( UIView *v = view.superview; v != nil; v = v.superview ) { - [path insertString:[NSString stringWithFormat:@"%@ => ", NSStringFromClass([v class])] atIndex:0]; - } - NSLog(@"Couldn't find match for %@%@", path, NSStringFromClass([view class])); - + NSMutableString *path = [NSMutableString string]; + for ( UIView *v = view.superview; v != nil; v = v.superview ) { + [path insertString:[NSString stringWithFormat:@"%@ => ", NSStringFromClass([v class])] atIndex:0]; + } + NSLog(@"Couldn't find match for %@%@", path, NSStringFromClass([view class])); + #endif - - return nil; + + return nil; } - (void)applyAttributeTable:(NSDictionary*)table toViewHierarchy:(UIView*)view { - NSDictionary *attributes = [table objectForKey:[NSValue valueWithPointer:(__bridge const void *)(view)]]; - if ( attributes ) { - [self applyAttributes:attributes toView:view]; - } - - if ( view.hidden ) return; - - if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; - - for ( UIView *subview in view.subviews ) { - [self applyAttributeTable:table toViewHierarchy:subview]; - } + + + NSDictionary *attributes = [table objectForKey:[NSValue valueWithPointer:(__bridge const void *)(view)]]; + if ( attributes ) { + [self applyAttributes:attributes toView:view]; + } + + if ( view.hidden ) return; + + if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; + + for ( UIView *subview in view.subviews ) { + [self applyAttributeTable:table toViewHierarchy:subview]; + } } - (NSDictionary*)attributesForView:(UIView*)view { - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - - [attributes setObject:[NSValue valueWithCGRect:view.frame] forKey:@"frame"]; - [attributes setObject:[NSValue valueWithCGRect:view.bounds] forKey:@"bounds"]; - [attributes setObject:[NSNumber numberWithBool:view.hidden] forKey:@"hidden"]; - [attributes setObject:[NSNumber numberWithInteger:view.autoresizingMask] forKey:@"autoresizingMask"]; - - return attributes; + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + + [attributes setObject:[NSValue valueWithCGRect:view.frame] forKey:@"frame"]; + [attributes setObject:[NSValue valueWithCGRect:view.bounds] forKey:@"bounds"]; + [attributes setObject:[NSNumber numberWithBool:view.hidden] forKey:@"hidden"]; + [attributes setObject:[NSNumber numberWithInteger:view.autoresizingMask] forKey:@"autoresizingMask"]; + + return attributes; } - (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view { - view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; - view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; - view.hidden = [[attributes objectForKey:@"hidden"] boolValue]; - view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; + view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; + view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; + view.hidden = [[attributes objectForKey:@"hidden"] boolValue]; + view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; } - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view { - if ( [view isKindOfClass:[UISlider class]] || - [view isKindOfClass:[UISwitch class]] || - [view isKindOfClass:[UITextField class]] || - [view isKindOfClass:[UIWebView class]] || - [view isKindOfClass:[UITableView class]] || - [view isKindOfClass:[UIPickerView class]] || - [view isKindOfClass:[UIDatePicker class]] || - [view isKindOfClass:[UITextView class]] || - [view isKindOfClass:[UIProgressView class]] || - [view isKindOfClass:[UISegmentedControl class]] ) return NO; - return YES; + if ( [view isKindOfClass:[UISlider class]] || + [view isKindOfClass:[UISwitch class]] || + [view isKindOfClass:[UITextField class]] || + [view isKindOfClass:[UIWebView class]] || + [view isKindOfClass:[UITableView class]] || + [view isKindOfClass:[UIPickerView class]] || + [view isKindOfClass:[UIDatePicker class]] || + [view isKindOfClass:[UITextView class]] || + [view isKindOfClass:[UIProgressView class]] || + [view isKindOfClass:[UISegmentedControl class]] ) return NO; + return YES; } @end From dd7a8e3a65a692e4ad55cafc2265535337e449f1 Mon Sep 17 00:00:00 2001 From: Andy Molloy Date: Tue, 25 Sep 2012 16:48:58 -0400 Subject: [PATCH 3/9] Undo formatting --- TPMultiLayoutViewController.m | 308 +++++++++++++++++----------------- 1 file changed, 153 insertions(+), 155 deletions(-) diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index 39f7629..00a73ef 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -29,203 +29,201 @@ @implementation TPMultiLayoutViewController #pragma mark - View lifecycle - (void)viewDidLoad { - [super viewDidLoad]; - - // Construct attribute tables - self.portraitAttributes = [self attributeTableForViewHierarchy:self.portraitView associateWithViewHierarchy:self.view]; - self.landscapeAttributes = [self attributeTableForViewHierarchy:self.landscapeView associateWithViewHierarchy:self.view]; - self.viewIsCurrentlyPortrait = (self.view == self.portraitView); - - // Don't need to retain the original template view hierarchies any more - self.portraitView = nil; - self.landscapeView = nil; + [super viewDidLoad]; + + // Construct attribute tables + self.portraitAttributes = [self attributeTableForViewHierarchy:self.portraitView associateWithViewHierarchy:self.view]; + self.landscapeAttributes = [self attributeTableForViewHierarchy:self.landscapeView associateWithViewHierarchy:self.view]; + self.viewIsCurrentlyPortrait = (self.view == self.portraitView); + + // Don't need to retain the original template view hierarchies any more + self.portraitView = nil; + self.landscapeView = nil; } - (void)viewDidUnload { - [super viewDidUnload]; + [super viewDidUnload]; - self.portraitAttributes = nil; - self.landscapeAttributes = nil; + self.portraitAttributes = nil; + self.landscapeAttributes = nil; } -(void)viewWillAppear:(BOOL)animated { - // Display correct layout for orientation - if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !self.viewIsCurrentlyPortrait) || - (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && self.viewIsCurrentlyPortrait) ) { - [self applyLayoutForInterfaceOrientation:self.interfaceOrientation]; - } + // Display correct layout for orientation + if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !self.viewIsCurrentlyPortrait) || + (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && self.viewIsCurrentlyPortrait) ) { + [self applyLayoutForInterfaceOrientation:self.interfaceOrientation]; + } } #pragma mark - Rotation - (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation { - NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? self.portraitAttributes : self.landscapeAttributes; - [self applyAttributeTable:table toViewHierarchy:self.view]; - self.viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); + NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? self.portraitAttributes : self.landscapeAttributes; + [self applyAttributeTable:table toViewHierarchy:self.view]; + self.viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); } -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !self.viewIsCurrentlyPortrait) || - (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && self.viewIsCurrentlyPortrait) ) { - [self applyLayoutForInterfaceOrientation:toInterfaceOrientation]; - } + if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !self.viewIsCurrentlyPortrait) || + (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && self.viewIsCurrentlyPortrait) ) { + [self applyLayoutForInterfaceOrientation:toInterfaceOrientation]; + } } #pragma mark - Helpers - (NSDictionary*)attributeTableForViewHierarchy:(UIView*)rootView associateWithViewHierarchy:(UIView*)associatedRootView { - NSMutableDictionary *table = [NSMutableDictionary dictionary]; - [self addAttributesForSubviewHierarchy:rootView associatedWithSubviewHierarchy:associatedRootView toTable:table]; - return table; + NSMutableDictionary *table = [NSMutableDictionary dictionary]; + [self addAttributesForSubviewHierarchy:rootView associatedWithSubviewHierarchy:associatedRootView toTable:table]; + return table; } - (void)addAttributesForSubviewHierarchy:(UIView*)view associatedWithSubviewHierarchy:(UIView*)associatedView toTable:(NSMutableDictionary*)table { - [table setObject:[self attributesForView:view] forKey:[NSValue valueWithPointer:(__bridge const void *)(associatedView)]]; - - if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; - - for ( UIView *subview in view.subviews ) { - UIView *associatedSubView = (view == associatedView ? subview : [self findAssociatedViewForView:subview amongViews:associatedView.subviews]); - if ( associatedSubView ) { - [self addAttributesForSubviewHierarchy:subview associatedWithSubviewHierarchy:associatedSubView toTable:table]; - } - } + [table setObject:[self attributesForView:view] forKey:[NSValue valueWithPointer:(__bridge const void *)(associatedView)]]; + + if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; + + for ( UIView *subview in view.subviews ) { + UIView *associatedSubView = (view == associatedView ? subview : [self findAssociatedViewForView:subview amongViews:associatedView.subviews]); + if ( associatedSubView ) { + [self addAttributesForSubviewHierarchy:subview associatedWithSubviewHierarchy:associatedSubView toTable:table]; + } + } } - (UIView*)findAssociatedViewForView:(UIView*)view amongViews:(NSArray*)views { - // First try to match tag - if ( view.tag != 0 ) { - UIView *associatedView = [[views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"tag = %d", view.tag]] lastObject]; - if ( associatedView ) return associatedView; - } - - // Next, try to match class, targets and actions, if it's a control - if ( [view isKindOfClass:[UIControl class]] && [[(UIControl*)view allTargets] count] > 0 ) { - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] - && [[(UIControl*)otherView allTargets] isEqualToSet:[(UIControl*)view allTargets]] - && [(UIControl*)otherView allControlEvents] == [(UIControl*)view allControlEvents] ) { - // Try to match all actions and targets for each associated control event - BOOL allActionsMatch = YES; - UIControlEvents controlEvents = [(UIControl*)otherView allControlEvents]; - for ( id target in [(UIControl*)otherView allTargets] ) { - // Iterate over each bit in the UIControlEvents bitfield - for ( NSInteger i=0; i<(NSInteger)sizeof(UIControlEvents)*8; i++ ) { - UIControlEvents event = 1 << i; - if ( !(controlEvents & event) ) continue; - if ( ![[(UIControl*)otherView actionsForTarget:target forControlEvent:event] isEqualToArray:[(UIControl*)view actionsForTarget:target forControlEvent:event]] ) { - allActionsMatch = NO; - break; - } - } - if ( !allActionsMatch ) break; + // First try to match tag + if ( view.tag != 0 ) { + UIView *associatedView = [[views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"tag = %d", view.tag]] lastObject]; + if ( associatedView ) return associatedView; + } + + // Next, try to match class, targets and actions, if it's a control + if ( [view isKindOfClass:[UIControl class]] && [[(UIControl*)view allTargets] count] > 0 ) { + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] + && [[(UIControl*)otherView allTargets] isEqualToSet:[(UIControl*)view allTargets]] + && [(UIControl*)otherView allControlEvents] == [(UIControl*)view allControlEvents] ) { + // Try to match all actions and targets for each associated control event + BOOL allActionsMatch = YES; + UIControlEvents controlEvents = [(UIControl*)otherView allControlEvents]; + for ( id target in [(UIControl*)otherView allTargets] ) { + // Iterate over each bit in the UIControlEvents bitfield + for ( NSInteger i=0; i<(NSInteger)sizeof(UIControlEvents)*8; i++ ) { + UIControlEvents event = 1 << i; + if ( !(controlEvents & event) ) continue; + if ( ![[(UIControl*)otherView actionsForTarget:target forControlEvent:event] isEqualToArray:[(UIControl*)view actionsForTarget:target forControlEvent:event]] ) { + allActionsMatch = NO; + break; + } + } + if ( !allActionsMatch ) break; + } + + if ( allActionsMatch ) { + return otherView; + } } - - if ( allActionsMatch ) { - return otherView; + } + } + + // Next, try to match title or image, if it's a button + if ( [view isKindOfClass:[UIButton class]] ) { + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] && [[(UIButton*)otherView titleForState:UIControlStateNormal] isEqualToString:[(UIButton*)view titleForState:UIControlStateNormal]] ) { + return otherView; } - } - } - } - - // Next, try to match title or image, if it's a button - if ( [view isKindOfClass:[UIButton class]] ) { - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] && [[(UIButton*)otherView titleForState:UIControlStateNormal] isEqualToString:[(UIButton*)view titleForState:UIControlStateNormal]] ) { - return otherView; - } - } - - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] && [(UIButton*)otherView imageForState:UIControlStateNormal] == [(UIButton*)view imageForState:UIControlStateNormal] ) { - return otherView; - } - } - } - - // Try to match by title if it's a label - if ( [view isKindOfClass:[UILabel class]] ) { - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] && [[(UILabel*)otherView text] isEqualToString:[(UILabel*)view text]] ) { - return otherView; - } - } - } - - // Try to match by text/placeholder if it's a text field - if ( [view isKindOfClass:[UITextField class]] ) { - for ( UIView *otherView in views ) { - if ( [otherView isKindOfClass:[view class]] && ([(UITextField*)view text] || [(UITextField*)view placeholder]) && - ((![(UITextField*)view text] && ![(UITextField*)otherView text]) || [[(UITextField*)otherView text] isEqualToString:[(UITextField*)view text]]) && - ((![(UITextField*)view placeholder] && ![(UITextField*)otherView placeholder]) || [[(UITextField*)otherView placeholder] isEqualToString:[(UITextField*)view placeholder]]) ) { - return otherView; - } - } - } - - // Finally, try to match by class - NSArray *matches = [views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"class = %@", [view class]]]; - if ( [matches count] == 1 ) return [matches lastObject]; - + } + + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] && [(UIButton*)otherView imageForState:UIControlStateNormal] == [(UIButton*)view imageForState:UIControlStateNormal] ) { + return otherView; + } + } + } + + // Try to match by title if it's a label + if ( [view isKindOfClass:[UILabel class]] ) { + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] && [[(UILabel*)otherView text] isEqualToString:[(UILabel*)view text]] ) { + return otherView; + } + } + } + + // Try to match by text/placeholder if it's a text field + if ( [view isKindOfClass:[UITextField class]] ) { + for ( UIView *otherView in views ) { + if ( [otherView isKindOfClass:[view class]] && ([(UITextField*)view text] || [(UITextField*)view placeholder]) && + ((![(UITextField*)view text] && ![(UITextField*)otherView text]) || [[(UITextField*)otherView text] isEqualToString:[(UITextField*)view text]]) && + ((![(UITextField*)view placeholder] && ![(UITextField*)otherView placeholder]) || [[(UITextField*)otherView placeholder] isEqualToString:[(UITextField*)view placeholder]]) ) { + return otherView; + } + } + } + + // Finally, try to match by class + NSArray *matches = [views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"class = %@", [view class]]]; + if ( [matches count] == 1 ) return [matches lastObject]; + #if VERBOSE_MATCH_FAIL - NSMutableString *path = [NSMutableString string]; - for ( UIView *v = view.superview; v != nil; v = v.superview ) { - [path insertString:[NSString stringWithFormat:@"%@ => ", NSStringFromClass([v class])] atIndex:0]; - } - NSLog(@"Couldn't find match for %@%@", path, NSStringFromClass([view class])); - + NSMutableString *path = [NSMutableString string]; + for ( UIView *v = view.superview; v != nil; v = v.superview ) { + [path insertString:[NSString stringWithFormat:@"%@ => ", NSStringFromClass([v class])] atIndex:0]; + } + NSLog(@"Couldn't find match for %@%@", path, NSStringFromClass([view class])); + #endif - - return nil; + + return nil; } - (void)applyAttributeTable:(NSDictionary*)table toViewHierarchy:(UIView*)view { - - - NSDictionary *attributes = [table objectForKey:[NSValue valueWithPointer:(__bridge const void *)(view)]]; - if ( attributes ) { - [self applyAttributes:attributes toView:view]; - } - - if ( view.hidden ) return; - - if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; - - for ( UIView *subview in view.subviews ) { - [self applyAttributeTable:table toViewHierarchy:subview]; - } + NSDictionary *attributes = [table objectForKey:[NSValue valueWithPointer:(__bridge const void *)(view)]]; + if ( attributes ) { + [self applyAttributes:attributes toView:view]; + } + + if ( view.hidden ) return; + + if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; + + for ( UIView *subview in view.subviews ) { + [self applyAttributeTable:table toViewHierarchy:subview]; + } } - (NSDictionary*)attributesForView:(UIView*)view { - NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; - - [attributes setObject:[NSValue valueWithCGRect:view.frame] forKey:@"frame"]; - [attributes setObject:[NSValue valueWithCGRect:view.bounds] forKey:@"bounds"]; - [attributes setObject:[NSNumber numberWithBool:view.hidden] forKey:@"hidden"]; - [attributes setObject:[NSNumber numberWithInteger:view.autoresizingMask] forKey:@"autoresizingMask"]; - - return attributes; + NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; + + [attributes setObject:[NSValue valueWithCGRect:view.frame] forKey:@"frame"]; + [attributes setObject:[NSValue valueWithCGRect:view.bounds] forKey:@"bounds"]; + [attributes setObject:[NSNumber numberWithBool:view.hidden] forKey:@"hidden"]; + [attributes setObject:[NSNumber numberWithInteger:view.autoresizingMask] forKey:@"autoresizingMask"]; + + return attributes; } - (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view { - view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; - view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; - view.hidden = [[attributes objectForKey:@"hidden"] boolValue]; - view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; + view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; + view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; + view.hidden = [[attributes objectForKey:@"hidden"] boolValue]; + view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; } - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view { - if ( [view isKindOfClass:[UISlider class]] || - [view isKindOfClass:[UISwitch class]] || - [view isKindOfClass:[UITextField class]] || - [view isKindOfClass:[UIWebView class]] || - [view isKindOfClass:[UITableView class]] || - [view isKindOfClass:[UIPickerView class]] || - [view isKindOfClass:[UIDatePicker class]] || - [view isKindOfClass:[UITextView class]] || - [view isKindOfClass:[UIProgressView class]] || - [view isKindOfClass:[UISegmentedControl class]] ) return NO; - return YES; + if ( [view isKindOfClass:[UISlider class]] || + [view isKindOfClass:[UISwitch class]] || + [view isKindOfClass:[UITextField class]] || + [view isKindOfClass:[UIWebView class]] || + [view isKindOfClass:[UITableView class]] || + [view isKindOfClass:[UIPickerView class]] || + [view isKindOfClass:[UIDatePicker class]] || + [view isKindOfClass:[UITextView class]] || + [view isKindOfClass:[UIProgressView class]] || + [view isKindOfClass:[UISegmentedControl class]] ) return NO; + return YES; } @end From 3cf75a1760ff8000199fe6e1b13061affdffa130 Mon Sep 17 00:00:00 2001 From: Andy Molloy Date: Sun, 30 Sep 2012 10:33:04 -0400 Subject: [PATCH 4/9] Add a mechanism to allow clients to register view classes they do not wish TPMultiLayoutViewController to descend into. --- TPMultiLayoutViewController.h | 3 +++ TPMultiLayoutViewController.m | 40 ++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/TPMultiLayoutViewController.h b/TPMultiLayoutViewController.h index 08b410b..b7bf090 100644 --- a/TPMultiLayoutViewController.h +++ b/TPMultiLayoutViewController.h @@ -12,6 +12,9 @@ // Call directly to use with custom animation (override willRotateToInterfaceOrientation to disable the switch there) - (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation; +// Call this with the class of custom views you do not wish TPMultiLayoutViewController to descend into. ++(void)registerViewClassToIgnore:(Class)viewClass; + @property (nonatomic, retain) IBOutlet UIView *landscapeView; @property (nonatomic, retain) IBOutlet UIView *portraitView; @end diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index 00a73ef..cbc570c 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -6,6 +6,7 @@ // #import "TPMultiLayoutViewController.h" +#import "HorizontalPicker.h" #define VERBOSE_MATCH_FAIL 1 // Comment this out to be less verbose when associated views can't be found @@ -24,10 +25,38 @@ - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view; @end +static NSMutableSet* sViewClassesToIgnore = nil; + @implementation TPMultiLayoutViewController #pragma mark - View lifecycle ++(void)registerViewClassToIgnore:(Class)viewClass +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sViewClassesToIgnore = [[NSMutableSet alloc] init]; + }); + + [sViewClassesToIgnore addObject:viewClass]; +} + ++(void)initialize +{ + [self registerViewClassToIgnore:[UISlider class]]; + [self registerViewClassToIgnore:[UISwitch class]]; + [self registerViewClassToIgnore:[UITextField class]]; + [self registerViewClassToIgnore:[UIWebView class]]; + [self registerViewClassToIgnore:[UITableView class]]; + [self registerViewClassToIgnore:[UIPickerView class]]; + [self registerViewClassToIgnore:[UIDatePicker class]]; + [self registerViewClassToIgnore:[UITextView class]]; + [self registerViewClassToIgnore:[UIProgressView class]]; + [self registerViewClassToIgnore:[UISegmentedControl class]]; + + [super initialize]; +} + - (void)viewDidLoad { [super viewDidLoad]; @@ -213,16 +242,7 @@ - (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view { } - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view { - if ( [view isKindOfClass:[UISlider class]] || - [view isKindOfClass:[UISwitch class]] || - [view isKindOfClass:[UITextField class]] || - [view isKindOfClass:[UIWebView class]] || - [view isKindOfClass:[UITableView class]] || - [view isKindOfClass:[UIPickerView class]] || - [view isKindOfClass:[UIDatePicker class]] || - [view isKindOfClass:[UITextView class]] || - [view isKindOfClass:[UIProgressView class]] || - [view isKindOfClass:[UISegmentedControl class]] ) return NO; + if ([sViewClassesToIgnore containsObject:[view class]]) return NO; return YES; } From 3cf81081e723375a2a85d317230d83a11e86bc96 Mon Sep 17 00:00:00 2001 From: Andy Molloy Date: Mon, 1 Oct 2012 23:05:03 -0400 Subject: [PATCH 5/9] Don't import a project-specific header that isn't actually used anyway. --- TPMultiLayoutViewController.m | 1 - 1 file changed, 1 deletion(-) diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index cbc570c..a142fd6 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -6,7 +6,6 @@ // #import "TPMultiLayoutViewController.h" -#import "HorizontalPicker.h" #define VERBOSE_MATCH_FAIL 1 // Comment this out to be less verbose when associated views can't be found From e487f6ef732d7279da97c087e00b9833e7e771ea Mon Sep 17 00:00:00 2001 From: Andy Molloy Date: Mon, 1 Oct 2012 23:05:47 -0400 Subject: [PATCH 6/9] Add the ability to ignore views which have a negative tag. --- TPMultiLayoutViewController.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index a142fd6..5701805 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -108,6 +108,11 @@ - (NSDictionary*)attributeTableForViewHierarchy:(UIView*)rootView associateWithV } - (void)addAttributesForSubviewHierarchy:(UIView*)view associatedWithSubviewHierarchy:(UIView*)associatedView toTable:(NSMutableDictionary*)table { + // Ignore views with negative tag + if ( view.tag < 0 ) { + return; + } + [table setObject:[self attributesForView:view] forKey:[NSValue valueWithPointer:(__bridge const void *)(associatedView)]]; if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; From 7e68837985b8d8976df3e52e48fbf8dd3cb7a2c0 Mon Sep 17 00:00:00 2001 From: Andy Molloy Date: Mon, 1 Oct 2012 23:06:15 -0400 Subject: [PATCH 7/9] Properly animate the changes as they are applied. --- TPMultiLayoutViewController.h | 2 +- TPMultiLayoutViewController.m | 33 ++++++++++++++++++--------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/TPMultiLayoutViewController.h b/TPMultiLayoutViewController.h index b7bf090..527db59 100644 --- a/TPMultiLayoutViewController.h +++ b/TPMultiLayoutViewController.h @@ -10,7 +10,7 @@ @interface TPMultiLayoutViewController : UIViewController // Call directly to use with custom animation (override willRotateToInterfaceOrientation to disable the switch there) -- (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation; +- (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation duration:(NSTimeInterval)duration; // Call this with the class of custom views you do not wish TPMultiLayoutViewController to descend into. +(void)registerViewClassToIgnore:(Class)viewClass; diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index 5701805..0ca92ac 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -13,9 +13,9 @@ @interface TPMultiLayoutViewController () - (NSDictionary*)attributeTableForViewHierarchy:(UIView*)rootView associateWithViewHierarchy:(UIView*)associatedRootView; - (void)addAttributesForSubviewHierarchy:(UIView*)view associatedWithSubviewHierarchy:(UIView*)associatedView toTable:(NSMutableDictionary*)table; - (UIView*)findAssociatedViewForView:(UIView*)view amongViews:(NSArray*)views; -- (void)applyAttributeTable:(NSDictionary*)table toViewHierarchy:(UIView*)view; +- (void)applyAttributeTable:(NSDictionary*)table toViewHierarchy:(UIView*)view duration:(NSTimeInterval)duration; - (NSDictionary*)attributesForView:(UIView*)view; -- (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view; +- (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view duration:(NSTimeInterval)duration; - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view; @property (nonatomic, strong) NSDictionary *portraitAttributes; @@ -80,22 +80,22 @@ -(void)viewWillAppear:(BOOL)animated { // Display correct layout for orientation if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !self.viewIsCurrentlyPortrait) || (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && self.viewIsCurrentlyPortrait) ) { - [self applyLayoutForInterfaceOrientation:self.interfaceOrientation]; + [self applyLayoutForInterfaceOrientation:self.interfaceOrientation duration:0]; } } #pragma mark - Rotation -- (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation { +- (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation duration:(NSTimeInterval)duration { NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? self.portraitAttributes : self.landscapeAttributes; - [self applyAttributeTable:table toViewHierarchy:self.view]; + [self applyAttributeTable:table toViewHierarchy:self.view duration:duration]; self.viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); } -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !self.viewIsCurrentlyPortrait) || (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && self.viewIsCurrentlyPortrait) ) { - [self applyLayoutForInterfaceOrientation:toInterfaceOrientation]; + [self applyLayoutForInterfaceOrientation:toInterfaceOrientation duration:duration]; } } @@ -126,7 +126,7 @@ - (void)addAttributesForSubviewHierarchy:(UIView*)view associatedWithSubviewHier } - (UIView*)findAssociatedViewForView:(UIView*)view amongViews:(NSArray*)views { - // First try to match tag + // First try to match tag if ( view.tag != 0 ) { UIView *associatedView = [[views filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"tag = %d", view.tag]] lastObject]; if ( associatedView ) return associatedView; @@ -212,10 +212,10 @@ - (UIView*)findAssociatedViewForView:(UIView*)view amongViews:(NSArray*)views { return nil; } -- (void)applyAttributeTable:(NSDictionary*)table toViewHierarchy:(UIView*)view { +- (void)applyAttributeTable:(NSDictionary*)table toViewHierarchy:(UIView*)view duration:(NSTimeInterval)duration { NSDictionary *attributes = [table objectForKey:[NSValue valueWithPointer:(__bridge const void *)(view)]]; if ( attributes ) { - [self applyAttributes:attributes toView:view]; + [self applyAttributes:attributes toView:view duration:duration]; } if ( view.hidden ) return; @@ -223,7 +223,7 @@ - (void)applyAttributeTable:(NSDictionary*)table toViewHierarchy:(UIView*)view { if ( ![self shouldDescendIntoSubviewsOfView:view] ) return; for ( UIView *subview in view.subviews ) { - [self applyAttributeTable:table toViewHierarchy:subview]; + [self applyAttributeTable:table toViewHierarchy:subview duration:duration]; } } @@ -238,11 +238,14 @@ - (NSDictionary*)attributesForView:(UIView*)view { return attributes; } -- (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view { - view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; - view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; - view.hidden = [[attributes objectForKey:@"hidden"] boolValue]; - view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; +- (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view duration:(NSTimeInterval)duration { + [UIView animateWithDuration:duration + animations:^{ + view.frame = [[attributes objectForKey:@"frame"] CGRectValue]; + view.bounds = [[attributes objectForKey:@"bounds"] CGRectValue]; + view.hidden = [[attributes objectForKey:@"hidden"] boolValue]; + view.autoresizingMask = [[attributes objectForKey:@"autoresizingMask"] integerValue]; + }]; } - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view { From d982e151ce6d767a9407973a0454e109027ee053 Mon Sep 17 00:00:00 2001 From: Andy Molloy Date: Mon, 1 Oct 2012 23:06:33 -0400 Subject: [PATCH 8/9] Documentation for some of the new features I've added. --- README.markdown | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.markdown b/README.markdown index c962f9d..16446b1 100644 --- a/README.markdown +++ b/README.markdown @@ -47,6 +47,12 @@ rearrange the view, using the layout we extracted from our original two views. 4. Title (for UILabel) 5. Text or placeholder (for UITextField) 6. Class +- If you wish a view to be ignored by TPMultiLayoutViewController, assign it a negative tag. +- You can register view classes TPMultiLayoutViewController should not descend into by calling + + [TPMultiLayoutViewContoller registerViewClassToIgnore:*yourclass*]; + + A good place to do this is in your class' -(void)initialize method. If you experience odd behaviour, check the log for "Couldn't find match..." messages. If a view cannot be matched to its counterpart, try setting the same tag for both views. From 4567623c5ece42fd0ad1c6636377ed6c51c94b00 Mon Sep 17 00:00:00 2001 From: Andy Molloy Date: Tue, 9 Oct 2012 12:03:50 -0400 Subject: [PATCH 9/9] Don't implement viewDidUnload as it is now deprecated and Apple even recommends against its use in earlier versions of iOS. Also, keep references to the portrait and landscape views around as they may be useful to subclasses. --- TPMultiLayoutViewController.m | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index 0ca92ac..242b1eb 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -63,17 +63,6 @@ - (void)viewDidLoad { self.portraitAttributes = [self attributeTableForViewHierarchy:self.portraitView associateWithViewHierarchy:self.view]; self.landscapeAttributes = [self attributeTableForViewHierarchy:self.landscapeView associateWithViewHierarchy:self.view]; self.viewIsCurrentlyPortrait = (self.view == self.portraitView); - - // Don't need to retain the original template view hierarchies any more - self.portraitView = nil; - self.landscapeView = nil; -} - -- (void)viewDidUnload { - [super viewDidUnload]; - - self.portraitAttributes = nil; - self.landscapeAttributes = nil; } -(void)viewWillAppear:(BOOL)animated {