patternMinor
iOS Utility methods for UIView
Viewed 0 times
iosutilitymethodsuiviewfor
Problem
Lately, I've been part of a team working on a utility library for iOS called Thundercats (on Github). We're about to start working on some changes and upgrades in preparation for a 2.0 release, so we figured it might be a good time to get some of our code publicly reviewed.
The library is written in Objective-C and is set up to work with Cocoapods, but we want to make sure we the library works well from iOS 6.0 and up and with both Swift and Objective-C.
This code is from the
UIView+TCAdditions.h
```
#import
typedef NS_ENUM(NSUInteger, TCSearchStrategy) {
TCSearchStrategyBreadthFirst,
TCSearchStrategyDepthFirst
};
@interface UIView (TCAdditions)
/**
* Calls resignFirstResponder on this view or its subviews (1 level down) if the view is the first responder.
*/
/**
* Returns a flattened subview hierarchy. All subviews within this view's subview hierarchy are returned.
*
* @return An array of all the view's subviews in its subview hierarchy.
*/
/**
* Returns the first subview it encounters that satisfies the condition block. This method uses a breadth first search strategy.
*
* @param conditionBlock The block to apply to each subview.
*
* @return The first subview encountered that satisfies the condition block.
*/
/**
* Returns the first subview it encounters that satisfies the condition block. This method can perform a breadth first search strategy or a depth first search strategy.
*
* @param searchStrategy The search strategy to use.
* @param conditionBlock The block to apply to each subview.
*
* @return The first subview encountered that satisfies the condition block.
*/
The library is written in Objective-C and is set up to work with Cocoapods, but we want to make sure we the library works well from iOS 6.0 and up and with both Swift and Objective-C.
This code is from the
UIView extension. It provides an easy means of doing some common recursive searching.UIView+TCAdditions.h
```
#import
typedef NS_ENUM(NSUInteger, TCSearchStrategy) {
TCSearchStrategyBreadthFirst,
TCSearchStrategyDepthFirst
};
@interface UIView (TCAdditions)
/**
* Calls resignFirstResponder on this view or its subviews (1 level down) if the view is the first responder.
*/
- (void)tc_findAndResignFirstResponder;
/**
* Returns a flattened subview hierarchy. All subviews within this view's subview hierarchy are returned.
*
* @return An array of all the view's subviews in its subview hierarchy.
*/
- (NSArray *)tc_getAllSubviewsRecursively;
/**
* Returns the first subview it encounters that satisfies the condition block. This method uses a breadth first search strategy.
*
* @param conditionBlock The block to apply to each subview.
*
* @return The first subview encountered that satisfies the condition block.
*/
- (UIView )tc_subviewThatSatisfiesBlock:(BOOL (^)(UIView ))conditionBlock;
/**
* Returns the first subview it encounters that satisfies the condition block. This method can perform a breadth first search strategy or a depth first search strategy.
*
* @param searchStrategy The search strategy to use.
* @param conditionBlock The block to apply to each subview.
*
* @return The first subview encountered that satisfies the condition block.
*/
- (UIView *)tc_findSubviewUsingSearchStrategy:(TCSearchStrategy)searchStrategy
Solution
I have only some small suggestions. The
method creates a (temporary) mutable array for each subview in the
hierarchy. It might be more effective to create only a single array
and passing this array around to append subviews recursively:
If you decide to use Xcode 7 and the latest OS X SDK at some point
then you can use the Objective-C "Lightweight Generics" to specify
that the returned array contains
so that this causes a compiler warning:
The
method searches only one level deep for the first responder. You could
implement it using the recursive search as
where the final
the search return
This would become even more simpler if you change the search methods
to include the receiver itself in the search instead of starting
the search at its subviews.
In both
because the method returns in the
indentation level:
- (NSArray *)tc_getAllSubviewsRecursivelymethod creates a (temporary) mutable array for each subview in the
hierarchy. It might be more effective to create only a single array
and passing this array around to append subviews recursively:
- (NSArray *)tc_getAllSubviewsRecursively {
NSMutableArray *subviews = [NSMutableArray new];
[self tc_addSubviewsRecursivelyTo:subviews];
return subviews;
}
// Private helper method:
- (void)tc_addSubviewsRecursivelyTo:(NSMutableArray *)subviews {
for (UIView *subview in self.subviews) {
[subviews addObject:subview];
[subview tc_addSubviewsRecursivelyTo:subviews];
}
}If you decide to use Xcode 7 and the latest OS X SDK at some point
then you can use the Objective-C "Lightweight Generics" to specify
that the returned array contains
UIView objects:- (NSArray *)tc_getAllSubviewsRecursively;so that this causes a compiler warning:
NSString *foo = [self.view tc_getAllSubviewsRecursively][0];
// Incompatible pointer types initializing 'NSString *' with an expression of type 'UIView *'The
- (void)tc_findAndResignFirstResponder;method searches only one level deep for the first responder. You could
implement it using the recursive search as
- (void)tc_findAndResignFirstResponder {
if ([self isFirstResponder]) {
[self resignFirstResponder];
return;
}
UIView *subview = [self tc_subviewThatSatisfiesBlock:^BOOL(UIView *view) {
return [view isFirstResponder];
}];
[subview resignFirstResponder];
}where the final
[subview resignFirstResponder] does nothing ifthe search return
nil.This would become even more simpler if you change the search methods
to include the receiver itself in the search instead of starting
the search at its subviews.
In both
tc_depthFirstSubviewThatSatisfiesBlock: andtc_breadthFirstSubviewThatSatisfiesBlock the else is not necessarybecause the method returns in the
if case. This saves oneindentation level:
- (UIView *)tc_depthFirstSubviewThatSatisfiesBlock:(BOOL (^)(UIView *))conditionBlock
{
for (UIView *subview in self.subviews) {
if (conditionBlock(subview)) {
return subview;
}
UIView *view = [subview tc_depthFirstSubviewThatSatisfiesBlock:conditionBlock];
if (view) {
return view;
}
}
return nil;
}Code Snippets
- (NSArray *)tc_getAllSubviewsRecursively- (NSArray *)tc_getAllSubviewsRecursively {
NSMutableArray *subviews = [NSMutableArray new];
[self tc_addSubviewsRecursivelyTo:subviews];
return subviews;
}
// Private helper method:
- (void)tc_addSubviewsRecursivelyTo:(NSMutableArray *)subviews {
for (UIView *subview in self.subviews) {
[subviews addObject:subview];
[subview tc_addSubviewsRecursivelyTo:subviews];
}
}- (NSArray<UIView *> *)tc_getAllSubviewsRecursively;NSString *foo = [self.view tc_getAllSubviewsRecursively][0];
// Incompatible pointer types initializing 'NSString *' with an expression of type 'UIView *'- (void)tc_findAndResignFirstResponder;Context
StackExchange Code Review Q#97830, answer score: 7
Revisions (0)
No revisions yet.