026-代码创建控件-iOS笔记

/ 0

学习目标

1.【理解】代码创建控件过程

2.【理解】代码实现QQ登陆界面

3.【理解】图片浏览器

4.【理解】汤姆猫小游戏

一、代码创建控件过程

所有控件都是类的对象,不同的类创建可以不同类型的控件。也是就说创建一个控件其实就是创建一个对应类的对象。

常用控件类型

UIButton:按钮,界面上可点击的大都是按钮

UILabel:标签,界面上只显示文字不能点击大都是标签

UITextField:文本框,界面上可输入数据的文本框

UIImageView:图片框,界面上不可点击的图片大都是图片框

使用代码创建控件的过程

//创建对象(控件),比如创建一个按钮(UIButton)
UIButton *btn = [[UIButton alloc] init];

//设置这个对象(控件)的frame属性,也是控件的坐标、大小。必须给控件设置frame才能看得到
btn.frame = CGRect(50,50,100,30);//坐标为50,50 宽、高为100、30

//设置对象(控件)的一些属性
//默认、高亮状态下的按钮文字
[btn setTitle:@"登陆" forState:UIControlStateNormal];
[btn setTitle:@"点击了登陆" forState:UIControlStateHighlighted];
//默认、高亮状态下的按钮背景颜色
[btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[btn setTitleColor:[UIColor greenColor] forState:UIControlStateHighlighted];
//默认、高亮状态下的按钮背景图
[btnLogin setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
[btnLogin setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateHighlighted];

//最后将创建好的对象(控件)添加到他的父容器(父视图)中
[self.view addSubview:btn];//一定要将创建好的对象添加到父容器,否则无法显示

注意

一定不要使用titleLabel去设置文本内容和颜色,因为按钮是有状态的。而状态的文本值是title里面取。所以如果为titleLabel设置文本,那和以系统不知道这个文本是那一种状态下显示的文本,干脆不显示。

二、代码实现QQ登陆界面

我们通过一个实例来对常用控件的基本属性、方法做个了解,和昨晚写的案例一样都是QQ登陆界面,不过这次使用纯代码的方式来实现。最终效果如下图:

editcode01

首先还是先创建一个Single View Application,设置界面尺寸,导入案例需要用到的素材,本次素材就两个按钮背景图,自行网上搜索素材。搞好相关设置然后就可以开始编写代码了。

具体实现代码如下

#import "ViewController.h"

@interface ViewController ()

//QQ、密码文本框(UITextField)控件
@property (strong, nonatomic) UITextField *txtQQ;
@property (strong, nonatomic) UITextField *txtPassword;
@end

@implementation ViewController

//控制器加载后会立即调此方法
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //添加QQ、密码的Label
    [self addQQLabel];
    [self addPasswordLabel];
    
    //添加QQ、密码的文本框
    [self addQQTextField];
    [self addPasswordTextField];
    
    //添加登陆按钮
    [self addLoginButton];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

//创建QQ标签(UILabel)的方法
- (void)addQQLabel {
    //创建控件对象
    UILabel *lblQQ = [[UILabel alloc] init];
    
    //设置控件的frame属性
    lblQQ.frame = CGRectMake(50, 50, 50, 21);
    
    //设置标签文本文字、字体、文字对齐方式
    lblQQ.text = @"QQ";
    lblQQ.font = [UIFont fontWithName:@"Arial" size:14];
    lblQQ.textAlignment = NSTextAlignmentLeft;
    
    //将创建好的对象添加到当前控制器
    [self.view addSubview:lblQQ];
}

//创建密码标签(UILabel)的方法
- (void)addPasswordLabel {
    //创建控件对象
    UILabel *lblPassword = [[UILabel alloc] init];
    
    //设置控件的frame属性
    lblPassword.frame = CGRectMake(50, 90, 50, 21);
    
    //设置标签文本文字、字体、文字对齐方式
    lblPassword.text = @"密码";
    lblPassword.font = [UIFont fontWithName:@"Arial" size:14];
    lblPassword.textAlignment = NSTextAlignmentLeft;
    
    //将创建好的对象添加到当前控制器
    [self.view addSubview:lblPassword];
}

//创建QQ文本框(UITextField)的方法
- (void)addQQTextField {
    //创建控件对象,因为文本框中的数据需要在其他地方访问,所以就设置为属性
    self.txtQQ = [[UITextField alloc] init];
    
    //设置控件的frame属性
    self.txtQQ.frame = CGRectMake(100, 45, 250, 30);
    
    //设置文本框边框样式、默认文本框内文字、清除文本内容按钮
    self.txtQQ.borderStyle = UITextBorderStyleRoundedRect;
    self.txtQQ.placeholder = @"请输入QQ";
    self.txtQQ.clearButtonMode = UITextFieldViewModeWhileEditing;
    
    //设置键盘类型
    self.txtQQ.keyboardType = UIKeyboardTypeNumberPad;
    
    //将创建好的对象添加到当前控制器
    [self.view addSubview:self.txtQQ];
}

//创建密码文本框(UITextField)的方法
- (void)addPasswordTextField {
    //创建控件对象,因为文本框中的数据需要在其他地方访问,所以就设置为属性
    self.txtPassword = [[UITextField alloc] init];
    
    //设置控件的frame属性
    self.txtPassword.frame = CGRectMake(100, 85, 250, 30);
    
    //设置文本框边框样式、默认文本框内文字、清除文本内容按钮
    self.txtPassword.borderStyle = UITextBorderStyleRoundedRect;
    self.txtPassword.placeholder = @"请输入密码";
    self.txtPassword.clearButtonMode = UITextFieldViewModeWhileEditing;
    
    //设置密码文本框的数据密文显示(密码看不到明文文字)
    [self.txtPassword setSecureTextEntry:YES];
    
    //将创建好的对象添加到当前控制器
    [self.view addSubview:self.txtPassword];
}

//创建登陆按钮(UIButton)的方法
- (void)addLoginButton {
    //按钮的UIButtonType属性是只读的,只能在创建的时候初始化,如果没有设置,默认是UIButtonTypeCustom。
    UIButton *btnLogin = [UIButton buttonWithType:UIButtonTypeCustom];
    
    //设置控件的frame属性
    btnLogin.frame = CGRectMake(170, 130, 100, 30);
    
    //分别设置按钮默认、高亮状态下的文字、文字颜色、背景图片
    [btnLogin setTitle:@"登陆" forState:UIControlStateNormal];
    [btnLogin setTitle:@"点击了登陆" forState:UIControlStateHighlighted];
    [btnLogin setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [btnLogin setTitleColor:[UIColor greenColor] forState:UIControlStateHighlighted];
    //背景图先拖到Assets.xcassets或者Images.xcassets里,注意是.png格式的图片
    [btnLogin setBackgroundImage:[UIImage imageNamed:@"buttongreen"] forState:UIControlStateNormal];
    [btnLogin setBackgroundImage:[UIImage imageNamed:@"buttongreen_highlighted"] forState:UIControlStateHighlighted];
    
    //设置按钮背景颜色,有背景图了背景颜色就可以不用设置了
    [btnLogin setBackgroundColor:[UIColor grayColor]];
    
    //为按钮添加绑定事件(单击事件),让按钮被点击后执行click方法
    /*
     第一个参数:调用谁的方法,让谁进行处理.往往就是self
     第二个参数:调用的方法
     第三个参数:事件类型
     可以让多个按钮绑定同一个事件,通过tag值进行按钮的区分
     */
    [btnLogin addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside];
    
    //将创建好的对象添加到当前控制器
    [self.view addSubview:btnLogin];
}

//登陆按钮的单击事件
- (void)click {
    //点击登录按钮后收起键盘
    [self.view endEditing:YES];
    
    NSString *msg = [NSString stringWithFormat:@"QQ:%@ 密码:%@",self.txtQQ.text,self.txtPassword.text];
    //在控制台输出用户输入的QQ号、密码
    NSLog(@"%@",msg);
}
@end

总结

要学会如何使用代码创建控件,首先得了解代码创建控件的基本过程(创建对象、设置对象的属性、添加对象到父容器)。并且还应该了解不同控件的一些必要属性和方法,这和我们前面OC中学习的创建对象、调用方法是没有任何区别的。

三、图片浏览器

这个案例的主要目的是熟悉UIButtonUIImageView的使用,通过这个案例要全面了按钮的常用属性的设置,添加的业务逻辑也会提升我们对代码的熟练程度。同时加强封装的思想,初步使用外部的plist文件,这在之后plist文件会频繁的使用。

UIButton:当点击图片后能够有相应操作,或者点击后有高亮效果就使用UIButton。

UIImageView:当仅仅是静态展示图片,不需要对图片进行相应的操作就使用UIImageView。

下图就是我们案例完成后的样子,点击左右按钮能够切换图片、图片对应标题和索引,第一张和最有一张有按钮禁用。

picsapp01

创建好项目导入需要用到的图片素材,再次强调Assets.xcassets中只能放png格式的图片,在使用时省略扩展名。

QQ20150806-1

拖拽两个Label控件、两个Button控件、一个Image View控件,并调整到尺寸、按钮默认、高亮背景图等等。

QQ20150806-2

通过控件连线,定义好对应属性、方法,还有数组和图片索引用于操作我们的图片资源。

QQ20150806-3

新建一个Property List(.plist)文件,用于封装数据。

QQ20150806-4

然后选择创建好的img.plist文件,创建如下图所示的plist文件。一个数组里有5个字典,每个字典有两个键值对,img、title是键,分别对应图片名称、图片文字描述内容。

QQ20150806-5

图片浏览器代码实现

#import "ViewController.h"

@interface ViewController ()
//存储图片资源的数组
@property (strong, nonatomic) NSArray *arrayImgs;
//图片
@property (weak, nonatomic) IBOutlet UIImageView *img;
//图片顶部标签
@property (weak, nonatomic) IBOutlet UILabel *topTitle;
//图片底部标签
@property (weak, nonatomic) IBOutlet UILabel *downTitle;

//上下页按钮
@property (weak, nonatomic) IBOutlet UIButton *preBtn;
@property (weak, nonatomic) IBOutlet UIButton *nextBtn;

//图片索引
@property (assign, nonatomic) int index;

//上一页按钮
- (IBAction)pre;
//下一页按钮
- (IBAction)next;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化让下标等于-1
    self.index = -1;
    //然后调用next方法,下标增加1。并显示下标为0的图片
    [self next];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

//懒加载
- (NSArray *)loadImgs {
    if (_arrayImgs == nil) {
        //如果_arrayImgs为nil则加载img.plist文件中的数组赋值给属性
        //plist文件是物理文件,所以需要获取文件目录,通过目录+文件名进行数据读取
        _arrayImgs = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"img.plist" ofType:nil]];
        /*
         [NSBundle mainBundle] 返回app安装后的根目录
         arrayWithContentsOfFile:pathForResource:ofType:方法读取plist文件,返回一个数组
         */
    }
    return _arrayImgs;
}

//上一页按钮
- (IBAction)pre {
    //数组下标自减,并调用方法显示图片等信息
    self.index--;
    [self showImg];
}

//下一页按钮
- (IBAction)next {
    //数组下标自增,并调用方法显示图片等信息
    self.index++;
    [self showImg];
}

//显示图片
- (void)showImg {
    //调用loadImgs方法初始化arrayImgs,并取出对应下标的字典
    NSDictionary *dict = self.loadImgs[self.index];
    
    //设置图片
    self.img.image = [UIImage imageNamed:dict[@"img"]];
    
    //设置图片顶部标题
    self.topTitle.text = [NSString stringWithFormat:@"%d/%lu",self.index + 1,self.arrayImgs.count];
    
    //设置图片底部标题
    self.downTitle.text = dict[@"title"];
    
    //判断按钮是否可点击
    //如果下标不为0,则返回YES给上一页按钮的enabled属性,否则反之
    self.preBtn.enabled = self.index != 0;
    //如果下标不等于数组最大下标,则返回YES给下一页按钮的enabled属性,否则反之
    self.nextBtn.enabled = self.index != self.arrayImgs.count - 1;
    
}
@end

使用plist文件的好处

1.数据被封装,不再暴露在外面。

2.如果想修改或者添加删除数据,不需要修改源代码,只需要修改外部的plist文件。

3.plist文件的本质就是XML文件,所以后期可以通过对XML文件的读写操作来完成一些数据持久化操作。

创建plist文件的注意事项

1.一个字典中可以有多个key—value,多个字典包含在一个数组中。

2.plist文件名称最好不要包含info,否则会认为是系统的plist文件。

3.plist文件是物理文件,所以需要获取文件目录,通过目录+文件名进行数据读取。

获取plist文件所在的文件全路径

NSString *path = [[NSBundle mainBundle] pathForResource:@"img.plist" ofType:nil];

读取plist中的数据存储到数组中

self.arrayImgs = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"img.plist" ofType:nil]];

