• iOS


    前言

    	NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusEnvironment>
    	
    	@available(iOS 2.0, *)		 public class UIView : UIResponder, NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusEnvironment
    
    • 苹果将控件的共同属性都抽取到父类 UIView 中,所有的控件最终都继承自 UIView。UIView 最纯洁、最干净的控件,拥有尺寸、位置、背景色等基本属性。

    • view:翻译过来是页面的意思,iOS 有层的概念。

    1、View 的创建

    • UIView 创建出来默认是透明的,在 iOS6 的时候是白色的。

    • Objective-C

      	// 实例化 view 对象,并设置 view 大小
      	UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 200, 100)]; 
        	
      	// 将 view 加到 window 上显示出来
      	[self.view addSubview:view];
      
    • Swift

      	// 实例化 view 对象,并设置 view 大小
      	let view:UIView = UIView(frame: CGRectMake(10, 20, 200, 100))
      
      	// 将 view 加到 window 上显示出来
      	self.view.addSubview(view)
      

    2、View 的设置

    • Objective-C

      	// 设置 frame
      	/*
      		frame:控件矩形框在父控件中的位置和尺寸,以父控件的左上角为坐标原点
      	*/
      	view.frame = CGRectMake(10, 20, 200, 100);
      
      	// 设置 bounds
      	/*
      		bounds:控件矩形框的位置和尺寸,以自己左上角为坐标原点,所以 bounds 的 x、y 一般为 0
      	*/
      	view.bounds = CGRectMake(0, 0, 200, 100);
      
      	// 设置中心位置
      	/*
      	 	控件中点的位置,以父控件的左上角为坐标原点
      	*/
      	view.center = self.view.center; 
      	
      	// 设置背景颜色
      	view.backgroundColor = [UIColor greenColor];
      
      	// 设置背景颜色半透明
      	view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
      	
      	// 设置视图透明度
      	/*
          	范围:0.0 ~ 1.0 ,0.0 透明,1.0 不透明(默认)
          	视图上的文字等内容的透明度也同时被改变
       	*/
      	view.alpha = 1.0;
      
      	// 设置 tag 值
      	/*
      		用于区分不同的 view ,所有继承 view 的类对象都可以设置 tag 值
      	*/
      	view.tag = 100;
          
      	// 设置用户交互属性
      	/*
      		YES:打开用户交互属性,NO:关闭用户交互属性
      	*/
      	view.userInteractionEnabled = YES;  
             
      	// 设置圆脚边框
      	/*
      		cornerRadius:圆角半径
      		masksToBounds:子图层是否剪切图层边界,默认为 NO
      	*/
      	view.layer.cornerRadius = 20;
      	view.layer.masksToBounds = YES;
          
      	// 设置边框
      	/*
      		borderWidth:边框粗细
      		borderColor:边框颜色
      	*/
      	view.layer.borderWidth = 5;
      	view.layer.borderColor = [[UIColor blueColor] CGColor];    
          
      	// 不允许子视图的范围超过父视图的范围
      	/*
      		不允许 view 的子视图的范围超过 view 的范围,在父视图上设置
      	*/
      	view.clipsToBounds = NO;
          
      	// 获得自己的所有子控件对象
      	/*
      		数组元素的顺序决定着子控件的显示层级顺序(下标越大的,越显示在上面)
      	*/
      	NSArray *subviews = self.view.subviews;
         	
      	// 获得自己的父控件对象
      	UIView *superview = self.view.superview;
         	
      	// 从父视图中移除
      	[view removeFromSuperview];
      	
      	// 根据一个 tag 标识找出对应的控件
      	UIView *view = [self.view viewWithTag:100];
      
    • Swift

      	// 设置 frame
      	/*
      		控件矩形框在父控件中的位置和尺寸,以父控件的左上角为坐标原点
      	*/
      	view.frame = CGRectMake(10, 20, 200, 100)
          
      	// 设置 bounds
      	/*
      		控件矩形框的位置和尺寸,以自己左上角为坐标原点,所以 bounds 的 x、y 一般为 0
      	*/
      	view.bounds = CGRectMake(0, 0, 200, 100)
          
      	// 设置中心位置
      	/*
      		控件中点的位置,以父控件的左上角为坐标原点
      	*/
      	view.center = self.view.center
          
      	// 设置背景颜色
      	view.backgroundColor = UIColor.greenColor()
      
      	// 设置背景颜色半透明
      	view.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.5)
      	
      	// 设置视图透明度
      	/*
          	范围:0.0 ~ 1.0 ,0.0 透明,1.0 不透明(默认)
          	视图上的文字等内容的透明度也同时被改变
       	*/
      	view.alpha = 1.0
      
      	// 设置 tag 值
      	/*
      		用于区分不同的 view ,所有继承 view 的类对象都可以设置 tag 值
      	*/
      	view.tag = 100
          
      	// 设置用户交互属性
      	/*
      		true:打开用户交互属性,false:关闭用户交互属性
      	*/
      	view.userInteractionEnabled = true
          
      	// 设置圆脚边框
      	/*
      		cornerRadius:圆角半径
      		masksToBounds:子图层是否剪切图层边界,默认为 false
      	*/
      	view.layer.cornerRadius = 20
      	view.layer.masksToBounds = true
          
      	// 设置边框
      	/*
      		borderWidth:边框粗细
      		borderColor:边框颜色
      	*/
      	view.layer.borderWidth = 5
      	view.layer.borderColor = UIColor.blueColor().CGColor
          
      	// 子视图的范围不允许超过父视图的范围
      	/*
      		不允许 view 的子视图的范围超过 view 的范围,在父视图上设置
      	*/
      	view.clipsToBounds = true
          
      	// 获得自己的所有子控件对象
      	/*
      		数组元素的顺序决定着子控件的显示层级顺序(下标越大的,越显示在上面)
      	*/
      	let subviews:Array = self.view.subviews
             	
      	// 获得自己的父控件对象
      	let superview:UIView? = self.view.superview
             	
      	// 从父视图中移除
      	view.removeFromSuperview()
      	
      	// 根据一个 tag 标识找出对应的控件
      	let view:UIView? = self.view.viewWithTag(100)
      

    3、View 的层次设置

    • 在 iOS 中后添加的 View 在上层。

    • Objective-C

      	// 放到最上层
      	/*
      		将 view1 放到最上层,层次关系 view2.view3.view1
      	*/
      	[self.view bringSubviewToFront:view1]; 
      
      	// 放倒最下面
      	/*
      		将 view3 放倒最下面,层次关系 view3.view1.view2
      	*/
       	[self.view sendSubviewToBack:view3]; 
      
      	// 位置进行交换
      	/*
      		将 view1 和 view3 位置进行交换,层次关系 view3.view2.view1
      	*/
       	[self.view exchangeSubviewAtIndex:0 withSubviewAtIndex:2];
      
      	// 放在上面
      	/*
      		将 view1 放在 view3 上面,层次关系 view2.view3.view1
      	*/
      	[self.view insertSubview:view1 aboveSubview:view3]; 
      
      	// 放在下面
      	/*
      		将 view3 放在 view2 下面,层次关系 view1.view3.view2
      	*/
      	[self.view insertSubview:view3 belowSubview:view2];  
      
      	// 放在 n 位置
      	/*
      		将 view1 放在1的位置,层次关系 view2.view1.view3
      	*/
      	[self.view insertSubview:view1 atIndex:1];
      
    • Swift

      	// 放到最上层
      	/*
      		将 view1 放到最上层,层次关系 view2.view3.view1
      	*/
      	self.view.bringSubviewToFront(view1)
      
      	// 放倒最下面
      	/*
      		将 view3 放倒最下面,层次关系 view3.view1.view2
      	*/
      	self.view.sendSubviewToBack(view3)
      
      	// 位置进行交换
      	/*
      		将 view1 和 view3 位置进行交换,层次关系 view3.view2.view1
      	*/
      	self.view.exchangeSubviewAtIndex(0, withSubviewAtIndex: 2) 
      
      	// 放在上面
      	/*
      		将 view1 放在 view3 上面,层次关系 view2.view3.view1
      	*/
      	self.view.insertSubview(view1, aboveSubview: view3)
      
      	// 放在下面
      	/*
      		将 view3 放在 view2 下面,层次关系 view1.view3.view2
      	*/
      	self.view.insertSubview(view3, belowSubview: view2)
      
      	// 放在 n 位置
      	/*
      		将 view1 放在1的位置,层次关系 view2.view1.view3
      	*/
      	self.view.insertSubview(view1, atIndex: 1)
      

    4、View 的旋转与缩放设置

    • Objective-C

      • 单一形变

        	// 控件的形变属性
        	@property(nonatomic) CGAffineTransform transform;
            
        	// 旋转
        	/*
        		(CGFloat angle) 旋转 45 度,需要输入的参数为弧度,45/180 * M_PI,1 度 = PI/180 弧度
        	*/
        	self.testView.transform = CGAffineTransformMakeRotation(0.25 * M_PI);
        	
        	[self.testView.layer setAffineTransform: CGAffineTransformMakeRotation(0.25 * M_PI)];
            
        	// 缩放
        	/*
        		(CGFloat sx, CGFloat sy) (1, 2) 宽度和高度的放大倍数
        	*/
        	self.testView.transform = CGAffineTransformMakeScale(1, 2);
        	
        	[self.testView.layer setAffineTransform: CGAffineTransformMakeScale(1, 2)];
            
        	// 平移
        	/*
        		(CGFloat tx, CGFloat ty) (100, 100) 水平和垂直方向的移动距离
        	*/
        	self.testView.transform = CGAffineTransformMakeTranslation(100, 100);
        	
        	[self.testView.layer setAffineTransform: CGAffineTransformMakeTranslation(100, 100)];
        
      • 叠加形变

        	// 旋转 + 缩放
        	CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);
        	self.testView.transform = CGAffineTransformScale(rotationTransform, 2, 2);
        
        	// 旋转 + 平移
        	CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);
        	self.testView.transform = CGAffineTransformTranslate(rotationTransform, 200, 100);
        
        	// 缩放 + 平移
        	CGAffineTransform scaleTransform = CGAffineTransformMakeScale(2, 2);
        	self.testView.transform = CGAffineTransformTranslate(scaleTransform, 100, 100);
        
        	// 旋转 + 缩放 + 平移
        	CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);
        	CGAffineTransform rotationScaleTransform = CGAffineTransformScale(rotationTransform, 2, 2);
        	self.testView.transform = CGAffineTransformTranslate(rotationScaleTransform, 200, 100);
        
      • 累加形变

        	// 连续旋转
        	self.testView.transform = CGAffineTransformRotate(self.testView.transform, 0.25 * M_PI);
        
        	// 连续缩放
        	self.testView.transform = CGAffineTransformScale(self.testView.transform, 2, 2);
        
        	// 连续平移
        	self.testView.transform = CGAffineTransformTranslate(self.testView.transform, 100, 100);
        
      • 还原形变

        	// 还原所有形变
        	self.testView.transform = CGAffineTransformIdentity;
        
        	[self.testView.layer setAffineTransform: CGAffineTransformIdentity];
        
    • Swift

      • 单一形变

        	// 控件的形变属性
        	public var transform: CGAffineTransform 
        	    
        	// 旋转
        	/*
        		(angle: CGFloat) 旋转 45 度,需要输入的参数为弧度,45/180 * M_PI,1 度 = PI/180 弧度
        	*/
        	self.testView.transform = CGAffineTransformMakeRotation(0.25 * CGFloat(M_PI))
        	
        	self.testView.layer.setAffineTransform(CGAffineTransformMakeRotation(0.25 * CGFloat(M_PI)))
        		
        	// 缩放
        	/*
        		(sx: CGFloat, _ sy: CGFloat) (1, 2) 宽度和高度的放大倍数
        	*/
        	self.testView.transform = CGAffineTransformMakeScale(1, 2)
        	
        	self.testView.layer.setAffineTransform(CGAffineTransformMakeScale(1, 2))
        	    
        	// 平移
        	/*
        		(tx: CGFloat, _ ty: CGFloat) 水平和垂直方向的移动距离
        	*/
        	self.testView.transform = CGAffineTransformMakeTranslation(100, 100)
        	
        	self.testView.layer.setAffineTransform(CGAffineTransformMakeTranslation(100, 100))
        
      • 叠加形变

        	// 旋转 + 缩放
        	let rotationTransform:CGAffineTransform = CGAffineTransformMakeRotation(0.25 * CGFloat(M_PI))
        	self.testView.transform = CGAffineTransformScale(rotationTransform, 2, 2)
        
        	// 旋转 + 平移
        	let rotationTransform:CGAffineTransform = CGAffineTransformMakeRotation(0.25 * CGFloat(M_PI))
        	self.testView.transform = CGAffineTransformTranslate(rotationTransform, 200, 100)
        
        	// 缩放 + 平移
        	let scaleTransform:CGAffineTransform = CGAffineTransformMakeScale(2, 2)
        	self.testView.transform = CGAffineTransformTranslate(scaleTransform, 100, 100)
        
        	// 旋转 + 缩放 + 平移
        	let rotationTransform:CGAffineTransform = CGAffineTransformMakeRotation(0.25 * CGFloat(M_PI))
        	let rotationScaleTransform:CGAffineTransform = CGAffineTransformScale(rotationTransform, 2, 2)
        	self.testView.transform = CGAffineTransformTranslate(rotationScaleTransform, 200, 100)
        
      • 累加形变

        	// 连续旋转
        	self.testView.transform = CGAffineTransformRotate(self.testView.transform, 0.25 * CGFloat(M_PI))
        
        	// 连续缩放
        	self.testView.transform = CGAffineTransformScale(self.testView.transform, 2, 2)
        
        	// 连续平移
        	self.testView.transform = CGAffineTransformTranslate(self.testView.transform, 100, 100)
        
      • 还原形变

        	// 还原所有形变
        	self.testView.transform = CGAffineTransformIdentity
        
        	self.testView.layer.setAffineTransform(CGAffineTransformIdentity)
        

    5、View 的跟随模式设置

    • Objective-C

      	// 父视图设置
      	/*
      		父视图允许子视图跟随
      		default is YES
      	*/
      	fatherView.autoresizesSubviews = YES;
          
      	// 子视图设置
      	/*
      		UIViewAutoresizingNone                 = 0,         // 不跟随
      		UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,    // 左边距 随父视图变化
      		UIViewAutoresizingFlexibleRightMargin  = 1 << 2,    // 右边距 随父视图变化
      		UIViewAutoresizingFlexibleTopMargin    = 1 << 3,    // 上边距 随父视图变化
      		UIViewAutoresizingFlexibleBottomMargin = 1 << 5     // 下边距 随父视图变化
      		
      		UIViewAutoresizingFlexibleWidth        = 1 << 1,    // 宽度 随父视图变化
      		UIViewAutoresizingFlexibleHeight       = 1 << 4,    // 高度 随父视图变化
      	*/
      	sonView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;          
      
    • Swift

      	// 父视图设置
      	/*
      		父视图允许子视图跟随
      		default is true
      	*/
      	fatherView.autoresizesSubviews = true
          
      	// 子视图设置
      	/*
              None                    // 不跟随
              FlexibleLeftMargin      // 左边距 随父视图变化
              FlexibleRightMargin     // 右边距 随父视图变化
              FlexibleTopMargin       // 上边距 随父视图变化
              FlexibleBottomMargin    // 下边距 随父视图变化
              
              FlexibleWidth           // 宽度 随父视图变化
              FlexibleHeight          // 高度 随父视图变化
      	*/
      	sonView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
      

    6、View 的动画设置

    6.1 block 方式

    • 设置控件位置、尺寸、透明度等的代码,放在 animateWithDuration: block 中,将自动以动画的方式改变。

    • Objective-C

      	// 移动时间 2 秒
      	[UIView animateWithDuration:2 animations:^{
      		
          	// 改变控件的位置和尺寸,改变后的位置或大小
          	view.frame = CGRectMake([UIScreen mainScreen].bounds.size.width - 60, 20, 50, 50);
      		
      	} completion:^(BOOL finished) {
      	                    
      		// 上一个设置完成后
      		
      		[UIView animateWithDuration:2 animations:^{
      			
      			// 改变控件的位置和尺寸,改变后的位置或大小
      			view.frame = CGRectMake(10, [UIScreen mainScreen].bounds.size.height - 110, 100, 100);
         		}];
      	}];
      
    • Swift

      	// 移动时间 2 秒
      	UIView.animateWithDuration(2, animations: {
      		
      		// 改变控件的位置和尺寸,改变后的位置或大小
      		view.frame = CGRectMake(UIScreen.mainScreen().bounds.size.width - 60, 20, 50, 50)
      		
          }) { (finished:Bool) in
      
      		// 上一个设置完成后
      		
      		UIView.animateWithDuration(2, animations: {
      			
      			// 改变控件的位置和尺寸,改变后的位置或大小
      			view.frame = CGRectMake(10, UIScreen.mainScreen().bounds.size.height - 110, 100, 100)
      		})
          }
      

    6.2 动画块方式

    • 设置控件位置、尺寸、透明度等的代码,放在 beginAnimations: 和 commitAnimations 之间,将自动以动画的方式改变。

    • Objective-C

      
      	// 开始一个动画块
      	[UIView beginAnimations:nil context:nil];
      	    
      	// 动画设置
      		
      		// 设置动画时间
      		/*
      		    default = 0.2
      		*/
      		[UIView setAnimationDuration:2.0];
      		    
      		// 设置延时
      		/*
      		    设置指定的时间后开始执行动画,default = 0.0
      		*/
      		[UIView setAnimationDelay:1.0];
      		    
      		// 设置动画开始执行时间
      		/*
      		    default = now ([NSDate date])
      		*/
      		[UIView setAnimationStartDate:[NSDate dateWithTimeIntervalSinceNow:10]];
      		    
      		// 设置动画执行节奏
      		/*
      		    UIViewAnimationCurveEaseInOut,         // slow at beginning and end  开始喝结束慢速,默认
      		    UIViewAnimationCurveEaseIn,            // slow at beginning          开始慢速
      		    UIViewAnimationCurveEaseOut,           // slow at end                结束慢速
      		    UIViewAnimationCurveLinear             //                            匀速
      		*/
      		[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
      		    
      		// 设置重复次数
      		/*
      		    default = 0.0.  May be fractional
      		*/
      		[UIView setAnimationRepeatCount:CGFLOAT_MAX];
      		    
      		// 设置是否自动返回
      		/*
      		    default = NO. used if repeat count is non-zero
      		*/
      		[UIView setAnimationRepeatAutoreverses:YES];
      		    
      		// 设置是否从当前状态开始动画
      		/*
      		    default = NO
      		*/
      		[UIView setAnimationBeginsFromCurrentState:YES];
      		    
      		// 设置代理
      		/*
      			default = nil
      		*/
      		[UIView setAnimationDelegate:self];
      		    
      		// 设置动画开始时执行的代理方法,自定义方法
      		/*
      		    default = NULL
      		*/
      		[UIView setAnimationWillStartSelector:@selector(startAnimations)];
      		    
      		// 设置动画结束时执行的代理方法,自定义方法
      		/*
      		    default = NULL
      		 */
      		[UIView setAnimationDidStopSelector:@selector(stopAnimations)];
      	    
      	// 动画之行后效果
      	
      		// 设置透明度,改变后的透明度
      		view.alpha = 0.0;
      		    
      		// 改变控件的位置和尺寸,改变后的位置或大小
      		view.center = CGPointMake(250, 250);
      		view.frame = CGRectMake(100, 180, 50, 50);
      	    
      	// 结束一个动画块
      	[UIView commitAnimations];
      
    • Swift

      	// 开始一个动画块
      	UIView.beginAnimations(nil, context: nil)
      	    
      	// 动画设置
      		
      		// 设置动画时间
      		/*
      		    default = 0.2
      		*/
      		UIView.setAnimationDuration(2.0)
      		    
      		// 设置延时
      		/*
      		    设置指定的时间后开始执行动画,default = 0.0
      		*/
      		UIView.setAnimationDelay(1.0)
      		    
      		// 设置动画开始执行时间
      		/*
      		    default = now ([NSDate date])
      		*/
      		UIView.setAnimationStartDate(NSDate(timeIntervalSinceNow: 10))
      		    
      		// 设置动画执行节奏
      		/*
      		    EaseInOut,         // slow at beginning and end  开始喝结束慢速,默认
      		    EaseIn,            // slow at beginning          开始慢速
      		    EaseOut,           // slow at end                结束慢速
      		    Linear             //                            匀速
      		*/
      		UIView.setAnimationCurve(.EaseOut)
      		    
      		// 设置重复次数
      		/*
      		    default = 0.0.  May be fractional
      		*/
      		UIView.setAnimationRepeatCount(9999999)
      		    
      		// 设置是否自动返回
      		/*
      		    default = NO. used if repeat count is non-zero
      		*/
      		UIView.setAnimationRepeatAutoreverses(true)
      		    
      		// 设置是否从当前状态开始动画
      		/*
      		    default = NO
      		*/
      		UIView.setAnimationBeginsFromCurrentState(true)
      		    
      		// 设置代理
      		/*
      		    default = nil
      		*/
      		UIView.setAnimationDelegate(self)
      		    
      		// 设置动画开始时执行的代理方法,自定义方法
      		/*
      		    default = NULL
      		*/
      		UIView.setAnimationWillStartSelector(#selector(UiView.startAnimations))
      		    
      		// 设置动画结束时执行的代理方法,自定义方法
      		/*
      		    default = NULL
      		*/
      		UIView.setAnimationDidStopSelector(#selector(UiView.stopAnimations))
      
      	// 动画之行后效果
      	
      		// 设置透明度,改变后的透明度
      		view.alpha = 0.0
      		    
      		// 改变控件的位置和尺寸,改变后的位置或大小
      		view.center = CGPointMake(250, 250)
      		view.frame = CGRectMake(100, 180, 50, 50)
      	    
      	// 结束一个动画块
      	UIView.commitAnimations()
      

    7、frame 与 NSValue 的相互转换

    • Objective-C

          // Frame 转 NSValue
          NSValue *freamValue = [NSValue valueWithCGRect:frame];
          
          // NSValue 转 Frame
          frame = [freamValue CGRectValue];
      
    • Swift

          // Frame 转 NSValue
          let freamValue:NSValue = NSValue(CGRect: frame)
          
          // NSValue 转 Frame
          frame = freamValue.CGRectValue()
      

    8、view 的封装(自定义控件)

    • 如果一个 view 内部的子控件比较多,一般会考虑自定义一个 view,把它内部子控件的创建屏蔽起来,不让外界关心。外界可以传入对应的模型数据给 view,view 拿到模型数据后给内部的子控件设置对应的数据。

    • 一个控件有 2 种创建方式:

      • 通过纯代码创建

        • 初始化时一定会调用 initWithFrame: 方法。想做一些初始化操作,应该在这个方法中执行。
        • 系统中 init 方法执行时会自动调用 initWithFrame: 方法。
      • 通过 xibstoryboard 创建

        • 初始化时不会调用 initWithFrame: 方法,只会调用 initWithCoder: 方法。
        • 初始化完毕后会调用 awakeFromNib 方法。想做一些初始化操作,应该在这个方法中执行。
      • 有时候希望在控件初始化时做一些初始化操作,比如添加子控件、设置基本属性,这时需要根据控件的创建方式,来选择在 initWithFrame:、initWithCoder:、awakeFromNib 的哪个方法中操作。

    8.1 纯代码自定义控件

    • 1> 在 initWithFrame: 方法中添加子控件,提供便利构造方法(类方法)。

      • 或者不使用 initWithFrame: 方法,而是使用子控件的懒加载方式,添加子控件。
    • 2> 在 layoutSubviews 方法中设置子控件的 frame(一定要调用 super 的 layoutSubviews)。

      • 控件的尺寸发生改变时会自动调用 layoutSubviews 方法,设置子控件的布局。
    • 3> 增加模型属性,在模型属性 setter 方法中设置数据到子控件上。

    • Objective-C

      • ShopView.h

        	#import <UIKit/UIKit.h>
        	
        	/// 数据模型
        	@class ShopModel;
        	
        	@interface ShopView : UIView
        	
        	/// 商品模型
        	@property (nonatomic, strong) ShopModel *shop;
        	
        	/// 便利的构造方法
        	+ (instancetype)shopView;
        	
        	@end
        
      • ShopView.m

        • 在 initWithFrame: 方法中添加子控件

          	#import "ShopView.h"
          	#import "ShopModel.h"
          	
          	@interface ShopView()
          	
          	/// 图片控件
          	@property (nonatomic, strong) UIImageView *iconView;
          	
          	/// 名字控件
          	@property (nonatomic, strong) UILabel *nameLabel;
          	
          	@end
          	
          	@implementation ShopView
          	
          	/// 便利的构造方法
          	+ (instancetype)shopView {
          	    return [[self alloc] init];
          	}
          
          	/**
          	 * 控件初始化方法
          	 * init 方法内部会自动调用 initWithFrame: 方法
          	 */
          	- (instancetype)initWithFrame:(CGRect)frame {
          	
          	    if (self = [super initWithFrame:frame]) {
          	        self.backgroundColor = [UIColor orangeColor];
          	        
          	        // 添加图片
          	        UIImageView *iconView = [[UIImageView alloc] init];
          	        iconView.backgroundColor = [UIColor blueColor];
          	        [self addSubview:iconView];
          	        self.iconView = iconView;
          	        
          	        // 添加文字
          	        UILabel *nameLabel = [[UILabel alloc] init];
          	        nameLabel.font = [UIFont systemFontOfSize:11];
          	        nameLabel.textAlignment = NSTextAlignmentCenter;
          	        nameLabel.backgroundColor = [UIColor redColor];
          	        [self addSubview:nameLabel];
          	        self.nameLabel = nameLabel;
          	    }
          	    return self;
          	}
          	
          	/**
          	 * 布局子控件
          	 * 这个方法专门用来布局子控件,一般在这里设置子控件的 frame
          	 * 当控件本身的尺寸发生改变的时候,系统会自动调用这个方法
          	 */
          	- (void)layoutSubviews{
          	
          	    // 一定要调用 super 的 layoutSubviews
          	    [super layoutSubviews];
          	    
          	    CGFloat shopW = self.frame.size.width;
          	    CGFloat shopH = self.frame.size.height;
          	    self.iconView.frame = CGRectMake(0, 0, shopW, shopW);
          	    self.nameLabel.frame = CGRectMake(0, shopW, shopW, shopH - shopW);
          	}
          	
          	/// 数据模型 setter 方法
          	- (void)setShop:(ShopModel *)shop {
          	
          	    _shop = shop;
          	    
          	    self.nameLabel.text = shop.name;
          	    self.iconView.image = [UIImage imageNamed:shop.icon];
          	}
          	
          	@end
          
        • 使用子控件的懒加载方法添加子控件

          	#import "ShopView.h"
          	#import "ShopModel.h"
          	
          	@interface ShopView()
          	
          	/// 图片控件
          	@property (nonatomic, strong) UIImageView *iconView;
          	
          	/// 名字控件
          	@property (nonatomic, strong) UILabel *nameLabel;
          	
          	@end
          	
          	@implementation ShopView
          	
          	/// 便利的构造方法
          	+ (instancetype)shopView {
          	    return [[self alloc] init];
          	}
          	
          	/// 子控件懒加载方法(getter 方法)
          	- (UIImageView *)iconView {
          	
          	    if (_iconView == nil) {
          	        UIImageView *iconView = [[UIImageView alloc] init];
          	        iconView.backgroundColor = [UIColor blueColor];
          	        [self addSubview:iconView];
          	        _iconView = iconView;
          	    }
          	    return _iconView;
          	}
          	
          	/// 子控件懒加载方法(getter 方法)
          	- (UILabel *)nameLabel {
          	
          	    if (_nameLabel == nil) {
          	        UILabel *nameLabel = [[UILabel alloc] init];
          	        nameLabel.font = [UIFont systemFontOfSize:11];
          	        nameLabel.textAlignment = NSTextAlignmentCenter;
          	        nameLabel.backgroundColor = [UIColor redColor];
          	        [self addSubview:nameLabel];
          	        _nameLabel = nameLabel;
          	    }
          	    return _nameLabel;
          	}
          
          	/**
          	 * 布局子控件
          	 * 这个方法专门用来布局子控件,一般在这里设置子控件的 frame
          	 * 当控件本身的尺寸发生改变的时候,系统会自动调用这个方法
          	 */
          	- (void)layoutSubviews{
          	
          	    // 一定要调用 super 的 layoutSubviews
          	    [super layoutSubviews];
          	    
          	    CGFloat shopW = self.frame.size.width;
          	    CGFloat shopH = self.frame.size.height;
          	    self.iconView.frame = CGRectMake(0, 0, shopW, shopW);
          	    self.nameLabel.frame = CGRectMake(0, shopW, shopW, shopH - shopW);
          	}
          	
          	/// 数据模型 setter 方法
          	- (void)setShop:(ShopModel *)shop {
          	
          	    _shop = shop;
          	    
          	    self.nameLabel.text = shop.name;
          	    self.iconView.image = [UIImage imageNamed:shop.icon];
          	}
          	
          	@end
          

    8.2 XIB 自定义控件

    • 新建自定义控件类。

      UIView2

    • 新建 xib 文件(文件名建议和自定义控件类的类名一致)。

      UIView3

    • 修改 xib 中控件绑定的类名。

      UIView4

    • 在类扩展中增加子控件的属性,关联 xib 中的子控件。封装 xib 的加载过程,增加模型属性,在模型属性 setter 方法中设置数据到子控件上。

    • Objective-C

      • ShopView.h

        	#import <UIKit/UIKit.h>
        	
        	@class ShopModel;
        	
        	@interface ShopView : UIView
        	
        	/** 模型数据 */
        	@property (nonatomic, strong) ShopModel *shop;
        	
        	+ (instancetype)shopView;
        	+ (instancetype)shopViewWithShopModel:(ShopModel *)shop;
        	@end
        
      • ShopView.m

        	#import "ShopView.h"
        	#import "ShopModel.h"
        	
        	@interface ShopView()
        	
        	/** 图标 */
        	@property (weak, nonatomic) IBOutlet UIImageView *iconView;
        	
        	/** 名字 */
        	@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
        	
        	@end
        	
        	@implementation ShopView
        	
        	+ (instancetype)shopView {
        	  	return [self shopViewWithShop:nil];
        	}
        	
        	+ (instancetype)shopViewWithShop:(ShopModel *)shop {
        	
        		// 加载 xib 自定义控件
        	  	ShopView *shopView = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) 
        	  	                                                    owner:nil 
        	  	                                                  options:nil] lastObject];
        	  	shopView.shop = shop;
        	    
        	  	return shopView;
        	}
        	
        	/// 数据模型 setter 方法
        	- (void)setShop:(ShopModel *)shop {
        	
        	  	_shop = shop;
        	    
        	  	// 设置子控件的数据
        	  	self.iconView.image = [UIImage imageNamed:shop.icon];
        	  	self.nameLabel.text = shop.name;
        	}
        	
        	@end
        

    9、九宫格计算思路

    UIView1

    • Objective-C

      	// 设置列数
      	int cols = 3;
      	
      	// 设置每一个单元格的尺寸
      	CGFloat shopW = 50;
      	CGFloat shopH = 70;
      
      	// 设置每一列和每一行之间的间距
      	/*
      		self.shopsView.frame.size.width 为父视图的宽度
      	*/
      	CGFloat colMargin = (self.shopsView.frame.size.width - cols * shopW) / (cols - 1);
      	CGFloat rowMargin = 10;
      
      	// 计算单元格的索引
      	/*
      		计算将要添加的单元格的索引
      	*/
      	NSUInteger index = self.shopsView.subviews.count;
      	    
      	// 计算单元格的 x 坐标值
      	NSUInteger col = index % cols;
      	CGFloat shopX = col * (shopW + colMargin);
      	    
      	// 计算单元格的 y 坐标值
      	NSUInteger row = index / cols;
      	CGFloat shopY = row * (shopH + rowMargin);
      	    
      	// 创建一个单元格视图控件
      	UIView *shopView = [[UIView alloc] init];
      	    
      	// 设置单元格的 frame
      	shopView.frame = CGRectMake(shopX, shopY, shopW, shopH);
      	
      	// 设置单元格视图控件的内容
      	shopView.shop = self.shops[index];
      
      	// 将单元格添加到父视图上
      	[self.shopsView addSubview:shopView];
      

    10、UI 控件 weak 引用

    • 我们知道,从 Storyboard 往编译器拖出来的 UI 控件的属性是 weak 的,那么,如果有一些 UI 控件我们要用代码的方式来创建,那么它应该用 weak 还是 strong 呢?为什么?

      	@property (weak, nonatomic) IBOutlet UIButton *myButton;
      
      • 这是一道有意思的问题。简单来说,这道题并没有标准答案,但是答案背后的解释却非常有价值,能够看出一个人对于引用计数,对于 view 的生命周期的理解是否到位。

      • 我们就能看到一些理解非常不到位的解释,例如说:Storyboard 拖线使用 weak 是为了规避出现循环引用的问题。

      • 这个理解是错误的,Storyboard 拖出来的控件即使是 strong 的,也不会有循环引用问题。UI 控件用默认用 weak,根源还是苹果希望只有这些 UI 控件的父 View 来强引用它们,而 ViewController 只需要强引用 ViewController.view 成员,则可以间接持有所有的 UI 控件。这样有一个好处是:在以前,当系统收到 Memory Warning 时,会触发 ViewController 的 viewDidUnload 方法,这样的弱引用方式,可以让整个 view 整体都得到释放,也更方便重建时整体重新构造。但是首先 viewDidUnload 方法在 iOS 6 开始就被废弃掉了,苹果用了更简单有效地方式来解决内存警告时的视图资源释放。总之就是,除非你特殊地操作 view 成员,ViewController.view 的生命期和 ViewController 是一样的了。

      • 所以在这种情况下,其实 UI 控件是不是 weak 其实关系并不大。当 UI 控件是 weak 时,它的引用计数是 1,持有它的是它的 superview,当 UI 控件是 strong 时,它的引用计数是 2,持有它的有两个地方,一个是它的 superview,另一个是这个 strong 的指针。UI 控件并不会持有别的对象,所以,不管是手写代码还是 Storyboard,UI 控件是 strong 都不会有循环引用的。

      • 那么回到我们的最初的问题,自己写的 view 成员,应该用 weak 还是 strong?我个人觉得应该用 strong,因为用 weak 并没有什么特别的优势,其实 weak 变量会有额外的系统维护开销的,如果你没有使用它的特别的理由,那么用 strong 的话应该更好。另外如果你要做 Lazy 加载,那么你也只能选择用 strong。当然,如果你非要用 weak,其实也没什么问题,只需要注意在赋值前,先把这个对象用 addSubView 加到父 view 上,否则可能刚刚创建完,它就被释放了。

    11、简述对 UIView、UIWindow 和 CALayer 的理解

    • UIView 对象定义了屏幕上的一个矩形区域,用于构建用户界面和响应用户触屏事件。一个 UIView 的实例可以包含和管理若干个子 UIView。UIView 的直接父类为 UIResponder 类,可以响应用户事件。

    • UIWindow 对象是所有 UIView 的根,管理和协调应用程序的显示。UIWindow 类是 UIView 的子类,可以看作是特殊的 UIView。一般应用程序只有一个 UIWindow 对象,即使有多个 UIWindow 对象,也只有一个 UIWindow 可以接受到用户的触屏事件。

    • UIViewController 对象负责管理所有 UIView 的层次结构,并处理用户的触摸事件。

    • UIScreen 可以获取设备屏幕的大小。

    • CALayer 直接从 NSObject 继承,因为缺少了 UIResponder 类,所以 CALayer 不能响应任何用户事件。QuartzCore 是 iOS 中提供图像绘制的基础库,并且 CALayer 是定义该框架中。CALayer 定义了 position、size、transform、animations 等基本属性。UIView 是基于 CALayer 的高层封装。UIView 相比 CALayer 最大区别是 UIView 可以响应用户事件,而 CALayer 不可以。UIView 侧重于对显示内容的管理,CALayer 侧重于对内容的绘制。UIView 和 CALayer 是相互依赖的关系。UIView 依赖与 calayer 提供的内容,CALayer 依赖 UIView 提供的容器来显示绘制的内容。归根到底 CALayer 是这一切的基础,如果没有 CALayer,UIView 自身也不会存在,UIView 是一个特殊的 CALayer 实现,添加了响应事件的能力。UIView 来自 CALayer,高于 CALayer,是 CALayer 高层实现与封装。UIView 的所有特性来源于 CALayer 支持。

  • 相关阅读:
    Eval版的ASP木马原理解析
    cmd命令
    Eval版的ASP木马原理解析
    Vbs脚本实现radmin终极后门
    迅雷是如何识别并偷偷上传文件的?
    迅雷是如何识别并偷偷上传文件的?
    广外男生病毒代码剖析
    cmd命令
    Vbs脚本实现radmin终极后门
    广外男生病毒代码剖析
  • 原文地址:https://www.cnblogs.com/QianChia/p/5746902.html
Copyright © 2020-2023  润新知