UIView(控件)
功能一:界面顯示
1. 屏幕上顯示的所有UI元素都叫做控件,也有人叫做視圖、組件;按鈕(UIButton)、文本(UILabel)都是控件;
2. 所有的控件最終都繼承自UIView
3. UI控件能夠展示的原因是因?yàn)橛幸粋€(gè)layer層功能二:事件響應(yīng)
1. UIView繼承自UIResponder。UIResponder繼承自NSObject
2. 大部分UI開(kāi)頭的類都是繼承自UIResponder,UIResponder具有事件響應(yīng)的能力,這是它們能夠處理事件的原因注意
1. UI控件沒(méi)有顯示原因總結(jié):
1. 沒(méi)有尺寸位置;
2. 尺寸位置超出視圖;
3. 沒(méi)有顏色或圖片等顯示要素;
4. 沒(méi)添加到當(dāng)前窗口或視圖中;
5. 透明(alpha = 0 || opapue = 1);
6. 其他控件或窗口擋住了;
7. 按鈕、segmentedControl等直接設(shè)置屬性,而沒(méi)有分狀態(tài)設(shè)置;
2. 大部分UI控件通過(guò)alloc.init創(chuàng)建都沒(méi)有尺寸,顏色等;開(kāi)關(guān),菊花控件、toolBar控件、導(dǎo)航條控件、標(biāo)簽條控件等除外
UIKit中可能用得上的UI控件:

UIkit坐標(biāo)系(UI控件位置、尺寸的基礎(chǔ))
- UIKit坐標(biāo)系:
- 坐標(biāo)系的原點(diǎn)(0,0)在屏幕的左上角
- x值向右正向延伸
- y值向下正向延伸

