123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- //
- // IQUIView+Hierarchy.m
- // https://github.com/hackiftekhar/IQKeyboardManager
- // Copyright (c) 2013-16 Iftekhar Qurashi.
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- #import "IQUIView+Hierarchy.h"
- #import "IQUITextFieldView+Additions.h"
- #import <UIKit/UICollectionView.h>
- #import <UIKit/UIAlertController.h>
- #import <UIKit/UITableView.h>
- #import <UIKit/UITextView.h>
- #import <UIKit/UITextField.h>
- #import <UIKit/UISearchBar.h>
- #import <UIKit/UIViewController.h>
- #import <UIKit/UIWindow.h>
- #import <objc/runtime.h>
- #import "IQNSArray+Sort.h"
- @implementation UIView (IQ_UIView_Hierarchy)
- -(UIViewController*)viewController
- {
- UIResponder *nextResponder = self;
-
- do
- {
- nextResponder = [nextResponder nextResponder];
- if ([nextResponder isKindOfClass:[UIViewController class]])
- return (UIViewController*)nextResponder;
- } while (nextResponder != nil);
- return nil;
- }
- -(UIViewController *)topMostController
- {
- NSMutableArray<UIViewController*> *controllersHierarchy = [[NSMutableArray alloc] init];
-
- UIViewController *topController = self.window.rootViewController;
-
- if (topController)
- {
- [controllersHierarchy addObject:topController];
- }
-
- while ([topController presentedViewController]) {
-
- topController = [topController presentedViewController];
- [controllersHierarchy addObject:topController];
- }
-
- UIViewController *matchController = [self viewController];
-
- while (matchController != nil && [controllersHierarchy containsObject:matchController] == NO)
- {
- do
- {
- matchController = (UIViewController*)[matchController nextResponder];
-
- } while (matchController != nil && [matchController isKindOfClass:[UIViewController class]] == NO);
- }
-
- return (UIViewController*)matchController;
- }
- -(UIView*)superviewOfClassType:(Class)classType
- {
- UIView *superview = self.superview;
-
- while (superview)
- {
- if ([superview isKindOfClass:classType])
- {
- //If it's UIScrollView, then validating for special cases
- if ([superview isKindOfClass:[UIScrollView class]])
- {
- NSString *classNameString = NSStringFromClass([superview class]);
- // If it's not UITableViewWrapperView class, this is internal class which is actually manage in UITableview. The speciality of this class is that it's superview is UITableView.
- // If it's not UITableViewCellScrollView class, this is internal class which is actually manage in UITableviewCell. The speciality of this class is that it's superview is UITableViewCell.
- //If it's not _UIQueuingScrollView class, actually we validate for _ prefix which usually used by Apple internal classes
- if ([superview.superview isKindOfClass:[UITableView class]] == NO &&
- [superview.superview isKindOfClass:[UITableViewCell class]] == NO &&
- [classNameString hasPrefix:@"_"] == NO)
- {
- return superview;
- }
- }
- else
- {
- return superview;
- }
- }
-
- superview = superview.superview;
- }
-
- return nil;
- }
- -(BOOL)_IQcanBecomeFirstResponder
- {
- BOOL _IQcanBecomeFirstResponder = NO;
-
- if ([self isKindOfClass:[UITextField class]])
- {
- _IQcanBecomeFirstResponder = [(UITextField*)self isEnabled];
- }
- else if ([self isKindOfClass:[UITextView class]])
- {
- _IQcanBecomeFirstResponder = [(UITextView*)self isEditable];
- }
- if (_IQcanBecomeFirstResponder == YES)
- {
- _IQcanBecomeFirstResponder = ([self isUserInteractionEnabled] && ![self isHidden] && [self alpha]!=0.0 && ![self isAlertViewTextField] && ![self isSearchBarTextField]);
- }
-
- return _IQcanBecomeFirstResponder;
- }
- - (NSArray*)responderSiblings
- {
- // Getting all siblings
- NSArray *siblings = self.superview.subviews;
-
- //Array of (UITextField/UITextView's).
- NSMutableArray<UIView*> *tempTextFields = [[NSMutableArray alloc] init];
-
- for (UIView *textField in siblings)
- if ((textField == self || textField.ignoreSwitchingByNextPrevious == NO) && [textField _IQcanBecomeFirstResponder])
- [tempTextFields addObject:textField];
-
- return tempTextFields;
- }
- - (NSArray*)deepResponderViews
- {
- NSMutableArray<UIView*> *textFields = [[NSMutableArray alloc] init];
-
- for (UIView *textField in self.subviews)
- {
- if ((textField == self || textField.ignoreSwitchingByNextPrevious == NO) && [textField _IQcanBecomeFirstResponder])
- {
- [textFields addObject:textField];
- }
-
- //Sometimes there are hidden or disabled views and textField inside them still recorded, so we added some more validations here (Bug ID: #458)
- //Uncommented else (Bug ID: #625)
- if (textField.subviews.count && [textField isUserInteractionEnabled] && ![textField isHidden] && [textField alpha]!=0.0)
- {
- [textFields addObjectsFromArray:[textField deepResponderViews]];
- }
- }
- //subviews are returning in incorrect order. Sorting according the frames 'y'.
- return [textFields sortedArrayUsingComparator:^NSComparisonResult(UIView *view1, UIView *view2) {
-
- CGRect frame1 = [view1 convertRect:view1.bounds toView:self];
- CGRect frame2 = [view2 convertRect:view2.bounds toView:self];
-
- CGFloat x1 = CGRectGetMinX(frame1);
- CGFloat y1 = CGRectGetMinY(frame1);
- CGFloat x2 = CGRectGetMinX(frame2);
- CGFloat y2 = CGRectGetMinY(frame2);
-
- if (y1 < y2) return NSOrderedAscending;
-
- else if (y1 > y2) return NSOrderedDescending;
-
- //Else both y are same so checking for x positions
- else if (x1 < x2) return NSOrderedAscending;
-
- else if (x1 > x2) return NSOrderedDescending;
-
- else return NSOrderedSame;
- }];
- return textFields;
- }
- -(CGAffineTransform)convertTransformToView:(UIView*)toView
- {
- if (toView == nil)
- {
- toView = self.window;
- }
-
- CGAffineTransform myTransform = CGAffineTransformIdentity;
-
- //My Transform
- {
- UIView *superView = [self superview];
-
- if (superView) myTransform = CGAffineTransformConcat(self.transform, [superView convertTransformToView:nil]);
- else myTransform = self.transform;
- }
-
- CGAffineTransform viewTransform = CGAffineTransformIdentity;
-
- //view Transform
- {
- UIView *superView = [toView superview];
-
- if (superView) viewTransform = CGAffineTransformConcat(toView.transform, [superView convertTransformToView:nil]);
- else if (toView) viewTransform = toView.transform;
- }
-
- return CGAffineTransformConcat(myTransform, CGAffineTransformInvert(viewTransform));
- }
- - (NSInteger)depth
- {
- NSInteger depth = 0;
-
- if ([self superview])
- {
- depth = [[self superview] depth] + 1;
- }
-
- return depth;
- }
- - (NSString *)subHierarchy
- {
- NSMutableString *debugInfo = [[NSMutableString alloc] initWithString:@"\n"];
- NSInteger depth = [self depth];
-
- for (int counter = 0; counter < depth; counter ++) [debugInfo appendString:@"| "];
-
- [debugInfo appendString:[self debugHierarchy]];
-
- for (UIView *subview in self.subviews)
- {
- [debugInfo appendString:[subview subHierarchy]];
- }
-
- return debugInfo;
- }
- - (NSString *)superHierarchy
- {
- NSMutableString *debugInfo = [[NSMutableString alloc] init];
- if (self.superview)
- {
- [debugInfo appendString:[self.superview superHierarchy]];
- }
- else
- {
- [debugInfo appendString:@"\n"];
- }
-
- NSInteger depth = [self depth];
-
- for (int counter = 0; counter < depth; counter ++) [debugInfo appendString:@"| "];
-
- [debugInfo appendString:[self debugHierarchy]];
- [debugInfo appendString:@"\n"];
-
- return debugInfo;
- }
- -(NSString *)debugHierarchy
- {
- NSMutableString *debugInfo = [[NSMutableString alloc] init];
- [debugInfo appendFormat:@"%@: ( %.0f, %.0f, %.0f, %.0f )",NSStringFromClass([self class]), CGRectGetMinX(self.frame), CGRectGetMinY(self.frame), CGRectGetWidth(self.frame), CGRectGetHeight(self.frame)];
-
- if ([self isKindOfClass:[UIScrollView class]])
- {
- UIScrollView *scrollView = (UIScrollView*)self;
- [debugInfo appendFormat:@"%@: ( %.0f, %.0f )",NSStringFromSelector(@selector(contentSize)),scrollView.contentSize.width,scrollView.contentSize.height];
- }
-
- if (CGAffineTransformEqualToTransform(self.transform, CGAffineTransformIdentity) == false)
- {
- [debugInfo appendFormat:@"%@: %@",NSStringFromSelector(@selector(transform)),NSStringFromCGAffineTransform(self.transform)];
- }
-
- return debugInfo;
- }
- -(BOOL)isSearchBarTextField
- {
- UIResponder *searchBar = [self nextResponder];
-
- BOOL isSearchBarTextField = NO;
- while (searchBar && isSearchBarTextField == NO)
- {
- if ([searchBar isKindOfClass:[UISearchBar class]])
- {
- isSearchBarTextField = YES;
- break;
- }
- else if ([searchBar isKindOfClass:[UIViewController class]]) //If found viewcontroller but still not found UISearchBar then it's not the search bar textfield
- {
- break;
- }
-
- searchBar = [searchBar nextResponder];
- }
-
- return isSearchBarTextField;
- }
- -(BOOL)isAlertViewTextField
- {
- UIResponder *alertViewController = [self viewController];
-
- BOOL isAlertViewTextField = NO;
- while (alertViewController && isAlertViewTextField == NO)
- {
- if ([alertViewController isKindOfClass:[UIAlertController class]])
- {
- isAlertViewTextField = YES;
- break;
- }
- alertViewController = [alertViewController nextResponder];
- }
-
- return isAlertViewTextField;
- }
- @end
- @implementation NSObject (IQ_Logging)
- -(NSString *)_IQDescription
- {
- return [NSString stringWithFormat:@"<%@ %p>",NSStringFromClass([self class]),self];
- }
- @end
|