iOS 控制元件檢視基類之 UIView

征途LN發表於2014-02-20

引言

UIView表示螢幕上德一塊矩形區域,在APP中佔有絕對重要的地位,因為iOS中所有的視覺化控制元件都是UIView的子類。負責渲染區域的內容,並且響應該區域內發生的觸控事件


目錄

 1、UIView家族

 2、UIView基本屬性

 3、幾何方法類

 4、控制級別類

 5、渲染類

 6、動畫類

 7、手勢類

 8、移動效果類

 9、基於佈局約束類(iOS 6.0)

10、基於佈局的約束core方法(iOS 6.0)

11、基於約束的相容性(iOS 6.0)

12、層次佈局的約束(iOS 6.0)

13、裝置大小布局約束(iOS 6.0)

14、佈局排程(iOS 6.0)

15、狀態恢復(iOS 6.0)

16、快照 (iOS 7.0)


 1、UIView家族

下圖是檢視的內層土

image

 2、UIView基本屬性

BOOL userInteractionEnabled; //是否接收events事件,預設YES

NSInteger tag; //標記

CALayer * layer; //和塗層有關

CGRect frame ;

CGRect bounds;

CGPoint center;

CGAffineTransform transform; //矩陣變換,這個是恆等變換,一般用來旋轉、縮放

CGFloat contentScaleFactor; //修改這個屬性可以讓UIView的渲染精度底稿,這樣即使在CGAffineTransform方法之後仍然能保持銳利


// 是否允許多點觸控
BOOL multipleTouchEnabled;   

// default is NO

BOOL       exclusiveTouch;         

// UIView的背影顏色

UIColor   backgroundColor;  

// UIView是否隱藏

BOOL hidden;  

// 為0時完全透明,為1時完全不透明。

CGFloat  alpha; 

//在類的層次結構中,如果clipsTobounds設為YES,超出superView的部分subview就不會顯示,否則會做顯示, 預設情況下是NO。

BOOL clipsToBounds;

//這是一個優化屬性,如果該值為YES, 那麼繪圖在繪製該檢視的時候把整個檢視當作不透明對待。這樣,繪圖系統在執行繪圖過程中會優化一些操作並提升系統效能;如果是設定為NO, 繪圖系統將其和其他內容平等對待,不去做優化操作。為了效能方面的考量,預設被置為YES(意味著‘優化’)。

BOOL opaque;   

UIView  *superview;// 父檢視
NSArray *subviews;// 子檢視

注:frame和center都是相對於父檢視的,bounds是相對於自身的。當view完全透明和隱藏時不能響應事件。

opaque和alpha 是有關係的。 一個不透明檢視需要整個邊界裡面的內容都是不透明的。基於這個原因,opaque設定為YES,要求對應的alpha必須為1.0。如果一個UIView例項opaque被設定為YES, 而同時它又沒有完全填充它的邊界(bounds),或者它包含了整個或部分的透明的內容檢視,那麼將會導致未知的結果。 因此,如果檢視部分或全部支援透明,那麼你必須把opaque這個值設定為NO.

3、幾何方法類

// hitTest主要用途是用來尋找那個檢視是被觸控了

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;   

// 如果我們不想讓某個檢視響應事件,讓此方法返回NO就行了。預設為YES

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

舉個例子,建立一個TestView裡面過載hitTest和pointInside方法:

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    [super hitTest:point withEvent:event];
    return self;
}  

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    NSLog(@"view pointInside");
    return YES;
}  

然後在TestView中增加一個子檢視TestSecondView也過載這兩個方法

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    [super hitTest:point withEvent:event]; 
    return self;  
}  

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    NSLog(@"second view pointInside");
    return YES; 
}  

這裡注意[super hitTest:point withEvent:event];必須要包括,否則hitTest無法呼叫父類的方法,這樣就沒法 使用PointInside:withEvent:進行判斷,那麼就沒法進行子檢視的遍歷。當去掉這個語句的時候,觸控事件就不可能進到子檢視中了,除非你在方法中直接返回子檢視的物件。這樣你在除錯的過程中就會發現,每次你點選一個view都會先進入到這個view的父檢視中的hitTest方法,然後 呼叫super的hitTest方法之後就會查詢pointInside是否返回YES如果是,則就把訊息傳遞個子檢視處理,子檢視用同樣的方法遞迴查詢 自己的子檢視。所以從這裡除錯分析看,hitTest方法這種遞迴呼叫的方式就一目瞭然了。