UIView位置、尺寸屬性
-
frame:控件矩形框在父控件中的位置和尺寸
1. 以父控件的左上角為坐標(biāo)原點(diǎn)view.frame = CGRectMake(0, 0, 100, 100); -
bonuds:控件矩形框的尺寸及在內(nèi)容層中的位置
1. 修改bounds的尺寸是以中心點(diǎn)為基準(zhǔn)伸縮的
2. 自己左上角'內(nèi)容層原點(diǎn)'為坐標(biāo)原點(diǎn),內(nèi)容層是無(wú)限大的
3. bonuds的x,y修改,實(shí)際上是影響可視化區(qū)域在內(nèi)容層中的位置。
4. 可視化區(qū)域,參照父控件,位置是不變。所以修改x,y,可以視作是內(nèi)容層反向移動(dòng),有如下規(guī)律:
1. bounds x > 0,表示需要看右邊內(nèi)容,可視區(qū)域相對(duì)內(nèi)容層原點(diǎn)往右邊走,我們看到的效果,內(nèi)容層原點(diǎn)往左移動(dòng)
2. bounds y > 0,表示需要看下邊內(nèi)容,可視區(qū)域相對(duì)內(nèi)容層原點(diǎn)往下邊走,我們看到效果,內(nèi)容層原點(diǎn)往上移動(dòng)view.bounds = CGRectMake(0, 0, 100, 100);
- center:控件中心點(diǎn)的位置
1. 以父控件的左上角為坐標(biāo)原點(diǎn)
2. 注意:在設(shè)置center時(shí),先確定尺寸,否則尺寸不確定,center定位不準(zhǔn),布局控件時(shí)應(yīng):
1. 先設(shè)置frame后設(shè)置center.
2. 或通過(guò)設(shè)置bonuds,這樣則不需要區(qū)分先后view.center = CGPointMake(100, 100);
UIView位置、尺寸屬性的修改
以frame的修改為例(位置,尺寸的修改可參照f(shuō)rame的修改)
- 不能直接修改->OC對(duì)象的結(jié)構(gòu)體屬性的成員
// imageView.frame.size.width = 100; // 不允許
-
方案一:通過(guò)CGRectMake函數(shù)(常用)
imageView.frame = CGRectMake(100, 100, 200, 200); -
方案二:利用臨時(shí)結(jié)構(gòu)體變量(常用)
- 使用場(chǎng)景:針對(duì)結(jié)構(gòu)體中某個(gè)變量的修改
- C結(jié)構(gòu)體的修改方式
CGRect tempFrame = imageView.frame; tempFrame.origin.x = 100; imageView.frame = tempFrame;
-
方案三:使用大括號(hào){}形式(不常用)
- C結(jié)構(gòu)體的修改方式
imageView.frame = redView.frame = (CGRect){(100, 100), (100, 100)};;
- C結(jié)構(gòu)體的修改方式
-
小結(jié):開(kāi)發(fā)中經(jīng)常需要設(shè)置或獲取UIView尺寸屬性,建議增加UIView(Rect)的分類,提高效率,步驟如下;
1. 創(chuàng)建Rect分類文件(UIView的分類),在.h文件通過(guò)@property聲明x,y,width,height,centerX,centerY,top,bottom,leading,trailing屬性;
1. 分類不能定義屬性,這里使用@property僅是聲明set、get方法,不會(huì)生成下劃線成員屬性并實(shí)現(xiàn)set、get方法
2. 實(shí)現(xiàn)set方法;
3. 實(shí)現(xiàn)get方法;
4. 建議UIView尺寸屬性名都帶上類前綴,如zq_x、zq_top等,避免以后會(huì)與原生或其他框架沖突.h文件 @interface UIView (Rect) /** 生成set、get方法聲明 */ @property(nonatomic,assign) CGFloat zq_x; @property(nonatomic,assign) CGFloat zq_y; @property(nonatomic,assign) CGFloat zq_width; @property(nonatomic,assign) CGFloat zq_height; @property(nonatomic,assign) CGFloat zq_centerX; @property(nonatomic,assign) CGFloat zq_centerY; @property(nonatomic,assign) CGFloat zq_top; @property(nonatomic,assign) CGFloat zq_leading;//左 @property(nonatomic,assign) CGFloat zq_bottom; @property(nonatomic,assign) CGFloat zq_trailing;//右.m文件(僅以代表舉例) #pragma mark - x set、get方法實(shí)現(xiàn) - (void)setZq_x:(CGFloat)zq_x { CGRect frame = self.frame; frame.origin.x = zq_x; self.frame = frame; } - (CGFloat)zq_x { return self.frame.origin.x; } #pragma mark - leading set、get方法實(shí)現(xiàn) - (void)setZq_leading:(CGFloat)zq_leading { self.zq_x = zq_leading; } - (CGFloat)zq_leading { return self.zq_x; } #pragma mark - bottom set、get方法實(shí)現(xiàn) - (void)setZq_bottom:(CGFloat)zq_bottom { self.zq_y = zq_bottom - self.zq_height; } - (CGFloat)zq_bottom { return self.zq_y + self.zq_height; }
UIView的transform(形變)屬性
- 控件的形變包括平移、縮放、旋轉(zhuǎn)等屬性,類型是CGAffineTransform
- 常用形變方式舉例,效果以Scale(伸縮)為例說(shuō)明,假設(shè)寬高都是100:
-
方式一:CGAffineTransformMakeXXX:每次從零開(kāi)始形變,每次都是相對(duì)于最開(kāi)始的狀態(tài)形變
- 效果說(shuō)明:重復(fù)執(zhí)行代碼,每次寬高都是從(100,100)->(50,50)
self.view.transform = CGAffineTransformMakeTranslation(100, 100); self.view.transform = CGAffineTransformMakeScale(0.5, 0.5); self.view.transform = CGAffineTransformMakeRotation(M_PI);
- 效果說(shuō)明:重復(fù)執(zhí)行代碼,每次寬高都是從(100,100)->(50,50)
-
方式二:CGAffineTransformXXX:接著上次的狀態(tài)形變
- 效果說(shuō)明:重復(fù)執(zhí)行代碼,寬高是從(100,100)->(50,50)->(25,25)->...
self.view.transform = CGAffineTransformTranslate(self.view.transform, 100, 100); self.view.transform = CGAffineTransformScale(self.view.transform, 0.5, 0.5); self.view.transform = CGAffineTransformRotate(self.view.transform, M_PI);
- 效果說(shuō)明:重復(fù)執(zhí)行代碼,寬高是從(100,100)->(50,50)->(25,25)->...
-
方式三:清空形變
- 效果說(shuō)明:寬高從(?,?)-> (100, 100)
self.view.transform = CGAffineTransformIdentity;
- 效果說(shuō)明:寬高從(?,?)-> (100, 100)
-
矩形的坐標(biāo)系轉(zhuǎn)換