四、帧动画之汤姆猫

这个案例的主要目的是了解什么是帧动画,同时掌握UIImageView的使用方式,为UIImageView控件添加动画效果和一般动画效果的添加及相关的参数设置。

png图片:一般存放在Images.xcassets中,同时在使用图片的时候不需要添加扩展名。

JPG图片:一般图片存储在Supporting Files下面,在使用jpg图片的时候一般添加扩展名。

创建项目并拖拽好各种控件,这里是先拖一个UIImageView控件占满整个屏幕,然后再为Image属性设置一张默认图片。然后拖拽6个图片按钮、3个没有任何样式的按钮(头部、双脚),并设置相关属性。

QQ20150806-6

将Button用到的png格式图片拖拽到Images.xcassets,其他图片拖拽到Supporting Files,这些文件夹里包含了大量图片。

QQ20150806-7

实现代码如下

#import "ViewController.h"

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageViewCat;

- (IBAction)drink;//喝牛奶
- (IBAction)fart;//放屁
- (IBAction)eat;//吃鸟
- (IBAction)scratch;//爪子挖屏幕
- (IBAction)pie;//扔馅饼
- (IBAction)cymbal;//敲锣
- (IBAction)knockout;//摸头
- (IBAction)footRight;//摸右脚
- (IBAction)footLeft;//摸左脚

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