// 將畫素point由point所在檢視轉換到目標檢視view中,返回在目標檢視view中的畫素值

- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view;

// 將畫素point從view中轉換到當前檢視中,返回在當前檢視中的畫素值

- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view;

// 將rect由rect所在檢視轉換到目標檢視view中,返回在目標檢視view中的rect

- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view;

// 將rect從view中轉換到當前檢視中,返回在當前檢視中的rect

- (CGRect)convertRect:(CGRect)rect fromView:(UIView *)view;

4、控制級別類

// 把檢視從父類移除
- (void)removeFromSuperview;
// 新增子檢視到某個位置
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;
// 更改子檢視的位置
- (void)exchangeSubviewAtIndex:(NSInteger)index1 withSubviewAtIndex:(NSInteger)index2;

// 新增子檢視
- (void)addSubview:(UIView *)view;
// 把view放在siblingSubview下面
- (void)insertSubview:(UIView *)view belowSubview:(UIView *)siblingSubview;
// 把view放在siblingSubview上面
- (void)insertSubview:(UIView *)view aboveSubview:(UIView *)siblingSubview;

注:1、addSubview:和insertSubview: atIndex:的區別:

  [view addSubView:oneView] == [view insertSubview:oneView atIndex:view.subviews.cout];

// 將view顯示在最前面(要將一個view顯示在最前面,只要呼叫其父檢視的bringSubviewToFront:方法)

- (void)bringSubviewToFront:(UIView *)view;

// 將view推送到背後

- (void)sendSubviewToBack:(UIView *)view;

// 當加入檢視完成後呼叫

- (void)didAddSubview:(UIView *)subview;

// 將移除子檢視會呼叫

- (void)willRemoveSubview:(UIView *)subview;

// 通知即將移動到新的父檢視中

- (void)willMoveToSuperview:(UIView *)newSuperview;

// 通知已經到新父檢視

- (void)didMoveToSuperview;

// 通知即將已移動到新的視窗

- (void)willMoveToWindow:(UIWindow *)newWindow;

// 通知已經移動新的視窗

- (void)didMoveToWindow;

// 判斷一個檢視是否在父檢視層中

- (BOOL)isDescendantOfView:(UIView *)view;

// 獲取標記的子檢視

- (UIView *)viewWithTag:(NSInteger)tag;     

// 標記為需要重新佈局,非同步呼叫layoutIfNeeded重新整理佈局,不立即重新整理,但layoutSubviews一定會被呼叫

- (void)setNeedsLayout;

// 如果有需要重新整理的標記,立即呼叫layoutSubviews進行佈局(如果沒有標記,不會呼叫layoutSubviews)

- (void)layoutIfNeeded;

// 重新佈局

- (void)layoutSubviews; 

5、渲染類

// 重寫此方法,執行重繪

- (void)drawRect:(CGRect)rect;

// 標記為需要重繪,非同步呼叫drawRect

- (void)setNeedsDisplay;

// 標記為需要區域性重繪

- (void)setNeedsDisplayInRect:(CGRect)rect;

注:drawRect是對receiver的重繪。setNeedDisplay在receiver標上一個需要被重新繪圖的標記,在下一個draw週期自動重繪,iphone device的重新整理頻率是60hz,也就是1/60秒後重繪

6、動畫類

// 開始一個動畫

+ (void)beginAnimations:(NSString *)animationID context:(void *)context; 

// 執行動畫,類似資料庫的事務處理

+ (void)commitAnimations; 

// 設定動畫代理,預設為nil

+ (void)setAnimationDelegate:(id)delegate; 

// 當動畫執行開始時,執行selector方法,預設為NULL

+ (void)setAnimationWillStartSelector:(SEL)selector;

// 當動畫執行結束時,執行selector方法

+ (void)setAnimationDidStopSelector:(SEL)selector; 

// 設定動畫時間,時間引數為double型別,預設0.2

+ (void)setAnimationDuration:(NSTimeInterval)duration;

// 設定動畫延遲時間

+ (void)setAnimationDelay:(NSTimeInterval)delay;  

// 設定在動畫塊內部動畫內部動畫屬性改變的開始時間

+ (void)setAnimationStartDate:(NSDate *)startDate;

// 設定動畫的旋轉曲度變化,預設為UIViewAnimationCurveEaseInOut.值定義在UIViewAnimationCurve結構體中。

