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. diff --git a/TPMultiLayoutViewController.h b/TPMultiLayoutViewController.h index 02c758a..527db59 100644 --- a/TPMultiLayoutViewController.h +++ b/TPMultiLayoutViewController.h @@ -7,18 +7,13 @@ #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; +- (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; @property (nonatomic, retain) IBOutlet UIView *landscapeView; @property (nonatomic, retain) IBOutlet UIView *portraitView; diff --git a/TPMultiLayoutViewController.m b/TPMultiLayoutViewController.m index 9cb993c..242b1eb 100644 --- a/TPMultiLayoutViewController.m +++ b/TPMultiLayoutViewController.m @@ -13,68 +13,78 @@ @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; +@property (nonatomic, strong) NSDictionary *landscapeAttributes; +@property (nonatomic, assign) BOOL viewIsCurrentlyPortrait; + @end +static NSMutableSet* sViewClassesToIgnore = nil; + @implementation TPMultiLayoutViewController -@synthesize portraitView, landscapeView; #pragma mark - View lifecycle -- (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); - - // Don't need to retain the original template view hierarchies any more - self.portraitView = nil; - self.landscapeView = nil; ++(void)registerViewClassToIgnore:(Class)viewClass +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sViewClassesToIgnore = [[NSMutableSet alloc] init]; + }); + + [sViewClassesToIgnore addObject:viewClass]; } -- (void)viewDidUnload { - [super viewDidUnload]; - - [portraitAttributes release]; - portraitAttributes = nil; - [landscapeAttributes release]; - landscapeAttributes = nil; ++(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)dealloc { - [portraitAttributes release]; - portraitAttributes = nil; - [landscapeAttributes release]; - landscapeAttributes = nil; +- (void)viewDidLoad { + [super viewDidLoad]; - [super dealloc]; + // 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); } -(void)viewWillAppear:(BOOL)animated { // Display correct layout for orientation - if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !viewIsCurrentlyPortrait) || - (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && viewIsCurrentlyPortrait) ) { - [self applyLayoutForInterfaceOrientation:self.interfaceOrientation]; + if ( (UIInterfaceOrientationIsPortrait(self.interfaceOrientation) && !self.viewIsCurrentlyPortrait) || + (UIInterfaceOrientationIsLandscape(self.interfaceOrientation) && self.viewIsCurrentlyPortrait) ) { + [self applyLayoutForInterfaceOrientation:self.interfaceOrientation duration:0]; } } #pragma mark - Rotation -- (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation { - NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? portraitAttributes : landscapeAttributes; - [self applyAttributeTable:table toViewHierarchy:self.view]; - viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); +- (void)applyLayoutForInterfaceOrientation:(UIInterfaceOrientation)newOrientation duration:(NSTimeInterval)duration { + NSDictionary *table = UIInterfaceOrientationIsPortrait(newOrientation) ? self.portraitAttributes : self.landscapeAttributes; + [self applyAttributeTable:table toViewHierarchy:self.view duration:duration]; + self.viewIsCurrentlyPortrait = UIInterfaceOrientationIsPortrait(newOrientation); } -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { - if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !viewIsCurrentlyPortrait) || - (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && viewIsCurrentlyPortrait) ) { - [self applyLayoutForInterfaceOrientation:toInterfaceOrientation]; + if ( (UIInterfaceOrientationIsPortrait(toInterfaceOrientation) && !self.viewIsCurrentlyPortrait) || + (UIInterfaceOrientationIsLandscape(toInterfaceOrientation) && self.viewIsCurrentlyPortrait) ) { + [self applyLayoutForInterfaceOrientation:toInterfaceOrientation duration:duration]; } } @@ -87,7 +97,12 @@ - (NSDictionary*)attributeTableForViewHierarchy:(UIView*)rootView associateWithV } - (void)addAttributesForSubviewHierarchy:(UIView*)view associatedWithSubviewHierarchy:(UIView*)associatedView toTable:(NSMutableDictionary*)table { - [table setObject:[self attributesForView:view] forKey:[NSValue valueWithPointer:associatedView]]; + // 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; @@ -100,7 +115,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; @@ -117,7 +132,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