【iOS开发-44】通过案例谈iOS代码重构:合并、格式化输出、宏变量、利用数组字典存储数据,以及利用plist的终极知识

首先我们今天的案例就是如下5个页面通过上一张下一张来切换:

(1)第一步,基本是以很傻很直接的方式来创建,这里用到的主要点有:

——把对象变量设置为全局变量使得可以在其他方法中调用来设置它们的属性

——设置了一个全局变量index,默认是0,然后通过增加减少这个index值并结合switch来调用不同的数据。

——利用先调用一次change方法初始化页面,使得页面定格在第一帧。

——利用按钮的enabled属性来设置按钮是否可以被点击,然后结合index的值分别在第1张和第5张时分别把上一张和下一张按钮设置为灰色不可点击。当然初始化页面的时候也要判断一下把上一张按钮设置为灰色。

——这里的change方法和btnCheck方法都是代码重构的产物,因为这两个方法都需要在preOne和nextOne方法中被调用,所以为了避免重复代码,所以把这些重复的部分都封装成了方法。

#import "ViewController.h"
//把一些对象定义成全局变量,这样可以多个方法中调用
@interface ViewController (){
    UIButton *btnPre;
    UIButton *btnNext;
    UILabel *numLabel;
    UILabel *descLabel;
    UIImageView *imgView1;
    int index1;
}

@end

@implementation ViewController