typedef NS_ENUM(NSInteger, UIViewAnimationCurve) { //動畫曲線
    // 緩慢開始,中間加速,然後減速到結束
    UIViewAnimationCurveEaseInOut,   
    // 緩慢開始,加速到結束     
    UIViewAnimationCurveEaseIn,    
    // 加速開始,加速到結束        
    UIViewAnimationCurveEaseOut, 
    //正常速度          
    UIViewAnimationCurveLinear  
};
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve; 

// 設定動畫在動畫模組中的重複次數

+ (void)setAnimationRepeatCount:(float)repeatCount;                 

// 設定動畫塊中的動畫效果是否自動重複播放

+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses;    

// 設定動畫是否從當前狀態開始播放,預設為NO.

+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;  

// 在動畫設定過渡效果

+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;

transition把一個過渡效果應用到檢視中。值定義在UIViewAnimationTransition結構體中。

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
    UIViewAnimationTransitionNone, 沒有過渡
    UIViewAnimationTransitionFlipFromLeft,  翻轉檢視從左到右
    UIViewAnimationTransitionFlipFromRight, 翻轉檢視從右到左
    UIViewAnimationTransitionCurlUp, 從上捲動
    UIViewAnimationTransitionCurlDown, 從下捲動
};

view:需要過渡的檢視物件。

cache:如果是YES,那麼在開始和結束圖片檢視渲染一次並在動畫中建立幀;否則,檢視將會在每一幀都渲染。例如快取,你不需要在檢視轉變中不停的更新,你只需要等到轉換完成再去更新檢視。

1、開始一個動畫塊。
2、在容器檢視中設定轉換。
3、在容器檢視中移除子檢視。
4、在容器檢視中新增子檢視。
5、結束動畫塊。

// 設定是否開啟動畫,預設YES,開啟

+ (void)setAnimationsEnabled:(BOOL)enabled;

// 驗證動畫是否開啟,YES開啟,NO關閉

+ (BOOL)areAnimationsEnabled;               

// iOS7之後替代 setAnimationsEnabled

+ (void)performWithoutAnimation:(void (^)(void))actionsWithoutAnimation;

// iOS4.0之後,動畫效果處理塊

+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;

(NSTimeInterval)duration :動畫時間

(NSTimeInterval)delay:延遲時間

(UIViewAnimationOptions)options 動畫引數

typedef NS_OPTIONS(NSUInteger, UIViewAnimationOptions) {
    UIViewAnimationOptionLayoutSubviews            = 1 <<  0,
    UIViewAnimationOptionAllowUserInteraction      = 1 <<  1, // turn on user interaction while animating
    UIViewAnimationOptionBeginFromCurrentState     = 1 <<  2, // start all views from current value, not initial value
    UIViewAnimationOptionRepeat                    = 1 <<  3, // repeat animation indefinitely
    UIViewAnimationOptionAutoreverse               = 1 <<  4, // if repeat, run animation back and forth
    UIViewAnimationOptionOverrideInheritedDuration = 1 <<  5, // ignore nested duration
    UIViewAnimationOptionOverrideInheritedCurve    = 1 <<  6, // ignore nested curve
    UIViewAnimationOptionAllowAnimatedContent      = 1 <<  7, // animate contents (applies to transitions only)
    UIViewAnimationOptionShowHideTransitionViews   = 1 <<  8, // flip to/from hidden state instead of adding/removing

    UIViewAnimationOptionCurveEaseInOut            = 0 << 16, // default
    UIViewAnimationOptionCurveEaseIn               = 1 << 16,
    UIViewAnimationOptionCurveEaseOut              = 2 << 16,
    UIViewAnimationOptionCurveLinear               = 3 << 16,

    UIViewAnimationOptionTransitionNone            = 0 << 20, // default
    UIViewAnimationOptionTransitionFlipFromLeft    = 1 << 20,
    UIViewAnimationOptionTransitionFlipFromRight   = 2 << 20,
    UIViewAnimationOptionTransitionCurlUp          = 3 << 20,
    UIViewAnimationOptionTransitionCurlDown        = 4 << 20,
    UIViewAnimationOptionTransitionCrossDissolve   = 5 << 20,
    UIViewAnimationOptionTransitionFlipFromTop     = 6 << 20,
    UIViewAnimationOptionTransitionFlipFromBottom  = 7 << 20,
} NS_ENUM_AVAILABLE_IOS(4_0);

(void))animations :動畫效果塊

可以設定屬性如下:frame\bounds\center\transform\alpha\backgroundColor\contentStretch

completion:(void (^)(BOOL finished))completion 動畫結束塊

例:

[UIView animateWithDuration: 2.00 delay:3.00 options:UIViewAnimationOptionAllowAnimatedContent
                 animations:^{
                         insertDemoTwo.alpha = 0.1;
                         insertDemoOne.alpha = 1.0;
                                        }
                 completion:^(BOOL finished) {
                     [UIView animateWithDuration:3.00
                                      animations:^{
                                          insertDemoTwo.center = CGPointMake(500.0, 470.0);
                                          insertDemoOne.center = CGPointMake(140.0, 100.0);
                                      }
                      ];


                 }

 ];


// 動畫效果處理(無延遲,無引數)iOS4.0之後,預設delay = 0.0,options = 0;
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;

例:

[UIView animateWithDuration:3.00
                 animations:^{
                     insertDemoOne.alpha = 1.0;
                     insertDemoTwo.alpha = 0.1;
                 }
                 completion:^(BOOL finished) {
                     insertDemoTwo.center = CGPointMake(500.0, 470.0);
                     insertDemoOne.center = CGPointMake(140.0, 100.0);                    
                     }
 ];

//動畫效果處理簡單版 iOS4.0之後(delay = 0.0,options = 0,completion = NULL)
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations;

+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);

// 過渡動畫效果塊,iOS4.0 UIViewAnimationOption本身就是一個動畫,animation是你自己定義的動畫,兩個同時執行
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;

例:

[UIView transitionWithView:insertDemoOne duration: 1.0 options:UIViewAnimationOptionTransitionFlipFromLeft
                                     animations:^{
                                         [insertDemoTwo removeFromSuperview];
                                         [insertDemoOne addSubview:insertDemoTwo];
                                     }
                                     completion:^(BOOL finished) {
                                         insertDemoOne.backgroundColor = [UIColor brownColor];
                                     }];

// 檢視之間切換的過渡動畫效果塊,iOS4,執行動畫之後,介面保留的時toView這個檢視,fromView就從介面上消失了
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion;


+ (void)performSystemAnimation:(UISystemAnimation)animation onViews:(NSArray *)views options:(UIViewAnimationOptions)options animations:(void (^)(void))parallelAnimations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);

7、手勢類

// 手勢識別器(iOS3.2)

NSArray *gestureRecognizers;

UIKit 中UIGestureRecognizer類的子類系列如下:
UITapGestureRecognizer – “輕擊”手勢。可以配置為“單擊”和“連擊”的識別。
UIPinchGestureRecognizer –“捏合”手勢。該手勢通常用於縮放檢視或改變可視元件的大小。
UIPanGestureRecognizer – “平移”手勢。識別拖拽或移動動作。
UISwipeGestureRecognizer – “輕掃”手勢。當使用者從螢幕上劃過時識別為該手勢。可以指定該動作的方向(上、下、左、右)。
UIRotationGestureRecognizer – “轉動”手勢。使用者兩指在螢幕上做相對環形運動。
UILongPressGestureRecognizer – “長按”手勢。使用1指或多指觸控螢幕並保持一定時間。

// 繫結手勢到檢視(iOS3.2)

- (void)addGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer; 

//從檢視中移除手勢 (iOS3.2)

- (void)removeGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer;

//手勢識別處理方式在gesture recognizer檢視轉出《UIGestureRecognizerStatePossible》狀態時呼叫,如果返回NO,則轉換到《UIGestureRecognizerStateFailed》;如果返回YES,則繼續識別觸控序列.(預設情況下為YES)。(iOS6.0)

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;

8、移動效果類(iOS7.0)

- (void)addMotionEffect:(UIMotionEffect *)effect;
- (void)removeMotionEffect:(UIMotionEffect *)effect;

9、基於佈局約束類(iOS6.0)

// 檢視佈局約束

- (NSArray *)constraints;

// 檢視佈局上新增一個約束

- (void)addConstraint:(NSLayoutConstraint *)constraint;

// 檢視佈局上新增多個約束

- (void)addConstraints:(NSArray *)constraints;

// 移除視力佈局上的一個約束

- (void)removeConstraint:(NSLayoutConstraint *)constraint;

// 移除檢視佈局上的多個約束

- (void)removeConstraints:(NSArray *)constraints;

10、基於佈局的約束core方法(iOS 6.0)

// 呼叫新的檢視佈局自動觸發,更新檢視佈局上的約束

- (void)updateConstraintsIfNeeded;

// 更新自定義檢視佈局約束

- (void)updateConstraints; 

// 判斷檢視佈局是否需要更新約束

