IOS - 实时显示iOS编写UI代码效果(控件的懒加载和使用Plist文件将数据与代码分离),iosplist

编写iOS应用UI的方式大概有两种,一种是Storyboard/Xib,另一种是手写代码。采用Storyboard/Xib方式组织UI,由于提供可视化的特性,只要从UI库中拖动UI控件,便可以显示结果,极大地提高开发速度。但面临一个问题就是多人协作开发,由于所有的UI都放在同一个Storyboard文件中,使用Git/SVN合并代码就会出现冲突。多人协作开发还不是主要问题,有人提出可以创建多个Storyboard来分开UI编写,而Storyboard/Xib最主要问题是代码复用性比较差。所以有些人就选择手写UI代码,这样不仅可以解决多人协作开发问题,而且通过自定义控件在多个View使用。但每次手写UI代码后都要编译、构建和运行,最后在模拟器显示,这样会拖慢开发速度。如果每次修改UI控件后,保存修改便实时在模拟器显示修改后结果,就可以极大的提高编写UI的速度。

Live Change.gif

Auto Layout

Auto Layout是什么

Auto Layout是一个基于constraint(约束)的布局系统,它根据UI元素之间约束关系来调整UI元素的位置和大小。

Auto Layout解决什么问题

  • 更容易适配不同分辨率设备的屏幕(iPhone 6 Plus, iPhone 6, iPhone 5s/5, iPhone 4s/4)
  • 当设备旋转时不需要做额外处理
  • 使用constraint来描述布局逻辑,更利于理解和清晰

如何使用Auto Layout

Auto Layout中约束的类对应是NSLayoutConstraint, 而创建NSLayoutConstraint对象主要有两种方式,第一种是

上面方法主要意思是,某个view1的attribute1等于(小于或等于/大于或等于)某个view2的attribute2的multiplier倍加上constant。而attribute主要由表示位置(上/下/左/右)和大小(宽/高)的以下几个值:

简化一下,使用公式可以表达为:

第二种方式是:

这种方式主要是采用Visual Format Language(可视化格式语言)来描述约束布局,虽然语法比较简洁,但是可读性比较差和容易出错。

Auto Layout存在问题

虽然Auto Layout在布局view方面是非常强大和灵活,但是创建constraint的语法过于繁杂,引用Masonry一个例子:

如此简单的一个例子都要编写这么多行代码,想象一下如果创建多个view的constraint时会多么痛苦啊。另一个方式是采用Visual Format Language (VFL),虽然语法比较简洁,但是可读性比较差和容易出错。

Masonry

为什么使用Masonry

Masonry是采用链式DSL(Domain-specific language)来封装NSLayoutConstraint,通过这种方式编写Auto Layout布局代码更加易读和简洁。
使用Masonry的MASConstraintMaker来表达相同constraint

甚至可以更短

如何使用

使用Masonry创建constraint来定义布局的方式有三种:mas_makeConstraintsmas_updateConstraintsmas_remakeConstraints

1. mas_makeConstraints

使用mas_makeConstraints创建constraint后,你可以使用局部变量或属性来保存以便下次引用它;如果创建多个constraints,你可以采用数组来保存它们。

2. mas_updateConstraints

有时你需要更新constraint(例如,动画和调试)而不是创建固定constraint,可以使用mas_updateConstraints方法

3. mas_remakeConstraints

mas_remakeConstraintsmas_updateConstraints比较相似,都是更新constraint。不过,mas_remakeConstraints是删除之前constraint,然后再添加新的constraint(适用于移动动画);而mas_updateConstraints只是更新constraint的值。

想了解以上三个代码片段的更多细节,可以下载Masonry iOS Examples工程查阅。

Classy

Classy简介和特性

Classy是一个能与UIKit无缝结合stylesheet(样式)系统。它借鉴CSS的思想,但引入新的语法和命名规则。

灵活内嵌的语法

{ } : ; 这些语法符号是可选的,你可以选择适合自己的风格来表达stylesheet。

你可以使用{ } : ; 来限定stylesheet

或者你使用空格来限定stylesheet

默认样式

Classy在应用程序Bundle默认查找文件名为stylesheet.cas的样式文件。如果你采用这个文件名,你可以不用做任何东西就能加载样式文件。
但如果你想指定其他file path(样式文件名),你可以创建[CASStyler defaultStyler]

