UIViewのカテゴリを収集・開発し続けてたら結構肥大化してきたのでtips程度に公開。(GitHubでやるべき?)
あんまり意味ないメソッドや人によっては嫌うようなメソッドもありますが、参考程度に。
※更なる発展の為、突込み大歓迎です!
UIView+Additions.h
//
// UIView+Additions.h
// PowerKit
//
#import <UIKit/UIKit.h>
typedef void (^Block)(void);
/**
* @brief UIViewのカテゴリ拡張
*/
@interface UIView (Additions)
/** @brief self.frame.origin.yへのアクセスを容易にするためのプロパティ。
* @return self.frame.origin.yと同じ値
*/
@property (nonatomic) CGFloat top;
/** @brief (self.frame.size.width + self.frame.origin.x)の値へのアクセスを容易にするためのプロパティ。
* @return (self.frame.size.width + self.frame.origin.x)と同じ値
*/
@property (nonatomic) CGFloat right;
/** @brief (self.frame.size.height + self.frame.origin.y)の値へのアクセスを容易にするためのプロパティ。
* @return (self.frame.size.height + self.frame.origin.y)と同じ値
*/
@property (nonatomic) CGFloat bottom;
/** @brief self.frame.origin.xへのアクセスを容易にするためのプロパティ。
* @return self.frame.origin.xと同じ値
*/
@property (nonatomic) CGFloat left;
/** @brief self.frame.origin.xへのショートカット */
@property (nonatomic) CGFloat x;
/** @brief self.frame.origin.yへのショートカット */
@property (nonatomic) CGFloat y;
/** @brief self.frame.size.widthへのショートカット */
@property (nonatomic) CGFloat width;
/** @brief self.frame.size.heightへのショートカット */
@property (nonatomic) CGFloat height;
/** @brief self.frame.origin.xの設定メソッド。 */
- (void)setX:(CGFloat)x;
/** @brief self.frame.origin.yの設定メソッド。 */
- (void)setY:(CGFloat)y;
/** @brief self.frame.size.widthの設定メソッド。 */
- (void)setWidth:(CGFloat)width;
/** @brief self.frame.size.heightの設定メソッド。 */
- (void)setHeight:(CGFloat)height;
/** @brief self.frame.originの設定メソッド。 */
- (void)setOrigin:(CGPoint)point;
/** @brief self.frame.sizeの設定メソッド。 */
- (void)setSize:(CGSize)size;
/** @brief 自身のcenterをsuperviewの中心になるように自動設定するメソッド。 */
- (void)setCenterOnSuperView;
/** @brief topプロパティのセッターメソッド。 */
- (void)setTop:(CGFloat)y;
/** @brief rightプロパティのセッターメソッド。 */
- (void)setRight:(CGFloat)right;
/** @brief bottomプロパティのセッターメソッド。 */
- (void)setBottom:(CGFloat)bottom;
/** @brief leftプロパティのセッターメソッド。 */
- (void)setLeft:(CGFloat)x;
/** @brief self.center.xのセッターメソッド。 */
- (void)setCenterX:(CGFloat)centerX;
/** @brief self.center.yのセッターメソッド。 */
- (void)setCenterY:(CGFloat)centerY;
/** @brief 自身が保持するself.center.xの値に引数xを足した結果を新たに保持するメソッド。 */
- (void)addCenterX:(CGFloat)x;
/** @brief 自身が保持するself.center.yの値に引数yを足した結果を新たに保持するメソッド。 */
- (void)addCenterY:(CGFloat)y;
/** @brief 自身が保持するself.frame.originの値に引数offsetの値を足した結果を新たに保持するメソッド。 */
- (void)addOffset:(CGPoint)offset;
/** @brief 自身が保持するself.frame.sizeの値に引数sizeの値を足した結果を新たに保持するメソッド。 */
- (void)addSize:(CGSize)size;
/** @brief このメソッドはxibファイルからUIViewを継承するクラスオブジェクトのインスタンス生成を可能にするためのメソッド。
* @param[in] xib この引数にはxibファイルのファイル名を指定します。
* @param[in] owner このインスタンスを生成する際にViewのオーナーを指定する必要があります。
* @return xibから生成したUIViewのインスタンス
*/
+ (id)viewFromXib:(NSString*)xib owner:(id)owner;
/** @brief このメソッドはxibの名前すら書くのが面倒という方向けのメソッド。
* このメソッドはxibファイル名とクラス名が同一である場合にのみ使用出来ます。
* @param[in] owner オーナーを指定します。
* @return xibから生成したUIViewのインスタンス
*/
+ (id)viewFromClassNameXibWithOwner:(id)owner;
/** @brief Viewサイズは設定せず、座標のみで初期化したい場合に使用するメソッド。
* このメソッドで初期化されたUIViewインスタンスのframeはCGRectMake(point.x, point.y, 0, 0)で初期化されます。
*/
- (id)initWithPoint:(CGPoint)point;
/** @brief View座標は設定せず、サイズのみで初期化したい場合に使用するメソッド。
* このメソッドで初期化されたUIViewインスタンスのframeはCGRectMake(0, 0, size.width, size.height)で初期化されます。
*/
- (id)initWithSize:(CGSize)size;
/**
* @brief Viewを生成する過程でUIScrollViewに内包したい・UIViewに内包したいといった場面が出てきます。
* そういった時にこのクラスメソッドを実行すると、呼び出し元のインスタンスタイプのViewでsubviewをパッケージングできます。
* @code
* UIScrollView *backScroll = [UIScrollView packagingSubView:subView];
* //UIScrollViewのインスタンスを生成し、subViewをaddSubViewしたインスタンスを返却します。
* @endcode
*/
+ (instancetype)packagingSubView:(UIView*)subview;
/** @brief packagingSubViewと同じ機能を有しています。
* 異なる点は引数:subviewのサイズに関わらず、引数:sizeで指定したサイズのViewで初期化される点です。
*/
+ (instancetype)packagingSubView:(UIView*)subview size:(CGSize)size;
/** @brief self.subviewsのうち、firstResponderのUIViewインスタンスを返却します。 */
- (UIView *)findFirstResponder;
/** @brief self.subviewsのうち、最初に見つかった引数:classに該当するクラスのオブジェクトを返却します。 */
- (id)findFirstSubviewWithClass:(Class)class;
/** @brief 自身の親ViewControllerへのポインタを取得します。 */
- (UIViewController*)viewController;
/** @brief 指定したUIViewのサブクラスをまとめてaddSubviewします。 */
- (void)addSubviews:(UIView *)subview, ... NS_REQUIRES_NIL_TERMINATION;
/** @brief Viewの絶対座標を求めます。 */
- (CGPoint)absolutePoint;
/** @brief 自身のスクリーンショットを作成し、UIImageとして返却するメソッド。 */
- (UIImage*)getImage;
/** @brief 自身にaddSubviewされたviewを全てremoveFromSuperViewするメソッド。 */
- (void)removeAffixedAllSubViews;
/** @brief 自身にaddSubviewされたviewを階層的にログ出力するメソッド。 */
- (void)dumpAllSubViews;
/** @brief アニメーションを実行しながらhiddenプロパティを設定するメソッド。 */
- (void)setHidden:(BOOL)hidden animated:(BOOL)animated duration:(float)duration;
/** @brief UIViewのタップ時にイベントを実行するためのメソッド。
* @param[in] numberOfTouches タッチする指の本数を指定します。
* @param[in] numberOfTaps タップする回数を指定します。
* @param[in] block タップ時に実行するイベントハンドラーを設定します。
* @warning 一つのViewに複数のタップイベントを設定することはできません。
*/
- (void)whenTouches:(NSUInteger)numberOfTouches tapped:(NSUInteger)numberOfTaps handler:(Block)block;
/** @brief UIViewのシングルタップ時に引数:blockのイベントハンドラーを実行するよう設定するメソッド。
*/
- (void)whenTapped:(Block)block;
/** @brief 自身にaddSubviewされたサブビューを列挙し、ブロックに引数として渡すメソッド。
*/
- (void)eachSubview:(void(^)(UIView *view))block;
/**
* @brief アニメーションを実行しながら、UIViewを指定したポイントへ位置を移動するメソッド。
*/
- (void)moveTo:(CGPoint)destination duration:(float)secs option:(UIViewAnimationOptions)option;
/**
* @brief ズームインアニメーションを実行しながら、addSubviewするメソッド。
*/
- (void)addSubviewWithZoomInAnimation:(UIView*)view duration:(float)secs option:(UIViewAnimationOptions)option;
/**
* @brief ズームアウトアニメーションを実行しながら、removeFromSuperViewするメソッド。
*/
- (void)removeWithZoomOutAnimation:(float)secs option:(UIViewAnimationOptions)option;
/**
* @brief フェードアニメーションを実行しながら、addSubviewするメソッド。
*/
- (void)addSubviewWithFadeAnimation:(UIView*)view duration:(float)secs option:(UIViewAnimationOptions)option;
/**
* @brief フェードアニメーションを実行しながら、removeFromSuperViewするメソッド。
*/
- (void)removeWithFadeOutAnimationWithDuration:(float)secs option:(UIViewAnimationOptions)option;
/**
* @brief アニメーションを実行する直前にsetupブロックを実行し、その後animationsブロックを実行、最後にcompletionブロックを実行します
*/
- (void)animateWithDuration:(float)secs setup:(void (^)(void))setup animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
/** @brief 指定したUIViewのtag番号を持つ要素をsubviewsの中からすべて列挙し、削除するメソッド。 */
- (void)removeSubviewsWithTagNumber:(NSInteger)tag;
#pragma mark - Draggable
/** @brief UIViewのドラッグイベントハンドルを有効にします。 */
- (void)enableDragging;
/** @brief UIViewのドラッグイベントハンドルを無効にします。 */
- (void)disableDragging;
@end
UIView+Additions.m
//
// UIView+Additions.m
// PowerKit
//
#import "UIView+Additions.h"
#import <objc/runtime.h>
@implementation UIView (Additions)
- (CGFloat)x
{
return self.frame.origin.x;
}
- (CGFloat)y
{
return self.frame.origin.y;
}
- (CGFloat)width
{
return self.frame.size.width;
}
- (CGFloat)height
{
return self.frame.size.height;
}
- (void)setX:(CGFloat)x
{
CGRect rect = self.frame;
rect.origin.x = x;
[self setFrame:rect];
}
- (void)setY:(CGFloat)y
{
CGRect rect = self.frame;
rect.origin.y = y;
[self setFrame:rect];
}
- (void)setWidth:(CGFloat)width
{
CGRect rect = self.frame;
rect.size.width = width;
[self setFrame:rect];
}
- (void)setHeight:(CGFloat)height
{
CGRect rect = self.frame;
rect.size.height = height;
[self setFrame:rect];
}
- (void)setOrigin:(CGPoint)point
{
CGRect rect = self.frame;
rect.origin = point;
[self setFrame:rect];
}
- (void)setSize:(CGSize)size
{
CGRect rect = self.frame;
rect.size = size;
[self setFrame:rect];
}
- (void)setCenterX:(CGFloat)centerX
{
[self setCenter:CGPointMake(centerX, self.center.y)];
}
- (void)setCenterY:(CGFloat)centerY
{
[self setCenter:CGPointMake(self.center.x, centerY)];
}
- (void)setCenterOnSuperView
{
[self setCenter:CGPointMake(self.superview.frame.size.width/2,self.superview.frame.size.height/2)];
}
- (CGFloat)top
{
return self.frame.origin.y;
}
- (void)setTop:(CGFloat)y
{
CGRect rect = self.frame;
rect.origin.y = y;
self.frame = rect;
}
- (CGFloat)right
{
return self.frame.origin.x + self.frame.size.width;
}
- (void)setRight:(CGFloat)right
{
CGRect rect = self.frame;
rect.origin.x = right - self.frame.size.width;
self.frame = rect;
}
- (CGFloat)bottom
{
return self.frame.origin.y + self.frame.size.height;
}
- (void)setBottom:(CGFloat)bottom
{
CGRect rect = self.frame;
rect.origin.y = bottom - self.frame.size.height;
self.frame = rect;
}
- (CGFloat)left
{
return self.frame.origin.x;
}
- (void)setLeft:(CGFloat)x
{
CGRect rect = self.frame;
rect.origin.x = x;
self.frame = rect;
}
- (void)addCenterX:(CGFloat)x
{
CGPoint point = self.center;
point.x += x;
self.center = point;
}
- (void)addCenterY:(CGFloat)y
{
CGPoint point = self.center;
point.y += y;
self.center = point;
}
- (void)addOffset:(CGPoint)offset
{
CGRect rect = self.frame;
rect.origin.x += offset.x;
rect.origin.y += offset.y;
self.frame = rect;
}
- (void)addSize:(CGSize)size
{
CGRect rect = self.frame;
rect.size.width += size.width;
rect.size.height += size.height;
self.frame = rect;
}
- (void)addCenter:(CGPoint)center
{
CGPoint point = self.center;
point.x += center.x;
point.y += center.y;
self.center = point;
}
+ (id)viewFromXib:(NSString *)xib owner:(id)owner
{
UINib *nib = [UINib nibWithNibName:xib bundle:nil];
if (nib == nil) return nil;
return [[nib instantiateWithOwner:owner options:nil] objectAtIndex:0];
}
+ (id)viewFromClassNameXibWithOwner:(id)owner
{
UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil];
if (nib == nil) return nil;
return [[nib instantiateWithOwner:owner options:nil] objectAtIndex:0];
}
- (id)initWithPoint:(CGPoint)point
{
self = [self initWithFrame:CGRectMake(point.x, point.y, 0, 0)];
return self;
}
- (id)initWithSize:(CGSize)size
{
self = [self initWithFrame:CGRectMake(0, 0, size.width, size.height)];
return self;
}
+ (instancetype)packagingSubView:(UIView *)subview
{
Class aClass = [self class];
id parent = [[aClass alloc] init];
[parent setFrame:subview.frame];
[subview setOrigin:CGPointMake(0,0)];
[parent addSubview:subview];
return parent;
}
+ (instancetype)packagingSubView:(UIView *)subview size:(CGSize)size
{
Class aClass = [self class];
id parent = [[aClass alloc] init];
[parent setFrame:CGRectMake(0, 0, size.width, size.height)];
[subview setOrigin:CGPointMake(0, 0)];
[parent addSubview:subview];
return parent;
}
- (UIView *)findFirstResponder
{
if ([self isFirstResponder]) {
return self;
}
for (UIView *subview in self.subviews) {
UIView *firstResponder = [subview findFirstResponder];
if (firstResponder) {
return firstResponder;
}
}
return nil;
}
- (id)findFirstSubviewWithClass:(Class)class
{
NSParameterAssert([class isSubclassOfClass:[UIView class]]);
if ([self isKindOfClass:class]) {
return self;
}
for (UIView *subview in self.subviews) {
UIView *subviewWithClass = [subview findFirstSubviewWithClass:class];
if (subviewWithClass) {
return subviewWithClass;
}
}
return nil;
}
- (UIViewController*)viewController
{
for (UIView *next = [self superview]; next; next = next.superview) {
UIResponder *nextResponder = [next nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return (UIViewController*)nextResponder;
}
}
return nil;
}
- (void)addSubviews:(UIView *)subview, ...
{
va_list argp;
va_start(argp, subview);
id obj = subview;
while (obj) {
[self addSubview:obj];
obj = va_arg(argp, typeof(id));
}
va_end(argp);
}
- (CGPoint)absolutePoint
{
CGPoint ret = self.frame.origin;
UIView *superView = [self superview];
while (superView) {
ret.x += superView.frame.origin.x;
ret.y += superView.frame.origin.y;
superView = [superView superview];
}
return ret;
}
- (UIImage*)getImage
{
UIGraphicsBeginImageContext(self.frame.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (void)removeAffixedAllSubViews
{
while (self.subviews.count) {
UIView *child = self.subviews.lastObject;
[child removeFromSuperview];
}
}
- (void)dumpAllSubViews
{
[self showSubViews:self depth:0];
}
- (void)setHidden:(BOOL)hidden animated:(BOOL)animated duration:(float)duration
{
if (!animated || self.hidden == hidden) {
self.hidden = hidden;
return;
}
CGFloat backupAlpha = self.alpha;
CGFloat endAlpha;
if (hidden) {
endAlpha = 0.0f;
} else {
self.alpha = 0.0f;
endAlpha = backupAlpha;
self.hidden = NO;
}
[[self class] animateWithDuration:duration
animations:^{
self.alpha = endAlpha;
} completion:^(BOOL finished) {
if (hidden) {
self.alpha = backupAlpha;
self.hidden = YES;
}
}];
}
-(UIView *)getRootView:(UIView *)view
{
while([view superview]){
view = [view superview];
}
return view;
}
-(void)showSubViews:(UIView *)view depth:(int)depth
{
NSLog(@"%@%@ (%lf,%lf) (%lf,%lf)",
[@"----------" substringToIndex:depth],
[view class],
view.frame.origin.x,
view.frame.origin.y,
view.frame.size.width,
view.frame.size.height);
for(UIView * subview in [view subviews]){
[self showSubViews:subview depth:depth + 1];
}
}
#pragma mark - Blocks Methods
- (void)setTouchBlock:(Block)touchBlock
{
objc_setAssociatedObject(self, @selector(touchBlock), touchBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (Block)touchBlock
{
return objc_getAssociatedObject(self, @selector(touchBlock));
}
- (void)handleTouch:(UITapGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateRecognized) {
Block touchBlock = [self touchBlock];
if (touchBlock) {
touchBlock();
}
}
}
- (void)whenTouches:(NSUInteger)numberOfTouches tapped:(NSUInteger)numberOfTaps handler:(Block)block
{
if (!block) return;
[self setTouchBlock:block];
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleTouch:)];
[gesture setNumberOfTouchesRequired:numberOfTouches];
[gesture setNumberOfTapsRequired:numberOfTaps];
[self.gestureRecognizers enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (![obj isKindOfClass:[UITapGestureRecognizer class]]) return;
UITapGestureRecognizer *tap = obj;
BOOL rightTouches = (tap.numberOfTouchesRequired == numberOfTouches);
BOOL rightTaps = (tap.numberOfTapsRequired == numberOfTaps);
if (rightTouches && rightTaps) {
[gesture requireGestureRecognizerToFail:tap];
}
}];
[self addGestureRecognizer:gesture];
[self setUserInteractionEnabled:YES];
}
- (void)whenTapped:(Block)block
{
[self whenTouches:1 tapped:1 handler:block];
}
- (void)eachSubview:(void (^)(UIView *))block
{
for (UIView *view in self.subviews) {
block(view);
}
}
#pragma mark - Animation Categories
- (void)moveTo:(CGPoint)destination duration:(float)secs option:(UIViewAnimationOptions)option
{
[UIView animateWithDuration:secs delay:0.0 options:option
animations:^{
self.frame = CGRectMake(destination.x,destination.y, self.frame.size.width, self.frame.size.height);
}
completion:nil];
}
- (void)addSubviewWithZoomInAnimation:(UIView*)view duration:(float)secs option:(UIViewAnimationOptions)option
{
CGAffineTransform trans = CGAffineTransformScale(view.transform, 0.01, 0.01);
view.transform = trans;
[self addSubview:view];
[UIView animateWithDuration:secs delay:0.0 options:option
animations:^{
view.transform = CGAffineTransformScale(view.transform, 100.0, 100.0);
}
completion:nil];
}
- (void)removeWithZoomOutAnimation:(float)secs option:(UIViewAnimationOptions)option
{
[UIView animateWithDuration:secs delay:0.0 options:option
animations:^{
self.transform = CGAffineTransformScale(self.transform, 0.01, 0.01);
}
completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
- (void)addSubviewWithFadeAnimation:(UIView*)view duration:(float)secs option:(UIViewAnimationOptions)option
{
view.alpha = 0.0;
[self addSubview:view];
[UIView animateWithDuration:secs delay:0.0 options:option
animations:^{view.alpha = 1.0;}
completion:nil];
}
- (void)removeWithFadeOutAnimationWithDuration:(float)secs option:(UIViewAnimationOptions)option
{
[UIView animateWithDuration:secs
delay:0.0
options:option
animations:^{
[self setAlpha:0];
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
- (void)animateWithDuration:(float)secs setup:(void (^)(void))setup animations:(void (^)(void))animations completion:(void (^)(BOOL))completion
{
if (setup) {
setup();
}
[UIView animateWithDuration:secs
animations:animations
completion:completion];
}
- (void)removeSubviewsWithTagNumber:(NSInteger)tag
{
for (UIView *v in self.subviews) {
if ([v tag] == tag) {
[v removeFromSuperview];
}
}
}
#pragma mark - draggable
- (void)setPanGesture:(UIPanGestureRecognizer *)panGesture
{
objc_setAssociatedObject(self, @selector(panGesture), panGesture, OBJC_ASSOCIATION_RETAIN);
}
- (UIPanGestureRecognizer *)panGesture
{
return objc_getAssociatedObject(self, @selector(panGesture));
}
- (void)handlePan:(UIPanGestureRecognizer *)sender
{
[self adjustAnchorPointForGestureRecognizer:sender];
CGPoint translation = [sender translationInView:[self superview]];
[self setCenter:CGPointMake([self center].x + translation.x, [self center].y + translation.y)];
[sender setTranslation:(CGPoint){0,0} inView:[self superview]];
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
UIView *piece = self;
CGPoint locationInView = [gestureRecognizer locationInView:piece];
CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];
piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
}
}
- (void)enableDragging
{
self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
[self.panGesture setMaximumNumberOfTouches:1];
[self.panGesture setMinimumNumberOfTouches:1];
[self.panGesture setCancelsTouchesInView:NO];
[self addGestureRecognizer:self.panGesture];
}
- (void)disableDragging
{
[self.panGesture setEnabled:NO];
}
@end