- (BOOL)needsUpdateConstraints;

// 設定檢視佈局是否需要更新約束

- (void)setNeedsUpdateConstraints;

11、基於約束的相容性(iOS 6.0)

// 標示是否自動遵循檢視佈局約束,預設為YES

- (BOOL)translatesAutoresizingMaskIntoConstraints; 

// 設定是否自動遵循檢視佈局約束

- (void)setTranslatesAutoresizingMaskIntoConstraints:(BOOL)flag;

// 返回是遵循自定義檢視佈局約束

+ (BOOL)requiresConstraintBasedLayout NS_AVAILABLE_IOS(6_0);

12、層次佈局的約束(iOS 6.0)

// 返回矩形對於指定檢視框架

- (CGRect)alignmentRectForFrame:(CGRect)frame;

// 返回框架對於指定檢視矩形

- (CGRect)frameForAlignmentRect:(CGRect)alignmentRect;

// 返回自定義檢視大小

- (UIEdgeInsets)alignmentRectInsets;

// 如果超出約束範圍,自動生成基線限制,以滿足檢視需求

- (UIView *)viewForBaselineLayout;

// 返回放大的檢視佈局軸線

- (UILayoutPriority)contentHuggingPriorityForAxis:(UILayoutConstraintAxis)axis;

// 設定放大的檢視佈局的軸線

- (void)setContentHuggingPriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis;

// 返回縮小的檢視佈局的軸線

- (UILayoutPriority)contentCompressionResistancePriorityForAxis:(UILayoutConstraintAxis)axis;

// 設定縮小的檢視佈局軸線

- (void)setContentCompressionResistancePriority:(UILayoutPriority)priority forAxis:(UILayoutConstraintAxis)axis;

13、裝置大小布局約束(iOS 6.0)

// 滿足約束檢視佈局的大小

- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize;

14、佈局排程(iOS 6.0)

// 返回影響檢視佈局限制的軸線

- (NSArray *)constraintsAffectingLayoutForAxis:(UILayoutConstraintAxis)axis;

// 返回檢視佈局約束是否影響指定檢視,主要用於除錯約束佈局,結合exerciseAmbiguityInLayout。

- (BOOL)hasAmbiguousLayout;

// 隨機改變不同效值佈局檢視,主要用於除錯基於約束佈局的檢視

- (void)exerciseAmbiguityInLayout; 

15、狀態恢復(iOS 6.0)

// 標誌是否支援儲存,恢復檢視狀態資訊

@property (nonatomic, copy) NSString *restorationIdentifier;

// 儲存檢視狀態相關資訊

- (void) encodeRestorableStateWithCoder:(NSCoder *)coder;

// 恢復和儲存檢視相關資訊

- (void) decodeRestorableStateWithCoder:(NSCoder *)coder; 

16、快照 (iOS7.0)

我們有時候可能需要取UIView物件的快照,有幾個原因,您可能希望從動畫效能改進上去分享您的應用程式的螢幕截圖。現有的方法面臨幾個問題:

1程式碼不簡單
2複雜的渲染選項,比如層面罩的難再生
3OpenGL層需要特殊的事例程式碼
4快照處理十分慢

事實上,真的沒有任何通用的“快照”程式碼可以應付所有可能的場景。 但是IOS7將會改變,UIView和UIScreen會有一些新的方法,為各種用例提供簡單的快照功能。

動畫快照 我們可能經常想對一個檢視進行動畫處理,但是檢視的動畫太複雜,要麼是動畫太密集,要麼就是需要額外的程式碼來控制正確的行為。 例如附帶的專案,我們建立一個UIView的子類,它只是簡單的新增一些子檢視,並旋轉來生成一個有趣的幾何排列。

// 呼叫snapshotViewAfterScreenUpdates:方法去建立複雜檢視的快照。方法返回一個UIView,來呈現被呼叫的檢視的介面。用這個方法去獲取檢視快照非常有效,比製作點陣圖要快的多。 當獲得檢視快照後,我們把它新增到容器檢視上,並移除實際的複雜檢視。然後可以動畫處理快照檢視了:

- (UIView *)snapshotViewAfterScreenUpdates:(BOOL)afterUpdates;

- (UIView *)resizableSnapshotViewFromRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates withCapInsets:(UIEdgeInsets)capInsets;
- (BOOL)drawViewHierarchyInRect:(CGRect)rect afterScreenUpdates:(BOOL)afterUpdates;

本文連結地址:Abel的部落格

相關文章