- 坐標(biāo)系:
- UIKit坐標(biāo)系 :以屏幕的左上角為坐標(biāo)原點(diǎn)
- view1坐標(biāo)系 : 以view1的左上角為坐標(biāo)原點(diǎn)
- view2坐標(biāo)系 : 以view2的左上角為坐標(biāo)原點(diǎn)
- 轉(zhuǎn)換過(guò)程:
- 判斷舊坐標(biāo)系,確定舊坐標(biāo)系原點(diǎn);
- 根據(jù)rect判斷矩形在坐標(biāo)系中的位置,可借助UIKit坐標(biāo)系中轉(zhuǎn);
- 確定新坐標(biāo)系,確定舊坐標(biāo)系原點(diǎn),判斷矩形在坐標(biāo)系中的位置
- 必須注意:rect不是控件,與控件無(wú)關(guān)系!只代表一塊區(qū)域
以convertRect: toView:為例;// 0.基礎(chǔ)數(shù)據(jù):UIKit坐標(biāo)系下,view1,view2的坐標(biāo) view1.frame = CGRectMake(100, 100, 100, 100); view2.frame = CGRectMake(200, 200, 100, 100); ------------------------------------------------- // 1.從零點(diǎn)矩形看坐標(biāo)轉(zhuǎn)換 // 不代表UIKit坐標(biāo)系左上角的點(diǎn)! CGRect zeroRect = CGRectZero; // {(0,0),(0,0)} CGRect newRect1 = [view1 convertRect:zeroRect toView:view2]; // view1坐標(biāo)系-> view2坐標(biāo)系 // UIKit坐標(biāo)系下,矩形位置為{(100,100),(0,0)},view2坐標(biāo)系左上角為{(200,200),(0,0)} // newRect1{(-100, -100), (0, 0)} CGRect newRect2 = [view2 convertRect:zeroRect toView:view1]; // view2坐標(biāo)系-> view1坐標(biāo)系 // UIKit坐標(biāo)系下,矩形位置為{(200,200),(0,0)},view1坐標(biāo)系左上角為{(100,100),(0,0)} // newRect2:{(100,100),(0,0)} ------------------------------------------------- // 2.rect={(100, 100, 100, 100)}看坐標(biāo)轉(zhuǎn)換 // 不代表UIKit坐標(biāo)系中view1控件! rect1 = redView.frame = CGRectMake(100, 100, 100, 100); CGRect newRect3 = [view1 convertRect:rect1 toView:view2]; // view1坐標(biāo)系-> view2坐標(biāo)系 // UIKit坐標(biāo)系下,矩形位置為{(200,200),(100,100)},view2坐標(biāo)系左上角為{(200,100),(200,100)} // newRect3 = {(0, 0), (100, 100)} CGRect newRect4 = [view2 convertRect:zeroRect toView:view1]; // view2坐標(biāo)系-> view1坐標(biāo)系 // UIKit坐標(biāo)系下,矩形位置為{(300,300),(100,100)},view1坐標(biāo)系左上角為{(100,100),(100,100)} // newRect6 = {(200, 200), (100, 100)} - 矩形的坐標(biāo)轉(zhuǎn)換另一個(gè)方法的原理與上述一致,只是fromView才是轉(zhuǎn)換前坐標(biāo)系,不再多論。
// 結(jié)果一致 CGRect newRect = [view1 convertRect:rect1 fromView:view2]; CGRect newRect = [view2 convertRect:rect1 toView:view1]; - 點(diǎn)得坐標(biāo)系轉(zhuǎn)換可參照上述原理,使用方法是:
CGPoint point = CGPointMake(100, 100); // 結(jié)果一致 [view1 convertPoint:point toView:view2]; [view2 convertRect:point fromView:view1];
常用坐標(biāo)系轉(zhuǎn)換 -> 獲得控件在主窗口(屏幕)中的區(qū)域(rect)
- 坐標(biāo)系轉(zhuǎn)換:控件的坐標(biāo)系 -> 主窗口的坐標(biāo)系
1. 控件的坐標(biāo)系:
1. 父控件坐標(biāo)系 :以父控件的左上角為坐標(biāo)原點(diǎn)(常用)
2. 自身坐標(biāo)系 :以控件自身內(nèi)容層的左上角為坐標(biāo)原點(diǎn)(常用)
2. 主窗口的坐標(biāo)系:即UIKit坐標(biāo)系
- 注意:在控件的坐標(biāo)系下,rect描述的必須是控件所在區(qū)域;存在規(guī)律:
1. 若以自身左上角為轉(zhuǎn)換前坐標(biāo)系原點(diǎn),rect為self.bounds;
2. 若以父控件左上角為轉(zhuǎn)換前坐標(biāo)系原點(diǎn),rect為self.frame - 獲取寫法概況如下:
// 這里為使顯示簡(jiǎn)潔,定義KEY_WINDOW為主窗口 #define KEY_WINDOW ([UIApplication sharedApplication].keyWindow)- 寫法一(推薦):
// convertRect: toView: // 第一個(gè)參數(shù)(這里是KEY_WINDOW)不能設(shè)置為nil // redView.bounds的x、y設(shè)置不影響轉(zhuǎn)換結(jié)果 CGRect newRect = [redView convertRect:redView.bounds toView:KEY_WINDOW]; CGRect newRect = [redView.superview convertRect:redView.frame toView:KEY_WINDOW];
- 等價(jià)于:
// 規(guī)律:交換寫法一中第一與第三參數(shù),由toView->fromView // redView.bounds的x、y設(shè)置不影響轉(zhuǎn)換結(jié)果 // convertRect: fromView: CGRect newRect = [KEY_WINDOW convertRect:redView.bounds fromView:redView]; CGRect newRect = [KEY_WINDOW convertRect:redView.frame fromView:redView.superview]; - 等價(jià)于(假設(shè)redView或其父控件加入的是主窗口):
// redView.bounds的x、y設(shè)置不影響轉(zhuǎn)換結(jié)果 CGRect newRect = [redView convertRect:redView.bounds toView:nil]; CGRect newRect = [redView.superview convertRect:redView.frame toView:nil]; // 最后一個(gè)參數(shù)可以設(shè)置(toView:nil)->表示以自身所在的窗口(self.window)的左上角為坐標(biāo)系原點(diǎn); // 盡管可以省略,開(kāi)發(fā)中建議標(biāo)明轉(zhuǎn)換的坐標(biāo)系,而不是設(shè)置為nil,這是因?yàn)椋?// self.window大部分情況下是keyWindow;但如果控件是加入到其他窗口中(如狀態(tài)欄等),則不是keyWindow // 若是比較兩矩形間、點(diǎn)與矩形的關(guān)系,此時(shí)需要保證轉(zhuǎn)換后的坐標(biāo)系必須是相同的坐標(biāo)系; - 等價(jià)于:
// 通過(guò) @protocol UICoordinateSpace <NSObject> 中的方法 // CoordinateSpace:坐標(biāo)空間(系) CGRect newRect = [redView convertRect:redView.bounds toCoordinateSpace:KEY_WINDOW]; CGRect newRect = [KEY_WINDOW convertRect:redView.bounds fromCoordinateSpace:redView]; CGRect newRect = [redView.superview convertRect:redView.frame toCoordinateSpace:KEY_WINDOW]; CGRect newRect = [KEY_WINDOW convertRect:redView.frame fromCoordinateSpace:redView.superview]; -
不等價(jià)于
// 正確轉(zhuǎn)換(寫法一): CGRect newRect = [redView convertRect:redView.bounds toView:KEY_WINDOW]; CGRect newRect = [redView.superview convertRect:redView.frame toView:KEY_WINDOW]; // 偽轉(zhuǎn)換 -> 描述的區(qū)域不是redView CGRect newRect = [redView convertRect:redView.frame toView:KEY_WINDOW]; CGRect newRect = [redView.superview convertRect:redView.bounds toView:KEY_WINDOW]; // 正確轉(zhuǎn)換(寫法二): CGRect newRect = [KEY_WINDOW convertRect:redView.bounds fromView:redView]; CGRect newRect = [KEY_WINDOW convertRect:redView.frame fromView:redView.superview]; // 偽轉(zhuǎn)換 -> 描述的區(qū)域不是redView CGRect newRect = [KEY_WINDOW convertRect:redView.frame fromView:redView]; CGRect newRect = [KEY_WINDOW convertRect:redView.bounds fromView:redView.superview]; // 偽轉(zhuǎn)換 -> 邏輯錯(cuò)誤 // 是獲取redView坐標(biāo)系下區(qū)域?yàn)閞ect的矩形在KEY_WINDOW坐標(biāo)系下的區(qū)域newRect,而不是相反 CGRect newRect = [KEY_WINDOW convertRect:redView.bounds toView:redView]; CGRect newRect = [KEY_WINDOW convertRect:redView.frame toView:redView.superview]; CGRect newRect = [KEY_WINDOW convertRect:redView.frame toView:redView]; CGRect newRect = [KEY_WINDOW convertRect:redView.bounds toView:redView.superview];
- 寫法一(推薦):
坐標(biāo)系轉(zhuǎn)換應(yīng)用 -> 矩形區(qū)域(Rect)、點(diǎn)(Point)比較
- 將
不同坐標(biāo)系下的區(qū)域(點(diǎn))轉(zhuǎn)換成同一個(gè)坐標(biāo)系下的區(qū)域(點(diǎn))時(shí),可以進(jìn)行區(qū)域間關(guān)系判斷及區(qū)域與點(diǎn)的關(guān)系判斷 - 判斷rect1是否包含了rect2
bool CGRectContainsRect(CGRect rect1, CGRect rect2) - 判斷rect1和rect2是否有重疊
bool CGRectIntersectsRect(CGRect rect1, CGRect rect2) - 判斷point是不是在rect上
bool CGRectContainsPoint(CGRect rect1, CGRect rect2)