// // TPMultiLayoutViewController.m // // Created by Michael Tyson on 14/08/2011. // Copyright 2011 A Tasty Pixel. All rights reserved. // #import "TPMultiLayoutViewController.h" #define VERBOSE_MATCH_FAIL 1 // Comment this out to be less verbose when associated views can't be found @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; - (NSDictionary*)attributesForView:(UIView*)view; - (void)applyAttributes:(NSDictionary*)attributes toView:(UIView*)view; - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view; @end @implementation TPMultiLayoutViewController @synthesize portraitView, landscapeView, viewIsCurrentlyPortrait; #pragma mark - View lifecycle - (void)viewDidLoad { [super viewDidLoad]; // Construct attribute tables portraitAttributes = [self attributeTableForViewHierarchy:portraitView associateWithViewHierarchy:self.view]; landscapeAttributes = [self attributeTableForViewHierarchy:landscapeView associateWithViewHierarchy:self.view]; viewIsCurrentlyPortrait = (self.view == portraitView); // Don't need to retain the original template view hierarchies any more self.portraitView = nil; self.landscapeView = nil; } - (void)dealloc { portraitAttributes = nil; landscapeAttributes = nil; } - (void)viewWillAppear:(BOOL)animated { // Display correct layout for orientation /*if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !viewIsCurrentlyPortrait) || (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && viewIsCurrentlyPortrait) ) { [self applyLayoutForInterfaceOrientation:self.interfaceOrientation]; }*/ [super viewWillAppear:animated]; } #pragma mark - Rotation - (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation { NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? portraitAttributes : landscapeAttributes; [self applyAttributeTable:table toViewHierarchy:self.view]; viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); } - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; //if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !viewIsCurrentlyPortrait) || // (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && 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; } - (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]; } } } - (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 ", NSStringFromClass([v class])] atIndex:0]; } NSLog(@"Couldn't find match for %@%@", path, NSStringFromClass([view class])); #endif 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*)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; } - (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]; } - (BOOL)shouldDescendIntoSubviewsOfView:(UIView*)view { if ( [view isKindOfClass:[UISlider class]] || [view isKindOfClass:[UISwitch class]] || [view isKindOfClass:[UITextField 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