//每一个按钮所触发的动画效果类似,只有图片名称及数量不一样,所以将其封装为方法
- (void)showImageDataswithCount:(int)count andImageName:(NSString *)imageName {
    //判断当前是否正在运行另外一个动画
    if (self.imageViewCat.isAnimating) {
        return;
    }
    /*
     将图片资源存储到数组中
     UIImageView动画效果需要为其animationImages属性指定包含图片路径或者文件名的数组
     所以我们首先需要获取到图片资源数组
     */
    NSMutableArray *arrayM = [[NSMutableArray alloc] init];
    for (int i = 0; i < count; i++) {
        //拼接每一张图片名称
        NSString *imgName = [NSString stringWithFormat:@"%@_%02d.jpg",imageName,i];
        
        /*
         使用包含图片名称的数组做为动画的数据源会将图片资源生成缓存,占用系统内存,这样不合理.
         应该使用图片的全路径数组,这样不会生成缓存,同时还应该在一个动画播放完毕后将其及时释放
         */
        //UIImage *img = [UIImage imageNamed:imgName];
        
        //图片资源放在Supporting Files
        NSString *path = [[NSBundle mainBundle] pathForResource:imgName ofType:nil];
        
        //这种方式加载的图片不会有缓存,每次用完就清空
        UIImage *img = [UIImage imageWithContentsOfFile:path];
        
        //存储到可变数组中
        [arrayM addObject:img];
    }
    //将图片资源数组赋值给animationImages属性
    self.imageViewCat.animationImages = arrayM;
    
    //设置UIImageView的一些相关的动画属性
    //设置动画时间
    self.imageViewCat.animationDuration = arrayM.count * 0.1;
    //动画循环次数
    self.imageViewCat.animationRepeatCount = 1;
    //动画开始播放
    [self.imageViewCat startAnimating];
    
    //动画播放完销毁,延迟执行代码
    [self.imageViewCat performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:arrayM.count * 0.1];
    
}
//吃鸟
- (IBAction)eat {
    [self showImageDataswithCount:40 andImageName:@"eat"];
}