- (void)viewDidLoad {

    btnPre=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    btnPre.frame=CGRectMake(50, 150, 60, 30);
    [btnPre setTitle:@"上一张" forState:UIControlStateNormal];
    [btnPre addTarget:self action:@selector(preOne) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnPre];
    btnNext=[UIButton buttonWithType:UIButtonTypeRoundedRect];
    btnNext.frame=CGRectMake(250, 150, 60, 30);
    [btnNext setTitle:@"下一张" forState:UIControlStateNormal];
    [btnNext addTarget:self action:@selector(nextOne) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btnNext];

    numLabel=[[UILabel alloc]init];
    numLabel.frame=CGRectMake(170, 50, 100, 30);
    [self.view addSubview:numLabel];

    descLabel=[[UILabel alloc]init];
    descLabel.frame=CGRectMake(110, 300, 200, 30);
    [self.view addSubview:descLabel];

    imgView1=[[UIImageView alloc]init];
    imgView1.frame=CGRectMake(130, 100, 100, 100);
    [self.view addSubview:imgView1];

    //因为一开始的index为0,所以我们直接调用change方法,相当于把第一帧的页面调出来初始化页面
    [self change];

    btnPre.enabled=(index1!=0);

    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

-(void)preOne{
    index1--;
    [self btnCheck];
    [self change];
}

-(void)nextOne{
    index1++;
    [self btnCheck];
    [self change];
}

//按钮的enabled属性,如果是NO,则变成灰色。其实以下语句是三目运算推到而来
-(void)btnCheck{
    btnPre.enabled=(index1!=0);
    btnNext.enabled=(index1!=4);
}

-(void)change{
    switch (index1) {
        case 0:
            [email protected]"1/5";
            [email protected]"This is the letter A";
            imgView1.image=[UIImage imageNamed:@"a.png"];
            break;
        case 1:
            [email protected]"2/5";
            [email protected]"This is the letter B";
            imgView1.image=[UIImage imageNamed:@"b.png"];
            break;
        case 2:
            [email protected]"3/5";
            [email protected]"This is the letter C";
            imgView1.image=[UIImage imageNamed:@"c.png"];
            break;
        case 3:
            [email protected]"4/5";
            [email protected]"This is the letter D";
            imgView1.image=[UIImage imageNamed:@"d.png"];
            break;
        case 4:
            [email protected]"5/5";
            [email protected]"This is the letter E";
            imgView1.image=[UIImage imageNamed:@"e.png"];
            break;
        default:
            break;
    }
}

@end

(2)对switch部分进行改造:利用格式化输出重构代码:用第二行代码代替注释掉的那5行代码。

-(void)change{
    numLabel.text=[NSString stringWithFormat:@"%d/%d",index1+1,5];
    switch (index1) {
        case 0:
            //[email protected]"1/5";
            [email protected]"This is the letter A";
            imgView1.image=[UIImage imageNamed:@"a.png"];
            break;
        case 1:
            //[email protected]"2/5";
            [email protected]"This is the letter B";
            imgView1.image=[UIImage imageNamed:@"b.png"];
            break;
        case 2:
            //[email protected]"3/5";
            [email protected]"This is the letter C";
            imgView1.image=[UIImage imageNamed:@"c.png"];
            break;
        case 3:
            //[email protected]"4/5";
            [email protected]"This is the letter D";
            imgView1.image=[UIImage imageNamed:@"d.png"];
            break;
        case 4:
            //[email protected]"5/5";
            [email protected]"This is the letter E";
            imgView1.image=[UIImage imageNamed:@"e.png"];
            break;
        default:
            break;
    }
}

(3)利用字典和数组把数据单独出来,并实现数据的删减和代码之间的独立,即增减数据后,不需要修改我们显示“总页数”等这些代码,但增减数据仍需要通过增减代码来实现。(在开头定义一个全局变量NSArray *arr1)

- (void)viewDidLoad {
    ……
    NSMutableDictionary *dic1=[NSMutableDictionary dictionary];
    dic1[@"icon"][email protected]"a.png";
    dic1[@"desc"][email protected]"This is the letter A";

    NSMutableDictionary *dic2=[NSMutableDictionary dictionary];
    dic2[@"icon"][email protected]"b.png";
    dic2[@"desc"][email protected]"This is the letter B";

    NSMutableDictionary *dic3=[NSMutableDictionary dictionary];
    dic3[@"icon"][email protected]"c.png";
    dic3[@"desc"][email protected]"This is the letter C";

    NSMutableDictionary *dic4=[NSMutableDictionary dictionary];
    dic4[@"icon"][email protected]"d.png";
    dic4[@"desc"][email protected]"This is the letter D";

    NSMutableDictionary *dic5=[NSMutableDictionary dictionary];
    dic5[@"icon"][email protected]"e.png";
    dic5[@"desc"][email protected]"This is the letter E";

    arr1=[NSArray arrayWithObjects:dic1,dic2,dic3,dic4,dic5, nil];
    //以上代码需要添加在self change上,否则这个初始化是没有数据可以初始化的
    [self change];

    btnPre.enabled=(index1!=0);

    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

初始化了数据之后,其他地方都可以进行简化了,比如计算页数,比如取数据,就可以利用字典和数组来取数据:

-(void)btnCheck{
    btnPre.enabled=(index1!=0);
    btnNext.enabled=(index1!=arr1.count-1);//计算页数的相关代码
}
-(void)change{
    numLabel.text=[NSString stringWithFormat:@"%d/%d",index1+1,5];
    //取出对应的数据
    NSDictionary *dic=arr1[index1];
    //设置icon图片
    imgView1.image=[UIImage imageNamed:dic[@"icon"]];
    //设置描述文字
    descLabel.text=dic[@"desc"];
}

以上一步的好处在于我们增减数据的时候只需要在增减新的dic6等等,然后把dic6之类的添加到数组arr1中即可。其余地方不需要手动修改数字,因为我们引用数字的地方是用了数组的count属性,引用数据的地方使用了数组和字典的相关属性,不是写死的,而是活的。

(4)利用宏变量避免代码出现错误的可能性,尤其是在多人协作开发时,利用宏变量的提示可减少误输入的可能。

#define kICONKEY @"icon"
#define kDESCRIP @"desc"

所以其他响应的地方都应该替换成宏变量。

(5)利用property创建变量,注意,虽然老师说建议控件对象用weak,而一般对象用strong,但是发现,使用weak根本无法实例化对象,所以此处暂时还是用strong,等查明原因再说。响应的下面的变量都可以用_****代替或者用self.***代替。建议此处用后者。

@interface ViewController (){
//    UIButton *btnPre;
//    UIButton *btnNext;
//    UILabel *numLabel;
//    UILabel *descLabel;
//    UIImageView *imgView1;
//    int index1;
}
@property(nonatomic,retain) UIButton *btnPre;
@property(nonatomic,retain) UIButton *btnNext;
@property(nonatomic,retain) UILabel *numLabel;
@property(nonatomic,retain) UILabel *descLabel;
@property(nonatomic,strong) UIImageView *imgView1;
@property(nonatomic,strong) NSArray *arr1;
@property(nonatomic,assign) int index1;

@end

(6)延迟加载,懒加载。只有需要的时候才初始化加载数据。也就是说,我们可以把数据型的属性的初始化放在这个数据的getter方法中,且做一个判断是否要重新加载。

我们默认的arr1的getter方法是:

-(NSArray *)arr1{
    return _arr1;
}

修改为如下,即如果这个数组加载过数据,则不用重复加载,而且是用self.arr1调用到它的时候才加载,这就是延迟加载:

-(NSArray *)arr1{
    if (_arr1==nil) {//此处用_arr1而不用self.arr1是避免死循环,因为self.arr1也是调用这个函数,会一直循环调用自身
        NSMutableDictionary *dic1=[NSMutableDictionary dictionary];
        dic1[kICONKEY][email protected]"a.png";
        dic1[kDESCRIP][email protected]"This is the letter A";

        NSMutableDictionary *dic2=[NSMutableDictionary dictionary];
        dic2[kICONKEY][email protected]"b.png";
        dic2[kDESCRIP][email protected]"This is the letter B";

        NSMutableDictionary *dic3=[NSMutableDictionary dictionary];
        dic3[kICONKEY][email protected]"c.png";
        dic3[kDESCRIP][email protected]"This is the letter C";

        NSMutableDictionary *dic4=[NSMutableDictionary dictionary];
        dic4[kICONKEY][email protected]"d.png";
        dic4[kDESCRIP][email protected]"This is the letter D";

        NSMutableDictionary *dic5=[NSMutableDictionary dictionary];
        dic5[kICONKEY][email protected]"e.png";
        dic5[kDESCRIP][email protected]"This is the letter E";

        _arr1=[NSArray arrayWithObjects:dic1,dic2,dic3,dic4,dic5, nil];
    }

    return _arr1;
}

(7)再进一步:把数据独立存放在plist中,以后增减数据只是修改plist文件,而不需要在代码中增减数据。

先创建plist文件:

然后,在代码中引用,注释掉的那些数据都已经存放在plist文件中了,用最下面的几行来使用plist文件即可,以后有增减数据,只要改动plist文件,不需要改动代码:

-(NSArray *)arr1{
    if (_arr1==nil) {
//        NSMutableDictionary *dic1=[NSMutableDictionary dictionary];
//        dic1[kICONKEY][email protected]"a.png";
//        dic1[kDESCRIP][email protected]"This is the letter A";
//
//        NSMutableDictionary *dic2=[NSMutableDictionary dictionary];
//        dic2[kICONKEY][email protected]"b.png";
//        dic2[kDESCRIP][email protected]"This is the letter B";
//
//        NSMutableDictionary *dic3=[NSMutableDictionary dictionary];
//        dic3[kICONKEY][email protected]"c.png";
//        dic3[kDESCRIP][email protected]"This is the letter C";
//
//        NSMutableDictionary *dic4=[NSMutableDictionary dictionary];
//        dic4[kICONKEY][email protected]"d.png";
//        dic4[kDESCRIP][email protected]"This is the letter D";
//
//        NSMutableDictionary *dic5=[NSMutableDictionary dictionary];
//        dic5[kICONKEY][email protected]"e.png";
//        dic5[kDESCRIP][email protected]"This is the letter E";

//        _arr1=[NSArray arrayWithObjects:dic1,dic2,dic3,dic4,dic5, nil];

        //取得mainBundle,即程序主文件夹
        NSBundle *path=[NSBundle mainBundle];
        //用取得的mainBundle来查找文件,返回路径
        NSString *pathFile=[path pathForResource:@"imgdata" ofType:@"plist"];
        _arr1=[NSArray arrayWithContentsOfFile:pathFile];
    }

    return _arr1;
}

(8)补充:怎么查看这个mainBundle资源库?

NSBundle* path=[NSBundle mainBundle]就是拿到这个资源库的路径,返回的是NSBundle对象,其实是一个路径,可以通过这个来访问资源库里面的所有资源。其实它具体放在哪里?无须上网查找,直接用NSLog(@"%@",path);把这个路径打印出来不就ok了嘛。

NSBundle </Users/Andy/Library/Developer/CoreSimulator/Devices/64EDA842-5B0C-448D-BF2B-B063D09B60CB/data/Containers/Bundle/Application/E0E6FE95-99D1-4F70-84CD-D73059EA71DF/hello.app> 

顺着上面这个路径就找到了这个hello.app包>>>右击显示包内容。大功告成。

(9)图片的大小不一样,如何是好?

一般我们会给UIImageView设定好固定的宽高,但是图片如果有大有小怎么办?需要用到调用“内容模式”contentMode这个属性,即调整UIImageView里面内容怎么缩放摆放的。一般默认的是拉伸图片直至填满整个UIViewView。这样通常会改变图片的宽高比,使得图片变形。我们一般常用的时,在保持图片宽高比的情况下,尽可能的填充这个UIImageView即可,这个属性以及值就是(以上代码为什么没有添加,因为我们做得时候就设定了图片都是100*100,UIImageView也是100*100,所以不需要用到这个属性):

self.imgView1.contentMode=UIViewContentModeScaleAspectFit;

(10)文字太多,自动换行怎么设置?

以描述文字的descLabel为例,我们本案例中文字并不是很多,且给了这个descLabel宽度200,足够用,所有只有一行。如果我们把宽度设置为100,就发现显示不下,最后有个...表示省略内容。设置多行,有个numberOfLine属性,你可以设置具体的2,3,4等行数数字,也可以直接用0,表示无所谓多少行。

需要注意的时,设置多行的时候,你的descLabel高度要足够,不然依然是显示...省略号。我们原先是30的高度,此处改成了60。

    self.descLabel.frame=CGRectMake(110, 300, 100, 60);
    self.descLabel.numberOfLines=0;

总结:要自己动手。虽然都明白其中原理,但是真正操作起来,会遇到一些很小但是很重要的问题,一个一个的解决,这样的积累估计就是新手和老鸟的区别。

时间: 2024-10-13 14:46:47

【iOS开发-44】通过案例谈iOS代码重构:合并、格式化输出、宏变量、利用数组字典存储数据,以及利用plist的终极知识的相关文章

iOS开发中经常用的实用代码合集

iOS开发中经常用的实用代码合集 本文整理了,在iOS开发中我们所遇到一些开发问题的技巧类的代码,让你在开发过程中避免了很多弯路,希望能给你的开发带来帮助和启发. 1.判断邮箱格式是否正确的代码: // 利用正则表达式验证 -( BOOL )isValidateEmail:( NSString  *)email { NSString  *emailRegex =  @"[A-Z0-9a-z._%+-][email protected][A-Za-z0-9.-]+\\.[A-Za-z]{2,4}&

iOS开发中一些有用的小代码

1.判断邮箱格式是否正确的代码: //利用正则表达式验证 -(BOOL)isValidateEmail:(NSString *)email { NSString *emailRegex = @"[A-Z0-9a-z._%+-][email protected][A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"; NSPredicate *emailTest = [NSPredicate predicateWithFormat:@"SELF MATCHES%@&qu

【iOS开发-56】案例BUG:按钮的enabled、控件的userInteractionEnabled以及两种提示框UIAlert和UIActionSheet

接上述案例找BUG:[iOS开发-51]案例学习:动画新写法.删除子视图.视图顺序.延迟方法.按钮多功能用法及icon图标和启动页设置 (1)BUG:答案满了就不能再点击option按钮,答案没满就能点. 在optionClick方法的if(full)中设置,即判断答案是否满了,如果满了,则: if (full) { //如果答案满了,不管是否正确,只要满了,下面的option按钮就不能被点击 for (UIButton *optionBtn in self.optionView.subview

【iOS开发-56】案例BUG:button的enabled、控件的userInteractionEnabled以及两种提示框UIAlert和UIActionSheet

接上述案例找BUG:[iOS开发-51]案例学习:动画新写法.删除子视图.视图顺序.延迟方法.button多功能使用方法及icon图标和启动页设置 (1)BUG:答案满了就不能再点击optionbutton,答案没满就能点. 在optionClick方法的if(full)中设置,即推断答案是否满了,假设满了.则: if (full) { //假设答案满了,无论是否正确,仅仅要满了,以下的optionbutton就不能被点击 for (UIButton *optionBtn in self.opt

iOS开发项目架构浅谈:MVC与MVVM

MVC MVC,Model-View-Controller,我们从这个古老而经典的设计模式入手.采用 MVC 这个架构的最大的优点在于其概念简单,易于理解,几乎任何一个程序员都会有所了解,几乎每一所计算机院校都教过相关的知识.而在 iOS 客户端开发中,MVC 作为官方推荐的主流架构,不但 SDK 已经为我们实现好了 UIView.UIViewController 等相关的组件,更是有大量的文档和范例供我们参考学习,可以说是一种非常通用而成熟的架构设计.但 MVC 也有他的坏处.由于 MVC 的

IOS开发系列--Objective-C之协议、代码块、分类

概述 ObjC的语法主要基于smalltalk进行设计的,除了提供常规的面向对象特性外,还增加了很多其他特性,这一节将重点介绍ObjC中一些常用的语法特性.当然这些内容虽然和其他高级语言命名不一样,但是我们都可以在其中找到他们的影子,在文章中我也会对比其他语言进行介绍,这一节的重点内容如下: 协议protocol 代码块block 分类category 协议protocol 在ObjC中使用@protocol定义一组方法规范,实现此协议的类必须实现对应的方法.熟悉面向对象的童鞋都知道接口本身是对

从零开始学ios开发(六):IOS控件(3),Segmented Control、Switch

这次的学习还是基于上一个项目继续进行(你也可以新建一个项目)学习Segmented Control和Switch. Segmented Control Switch Segmented Control和Switch的主要区别在于Segmented Control可以有多个值进行选择,而Switch只有2个值. 1)添加Segmented Control从object library中拖一个Segmented Control到iphone界面上然后调整Segmented Control位置以及它的

从零开始学ios开发(五):IOS控件(2),Slider

下面继续学习ios的其他控件,这次会使用到的控件有Slider,当然还有一些之前已经使用过的控件Label. 这次我们不新建一个project了,当然如果你愿意重新创建一个新的项目也完全可以,我们还是使用上一篇的项目Control Fun. 上一篇中,我们最后的成果如下图所示我们添加了一个ImageView,2个Label和2个TextField,现在我们继续在此基础上添加其他的控件. 1)添加Slider和LabelSlider类似于一个滑块,左右(或者上下)滑动来改变数值,在object l

从零开始学ios开发(四):IOS控件(1),Image View、Text Field、Keyboard

长话短说,谢谢大家的关注,这篇写了好长时间,下面继续学习ios.我将用2到3篇的篇幅来学习iphone上的一些常用控件,包括Image View.Text Field.Keyboard.Slider等等,这篇的内容包括ImageView和Keyboard的使用.完成后的效果图如下: 1)创建一个新的project,选择“Single View Application”,命名为“Control Fun”,然后保存.一些和前几章相似的步骤在从这篇起就开始一笔待过了,也不再做截图了,例如这里的创建一个