如果你还想当发生错误时,获取错误信息以便于调试,可以使用-(void)setFilePath:error:

如果你是使用Storyboard/Xib组织UI界面,那就需要在main.mint main(int argc, char * argv[])方法设置 filePath,这样可以确保在创建UIWindow之前加载stylesheet。否则(采用手写UI代码),你在 AppDelegate.m- (BOOL)application:didFinishLaunchingWithOptions:方法设置filePath

Live Reload

Live Reload是实时显示编写UI代码效果的关键特性,它能够实时检查stylesheet文件变化,无需重新编译、构建和运行模拟器,从而极大提高开发速度。
为了启用Live Reload,你需要指定stylesheet路径,并且只运行在模拟器上。

Selectors

Style Selectors是指定哪个view使用哪种样式的方式。主要有三种方法来指定目标view:

  1. Object Class
  2. View Hierarchy
  3. Style Class

你可以混合使用三种方法,例子如下:

想了解具体如何使用,请查阅官网Selectors章节

为了避免与Objective-C的message selectors混淆,术语style selectors表示Classy stylesheets的selectors

Properties

Classy支持所有UIAppearance的属性和方法,也支持与UIAppearance无关的很多属性。Classy使用与UIKit相同属性命名,所以你不必考虑如何将style property映射到Objective-C的property
UIPageControl类的属性如下:

style property的名字采用与objective-c一样的名字

style property的命名规则采用kebab case

想了解具体如何使用,请查阅官网Properties章节

Keep it DRY(Don’t Repeat Yourself)

在编程中一个很重要的原则就是避免重复,这不仅可以大量减少重复代码,并且使得代码更加容易复用和维护。Classy提供三种方式避免代码重复:grouping,nestingvariables

Grouping

如果有两个以上的style selectors共用相同的属性时

我们可以提取相同的属性到分组style selector中

Nesting

如果两个以上style selectors共用相同的view hierarchy时

我们通过nesting方式将view hierarchies表达成这样方式

Variables