//喝牛奶
- (IBAction)drink {
    [self showImageDataswithCount:81 andImageName:@"drink"];
}

//放屁
- (IBAction)fart {
    [self showImageDataswithCount:28 andImageName:@"fart"];
}

//爪子挖屏幕
- (IBAction)scratch {
    [self showImageDataswithCount:56 andImageName:@"scratch"];
}

//扔馅饼
- (IBAction)pie {
    [self showImageDataswithCount:24 andImageName:@"pie"];
}

//敲锣
- (IBAction)cymbal {
    [self showImageDataswithCount:13 andImageName:@"cymbal"];
}

//摸头
- (IBAction)knockout {
    [self showImageDataswithCount:81 andImageName:@"knockout"];
}

//摸右脚
- (IBAction)footRight {
    [self showImageDataswithCount:30 andImageName:@"footRight"];
}

//摸左脚
- (IBAction)footLeft {
    [self showImageDataswithCount:30 andImageName:@"footLeft"];
}
@end

最终效果如下图,由于动画图片太大就么有整动态图:

QQ20150806-8

常见错误

1. jpg文件没有添加扩展名。

2. 读取全路径的jpg素材文件的时候却没有将jpg没有添加到 Supporting Files造成无法获取到。

3. 清空资源的时候没有关注self是实际含义.

注意事项

1. 使用图片文件全路径而不是使用图片名称。

2. 动画播放完毕之后及时将当前图片资源释放。

3. 拼接生成正确的文件名。

4. 在制作动画的时候,如果想设置其它属性注意要先设置好其它属性再开始动画,否则可能动画没有效果。