Classy让你通过定义variables来将多个相同的style property值存储以便共享。Variable命名规则如下:

  • 必须以大小写字母$符号开头
  • 可以包含_-或任何字母数字

    最后官方还提供一个实例来解释具体如何使用:Custom Views Example

    ClassyLiveLayout

    ClassyLiveLayout通过结合Classy stylesheets与Masonry一起使用,能够在运行的模拟器中微调Auto Layout约束实时显示效果的工具。

    ClassyLiveLayout一个核心category:UIView+ClassyLayoutProperties,在UIView定义以下属性:

    cas_margincas_size分别表示UI元素的位置和大小,而其余的属性都是对两个属性进一步细分。我们可以从stylesheets中访问style properties来定义constraints布局,做到将数据与代码分离,有利于修改和复用代码。

    我们可以在updateConstraintsupdateViewConstrains定义布局时引用style properties

    当定义view layouts时,将Auto Layout的constraints都放在stylesheets中实时加载(Live reload)。如果你修改constraints,无需重新编译、构建和运行模拟器便能实时看到修改后的效果。

    示例工程

    配置工程

    由于需要引用Masonry,Classy和ClassyLiveLayout,Podfile配置如下:

    编写代码

    1. 添加stylesheet.cas文件到工程

    当安装好Masonry,Classy和ClassyLiveLayout后,第一次运行项目会出现没有stylesheet.cas文件错误:


    No stylesheet.cas file error.png

    只要向工程添加空的stylesheet.cas文件即可。


    Create stylesheet.cas file.png

    2. 创建LiveView类,该类继承SHPAbstractView


    Create LiveView inherit SHPAbstractView.png

    ViewController创建LiveView对象,然后被self.view引用。


    Setup root view in ViewController.png

    当编译运行时,在SHPAbstractView.h由于找不到UIView出现编译错误。


    SHPAbstractView Compile error.png

    只需引入UIKit便可以解决,但运行一下应用程序,出现一下错误:


    Must override methods.png

    主要原因是任何自定义UIView继承SHPAbstractView都需要override两个方法:- (void)addSubviews- (void)defineLayout,我们可以查看SHPAbstractView的源码可知:


    SHPAbstractView Source Code .png

    所以只要在LiveView.m文件覆盖两个方法即可

    3. LiveView类设计

    LiveView主要由包含redBoxViewblueBoxView两个属性,redBoxView表示红色方块,blueBoxView表示蓝色方块。

    4. LiveView类实现

    由于SHPAbstractView类如何初始化View已经做了处理,暴露两个接口- (void)addSubviews-(void)defineLayout分别处理构建view hierarchy和定义布局,子类只要覆盖SHPAbstractView这两个方法就可以创建LiveView了。
    但是我们将Auto Layout的constraints都放在stylesheets中实时加载(Live reload),即放在本工程的stylesheet.cas文件,将布局数据和布局代码分离。

    有了constraints数据后,便可以在代码布局:

    5. 模拟器支持Live Reload

    为了启用Live Reload,你需要指定stylesheet路径,并且只运行在模拟器上。


    Support Live Reload.png

    此时效果:


    Live Change.gif

    6. 分离样式文件

    由于有网友提出这样一个问题:如果所有view的样式都放在同一个stylesheet.cas文件,会让stylesheet.cas文件繁杂,并且当多个人协同开发时,不易于合并代码,所以有必要将样式文件分离到多个文件中。

    1.创建variable.cas文件,并将redBox对应UIView的样式放在variable.cas文件中。


    variable.cas file.png

    2.在stylesheet.cas样式文件使用@import指令引用variable.cas文件


    stylesheet.cas file.png

    最后效果


    Live Change 1.gif


    Live Change 2.gif

    示例代码存放地址:LiveAutoLayout

    总结

    之前手写UI代码每次更改一般都要重新编译、构建和运行模拟器才能看到效果,但结合使用Masonry,Classy和ClassLiveLayout之后,告别这个费时过程,极大地提高开发速度;不仅如此,我们将Auto Layout的constraints都放在stylesheets中实时加载(Live reload),将布局数据和布局代码分离,使得代码更加复用和维护。Classy还提供三种避免重复方法:Grouping, Nestting和Variable,尽可能复用样式数据。


    范例二

    使用UIImageView、UILabel、UIButton实现一个综合小案例

    功能分析

    (1)点击箭头切换序号、图片、描述 (2)如果是首张图片,左边箭头不能点击 (3)如果是尾张图片,右边箭头不能点击

    步骤分析

    (1)搭建UI界面 (2)监听按钮点击

    切换序号、图片、描述

     

    1. 界面分析

    1> 需要读取或修改的属性的控件

    // 序号标签

    // 图片

    // 图片描述

    // 左边按钮

    // 右边按钮

    2> 需要监听响应事件的对象,需要添加监听方法

    // 左边按钮

    // 右边按钮

    uiimage 是图片,不是控件,他的父类为NSObject,UIImageView是加载图片的控件,父类为UIView

    完全的代码编写界面(复习回忆)

    #import "ViewController.h"
    
    @interface ViewController ()
    //序号标签
    @property (nonatomic, strong) UILabel *noLabel;
    //图片
    @property (nonatomic, strong) UIImageView *icon;
    //图片描述
    @property (nonatomic, strong) UILabel *descLabel;
    //左边按钮
    @property (nonatomic, strong) UIButton *leftButton;
    //右边按钮
    @property (nonatomic, strong) UIButton *rightButton;
    @end
    
    @implementation ViewController
    
    //初始化工作
    //viewDidLoad是视图加载完成后调用的方法,通常在此方法中执行视图控制器的初始化工作
    - (void)viewDidLoad {
        [super viewDidLoad];
        //实例化控件
        //1、序号标签的编写
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 320, 40)];
        label.text = @"1/5";
        //居中对齐
        label.textAlignment = NSTextAlignmentCenter;
        [self.view addSubview:label];
        //记录改变
        self.noLabel = label;
        
        //2、图片控件
        CGFloat imageW = 200;
        CGFloat imageH = 200;
        CGFloat imageX = (320 - imageW) / 2;
        CGFloat imageY = 80;
        //实例化一个图像视图
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(imageX, imageY, imageW, imageH)];
        //实例化一个图像
        UIImage *image = [UIImage imageNamed:@"biaoqingdi"];
        //把图片显示到imageView
        imageView.image = image;
        [self.view addSubview:imageView];
        //记录下改变
        self.icon = imageView;
        
        //3、图片描述 label 控件
        UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 300, 300, 80)];
        label1.text = @"发发发";
        //居中对齐
        label1.textAlignment = NSTextAlignmentCenter;
        [self.view addSubview:label1];
        //记录改变
        self.descLabel = label1;
        
        //4、左边的按钮
        UIButton *leftBtn = [[UIButton alloc] init];
        //设置按钮的背景图
        [leftBtn setBackgroundImage:[UIImage imageNamed:@"left_normal"] forState:UIControlStateNormal];
        [leftBtn setBackgroundImage:[UIImage imageNamed:@"left_highlighted"] forState:UIControlStateHighlighted];
        //设置按钮的大小
        leftBtn.frame = CGRectMake(0, 0, 40, 40);
        //设置按钮的位置
        leftBtn.center = CGPointMake(self.icon.frame.origin.x / 2, self.icon.center.y);
        
        [self.view addSubview:leftBtn];
        self.leftButton = leftBtn;
        
        //5、右边的按钮
        UIButton *rightBtn = [[UIButton alloc] init];
        //设置按钮的背景图
        [rightBtn setBackgroundImage:[UIImage imageNamed:@"right_normal"] forState:UIControlStateNormal];
        [rightBtn setBackgroundImage:[UIImage imageNamed:@"right_highlighted"] forState:UIControlStateHighlighted];
        //设置按钮的大小
        rightBtn.frame = CGRectMake(0, 0, 40, 40);
        //设置按钮的位置
        rightBtn.center = CGPointMake(self.view.frame.size.width - self.icon.frame.origin.x / 2, self.icon.center.y);
        
        [self.view addSubview:rightBtn];
        self.leftButton = rightBtn;
    }
    
    @end

    完整的代码如下:

    #import "ViewController.h"
    
    @interface ViewController ()
    //序号标签
    @property (nonatomic, strong) UILabel *noLabel;
    //图片
    @property (nonatomic, strong) UIImageView *icon;
    //图片描述
    @property (nonatomic, strong) UILabel *descLabel;
    //左边按钮
    @property (nonatomic, strong) UIButton *leftButton;
    //右边按钮
    @property (nonatomic, strong) UIButton *rightButton;
    
    //图片索引,index默认是0
    @property (nonatomic, assign) int index;
    
    /**设置一个图像的数组*/
    //新的注释,可以显式中文
    @property (nonatomic, strong) NSArray *imageList;
    
    /*
     @property 
     自动为我们生成 set,get 方法的声明和实现
     带下划线的成员变量
     */
    @end
    
    @implementation ViewController
    
    //控件懒加载
    //不需要每次都在 viewdidload 里实例化数组,只要在需要的时候实例化即可
    //重写 get 方法
    - (NSArray *)imageList
    {
        //只有第一次调用imageList 的 getter 方法的时候,如果为空,那么再实例化并建立数组,其他时候,直接返回成员变量
        if (_imageList == nil) {
            //使用字典
            NSDictionary *dict1 = @{@"name" : @"biaoqingdi", @"desc" : @"表情"};
            NSDictionary *dict2 = @{@"name" : @"bingli", @"desc" : @"病历"};
            NSDictionary *dict3 = @{@"name" : @"chiniupa", @"desc" : @"吃牛扒"};
            NSDictionary *dict4 = @{@"name" : @"danteng", @"desc" : @"蛋疼"};
            NSDictionary *dict5 = @{@"name" : @"wangba", @"desc" : @"王八"};
            
            self.imageList = @[dict1, dict2, dict3, dict4, dict5];
        }
        
        return _imageList;
    }
    
    //初始化工作
    //viewDidLoad是视图加载完成后调用的方法,通常在此方法中执行视图控制器的初始化工作
    - (void)viewDidLoad {
        [super viewDidLoad];
        //实例化控件
        //1、序号标签的编写
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 320, 40)];
      //  label.text = @"1/5";
        //居中对齐
        label.textAlignment = NSTextAlignmentCenter;
        [self.view addSubview:label];
        //记录改变
        self.noLabel = label;
        
        //2、图片控件
        CGFloat imageW = 200;
        CGFloat imageH = 200;
        CGFloat imageX = (320 - imageW) / 2;
        CGFloat imageY = 80;
        //实例化一个图像视图
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(imageX, imageY, imageW, imageH)];
        //实例化一个图像
       // UIImage *image = [UIImage imageNamed:@"biaoqingdi"];
        //把图片显示到imageView
       // imageView.image = image;
        //把图像增加到 view
        [self.view addSubview:imageView];
        //记录下改变
        self.icon = imageView;
        
        //3、图片描述 label 控件
        UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 300, 300, 80)];
       // label1.text = @"发发发";
        //居中对齐
        label1.textAlignment = NSTextAlignmentCenter;
        [self.view addSubview:label1];
        //记录改变
        self.descLabel = label1;
        
        //4、左边的按钮
        UIButton *leftBtn = [[UIButton alloc] init];
        //设置按钮的背景图
        [leftBtn setBackgroundImage:[UIImage imageNamed:@"left_normal"] forState:UIControlStateNormal];
        [leftBtn setBackgroundImage:[UIImage imageNamed:@"left_highlighted"] forState:UIControlStateHighlighted];
        //设置按钮的大小
        leftBtn.frame = CGRectMake(0, 0, 40, 40);
        //设置按钮的位置
        leftBtn.center = CGPointMake(self.icon.frame.origin.x / 2, self.icon.center.y);
        [self.view addSubview:leftBtn];
        //设置监听
        [leftBtn addTarget:self action:@selector(leftClick) forControlEvents:UIControlEventTouchUpInside];
        self.leftButton = leftBtn;
        
        //5、右边的按钮
        UIButton *rightBtn = [[UIButton alloc] init];
        //设置按钮的背景图
        [rightBtn setBackgroundImage:[UIImage imageNamed:@"right_normal"] forState:UIControlStateNormal];
        [rightBtn setBackgroundImage:[UIImage imageNamed:@"right_highlighted"] forState:UIControlStateHighlighted];
        //设置按钮的大小
        rightBtn.frame = CGRectMake(0, 0, 40, 40);
        //设置按钮的位置
        rightBtn.center = CGPointMake(self.view.frame.size.width - self.icon.frame.origin.x / 2, self.icon.center.y);
        
        [self.view addSubview:rightBtn];
        
        //设置监听
        [rightBtn addTarget:self action:@selector(rightClick) forControlEvents:UIControlEventTouchUpInside];
        self.rightButton = rightBtn;
        
        [self change];
    }
    
    - (void)change
    {
        //更具 self.index 来显示序号标签,图形,,描述
        self.noLabel.text = [NSString stringWithFormat:@"%d / %d", self.index + 1, 5];
        self.icon.image = [UIImage imageNamed:self.imageList[self.index][@"name"]];
        self.descLabel.text = self.imageList[self.index][@"desc"];
        
        self.leftButton.enabled = (self.index != 0);
        self.rightButton.enabled = (self.index != 4);
    }
    
    //left
    - (void)leftClick
    {
        self.index--;
        [self change];
    }
    
    //right
    - (void)rightClick
    {
        self.index++;
        [self change];
    }
    
    @end

     

    小结:

    /**设置一个图像的数组*/

    这是 xcode 的新的注释,鼠标浮动时,可以显式出中文注释。

     

    手码懒加载创建控件的步骤

    1> 定义控件属性,注意:属性必须是strong的,如下:

    @property (nonatomic, strong) UIImageView *icon;

     

    2> 在属性的getter方法中实现懒加载。

     

    使用懒加载的好处:

    1> 不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强

    2> 每个控件的getter方法中分别负责各自的实例化处理,代码彼此之间的独立性强,松耦合

     

    按钮的状态

    normal(普通状态) 默认情况 对应的枚举常量:UIControlStateNormal   highlighted(高亮状态) 按钮被按下去的时候(手指还未松开) 对应的枚举常量:UIControlStateHighlighted   disabled(失效状态,不可用状态) 如果enabled属性为NO,就是处于disable状态,代表按钮不可以被点击 对应的枚举常量:UIControlStateDisabled  

    使用Plist文件重构本项目代码:

    目的:将数据与代码分离(类似 java 里的 xml 文件写数据,数据和代码分离)

    之前的代码,尤其是字典那部分,还是处理的不好,显得太耦合。需要把数据和代码分离,这里学习属性列表文件,property list

    新建file

    本地文件,也可以网络上解析 xml 文件

     

    这样,只需要修改对应的 xml 文件即可,不用再打开代码,修改代码

    //控件懒加载
    //不需要每次都在 viewdidload 里实例化数组,只要在需要的时候实例化即可
    - (NSArray *)imageList
    {
        //只有第一次调用imageList 的 getter 方法的时候,如果为空,那么再实例化并建立数组,其他时候,直接返回成员变量
        if (_imageList == nil) {
            //bundle 包的概念  只读
            NSString *path = [[NSBundle mainBundle] pathForResource:@"imageDate" ofType:@".plist"];
            NSLog(@"%@", path);
            //File 表示从完整路径查找文件
            _imageList = [NSArray arrayWithContentsOfFile:path];
        }
        
        return _imageList;
    }

    小结:

    1、将数据与代码分离,Plist 文件的加载方法:

    直接将数据直接写在代码里面,不是一种合理的做法。如果数据经常改,就要经常翻开对应的代码进行修改,造成代码扩展性低,因此,可以考虑将经常变的数据放在文件中进行存储,程序启动后从文件中读取最新的数据。如果要变动数据,直接修改数据文件即可,不用修改代码。 一般可以使用属性列表文件存储NSArray或者NSDictionary之类的数据,这种属性列表文件的扩展名是plist,因此也成为“Plist文件”

    NSString *path = [[NSBundle mainBundle] pathForResource:@"ImageData" ofType:@"plist"];

    _imageList = [NSArray arrayWithContentsOfFile:path];

    提示:通常在方法中出现File字眼,通常需要传递文件的全路径作为参数,如下全路径:

    /Users/dashuai/Library/Developer/CoreSimulator/Devices/83C611C9-DE98-4D02-BC64-D31C0403766E/data/Containers/Bundle/Application/E04713CF-A9D4-49D1-A934-B4093BCE5B3C/图片浏览.app/imageDate.plist

     

    2、要想让UILabel自动换行,设置Lines为0即可。

     

    3、UIButton和UIImageView

    相同点 都能显示图片   不同点 UIButton默认情况就能监听点击事件,而UIImageView默认情况下不能 UIButton可以在不同状态下显示不同的图片 UIButton既能显示文字,又能显示图片   如何选择 UIButton:需要显示图片,点击图片后需要做一些特定的操作 UIImageView:仅仅需要显示图片,点击图片后不需要做任何事情   NSArray和NSDictionary的使用 当图片内容非常多时,“根据index来设置内容”的代码就不具备扩展性,要经常改动,为了改变现状,可以考虑将图片数据保存到一个数组中,数组中有序地放着很多字典,一个字典代表一张图片数据,包含了图片名、图片描述

    @property (strong, nonatomic) NSArray *images;

    由于只需要初始化一次图片数据,因此放在get方法中初始化,将属性放在get方法中初始化的方式,称为“懒加载”\”延迟加载”

     

    /**设置一个图像的数组*/

    这是 xcode 的新的注释,鼠标浮动时,可以显式出中文注释。

     

    手码懒加载创建控件的步骤

    1> 定义控件属性,注意:属性必须是strong的,如下:

    @property (nonatomic, strong) UIImageView *icon;

     

    2> 在属性的getter方法中实现懒加载。

     

    使用懒加载的好处:

    1> 不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强

    2> 每个控件的getter方法中分别负责各自的实例化处理,代码彼此之间的独立性强,松耦合

     

    按钮的状态

    normal(普通状态) 默认情况 对应的枚举常量:UIControlStateNormal   highlighted(高亮状态) 按钮被按下去的时候(手指还未松开) 对应的枚举常量:UIControlStateHighlighted   disabled(失效状态,不可用状态) 如果enabled属性为NO,就是处于disable状态,代表按钮不可以被点击 对应的枚举常量:UIControlStateDisabled 

猜你喜欢

转载自blog.csdn.net/yuleidnf/article/details/51374392