一起养成写作习惯!这是我参与「日新方案 4 月更文挑战」的第10天,点击查看活动概况。

前言

案例:建立九宫格

  1. 确认开发思路
  2. 建立界面、编写代码
  3. 九宫格的布局
  4. 字典装模型(模型数据的处理,plist文件的加载)->完成按钮的监听办法
  5. 运用类办法加载xib,简化代码建立界面
  6. 自界说视图,运用数据模型安装视图内容
    若一个view的内部子控件比较多,一般会考虑自界说一个view 把内部的子控件创立屏蔽起来,不让外界关怀

字典转模型


/** 一般完成字典实例化模型,都完成了以下模型的实例化办法*/
//运用字典实例化模型
- (instancetype) initWithDictionary :(NSDictionary *) appDictionary;
//类办法能够快速实例化一个目标--把代码放在它最应该呆的当地
+ (instancetype) appInfoWithDictionary : (NSDictionary *) appDictionary;
//回来plist文件对应的模型数组 ,运用懒加载

KVC的赋值

- (instancetype) initWithDictionary:(NSDictionary *)appDictionary{
    //self is object
    self = [super init];
    if (self) {//已然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较,由于YES被界说为1
        //init local vars 将plist文件的信息在此处进行字典转模型
        //KVC (key value coding) 键值编码:是一种直接修正、读取目标特点的一种办法;KVC被称为cocoa的大招
        [self setValuesForKeysWithDictionary:appDictionary];//本质上是调用        self setValue:<#(nullable id)#> forUndefinedKey:(nonnull NSString *)
    }
    return self;
}

原文

kunnan.blog.csdn.net/article/det…

I、 基础知识

1.1 深仿制&浅仿制


深仿制即内容仿制

即源目标和副本指向的是不同的两个目标;
源目标的引证计数器不变,副本的引证计算器为1;

指针仿制(浅仿制)

源目标和副本指向同一个目标;
目标的引证计算器+1,相当于做了一次retain操作

1.2 代码重构(条件是现已完成了基本功能)

开发前:设定开发方案、进程
开发进程中:每一个进程告一段落之后,我们要暂停,进行代码审阅,有针对性的重构(抽离重复代码,模型和视图各尽职责)

代码重构的原则: 把代码放在它最应该呆的当地

1、运用类办法完成字典实例化模型 (模型,一般是plist文件,网络)

运用类办法实例化模型数组

//类办法能够快速实例化一个目标--把代码放在它最应该呆的当地
+ (instancetype) appInfoWithDictionary : (NSDictionary *) appDictionary;
//回来plist文件对应的模型数组 ,运用懒加载
+ (NSArray *)appList;

2、运用类办法实例化视图目标,并用数据模型安装视图内容

用类办法进行视图的实例化

+ (instancetype) appView;//运用类办法加载xib
+ (instancetype) appViewWithAppInfo:(KNAppInfo *) appInfo;//运用类办法加载xib,参数用于视图的数据安装

1.3 九宫格计算办法

1)每一列的x值相同,x值由列号决议
2)每一行的y值相同,y值由行号决议

        //建立界面,九宫格(以View为单元,内含UILabel、UIButton、UIImageView,同行和同列的方位关系 center.x = x+ width*0.5)
    //view 的封装,带有数据模型的结构器以便进行内部控件的数据安装 ;数据模型(plist-》字典-》模型)--自界说的Plist 一般放置于Supporting Files目录中
#define kAppViewWidth 80 //视图宽度
#define KAppViewHeight  90 //视图高度
#define kColumnCount 4 //每行的视图个数--总列数
#define kRowCount 5 // 每一列的视图个数--总行数
    //水平距离
    CGFloat marginX =(CGRectGetWidth(self.view.frame)- kColumnCount * kAppViewWidth)/(kColumnCount+1);
    //笔直距离
    CGFloat marginY = (CGRectGetHeight(self.view.frame)- kRowCount* KAppViewHeight)/(kRowCount+1);
    for (int i=0; i<28; i++) {
        //行号
        int  row = i/kColumnCount;
        //列号
        int column =i%kColumnCount;
        //方位
        CGFloat x=marginX+(marginX+kAppViewWidth)*column;//x值决议视图所在的列;
        CGFloat y= marginY+(marginY+KAppViewHeight)*row; //y值决议视图所在的行
        //创立视图
        UIView *appView = [[UIView alloc]initWithFrame: CGRectMake(x, y, kAppViewWidth, KAppViewHeight)];
        [appView setBackgroundColor:[UIColor redColor]];
        //将子视图增加至父视图
        [self.view addSubview:appView];
    }

1.4 内存剖析(栈、堆的存储信息)

1、只读指针特点的剖析(指向关系不行变,指向目标的内容可变)

不行变特点的值,若存储的是指针,则该特点对应的目标成员是可变的
只读指针特点的地址值不行变,意味的指针和指向的目标间的关系不行变,但被指向的目标内容是可变的

示例1:UIButton 目标有UILabel 、UIImageView 特点,都是readonly

即这两者的特点存储的指针地址是只读的,不行修正
,但只读的指针指向的目标的特点成员是能够修正的

示例2:修正UIButton的只读特点titleLabel指针对应的目标特点font(readonly表明titleLabel的指针指不行修正,但label的font能够修正)

 [[downloadButton titleLabel] setFont: [UIFont systemFontOfSize:12]];
//设置UIButton的title的font,先获取UI Button的内部UILabel:@property (nullable,nonatomic,readonly,strong) UILabel     *titleLabel NS_AVAILABLE_IOS(3_0);
        //@property(nonatomic,strong) UIFont         *font              NS_DEPRECATED_IOS(2_0, 3_0) __TVOS_PROHIBITED;过时

示例3:a basic type & a class 的声明办法

a basic type是存储在栈区
class是栈区存储着堆区的目标对应的指针

//Change this:    
 CGFloat *marginX =(self.view.bounds.size.width - kColumnCount * kAppViewWidth)/(kColumnCount+1);
//to
 CGFloat *marginX =(self.view.bounds.size.width - kColumnCount * kAppViewWidth)/(kColumnCount+1); 
//Get rid of the asterisk. CGFloat is not a class, it is a typedef for double (a basic type).

1.5 instancetype简介

OC中,在IOS7之后主推instancetype。 swift言语中,绝大数的类的实例化,也都不需求指定类型。

C++的11版别中,也有类似的关键字auto类型
可见所有的言语都致力于,使言语更简单运用

instancetype 在类型表明上跟id相同(能够表明任何目标类型)

instancetype 首要用于类办法实例化目标时,让编译器自动推进目标的实践类型,以防止运用ID,形成开发中不必要的费事

1.instancetype 只能运用于回来值类型,不能像id相同用于参数类型;但instancetype比id多的一个优点是:

IDE会查看instancetype的实在类型 ,提早正告(Incompatible pointer types initializing ‘NSString *’ with an expression of type ‘KNAppInfo *’),以防止运用ID形成开发中不必要的费事

2.Init办法应该遵从Apple生成代码模板的命名规矩,回来类型应该运用instancetype而不是id。
当类结构办法被运用时,它应该回来类型是instancetype而不是id。这样保证编译器正确地揣度结果类型,可防止运行时才报错。

特点不能运用New 最初进行命名

1.6 @synthesize的用法


@synthesize 中能够界说 与变量名不相同的getter和setter的命名,籍此来维护变量不会被不恰当的拜访(自动指定特点运用的成员变量称号)


@property (nonatomic,strong,readonly) UIImage *image;//存储字典对应的图片目标
//完成文件
#import "KNAppInfo.h"
@implementation KNAppInfo
@synthesize image =_image;//@synthesize 中能够界说 与变量名不相同的getter和setter的命名,籍此来维护变量不会被不恰当的拜访
/**
 懒加载模型的图片目标特点
 */
- (UIImage *)image{
    if (nil == _image) {//
        _image = [UIImage imageNamed:self.icon] ;
    }
    return _image;
}

1.7 @property

声明property的语法为:@property (参数1,参数2) 类型 名字;

1)参数首要分为三类:
读写特点: (readwrite/readonly
语意:(assign/retain/copy)
原子性: (atomicity/nonatomic)针对多线程问题
2)各参数意义如下:
readwrite: 发生setter\getter办法
readonly: 只发生简单的getter,没有setter。
assign: 默许类型,setter办法直接赋值,而不进行retain操作
retain: setter办法对参数进行release旧值,再retain新值。
copy: setter办法进行Copy操作,与retain相同
nonatomic: 制止多线程,变量维护,进步性能

developer.apple.com/library/mac…

II、字典转模型

  • 运用字典的害处

一般取出和修正数据字典的数据,都要经过编写“字符串类型”的key值-》修正器IDE没有智能提示、手动写key简单写错,且此时IDE不会有任何的正告和报错。
字典的运用比如

dict[@"name"] = @"Jack";
NSString *name = dict[@"name"];
  • 运用数据模型的优点

1)数据模型(专门用来存放数据的目标),运用数据模型表明数据更专业些
2)运用模型修正数据、读取数据均选用目标的特点,进步编写效率

2.1 字典转模型的完成进程

1)字典转模型的进程,一般被封装在模型内部
2)模型应该提供一个“带有NSDictionary类型”参数的结构办法

- (instancetype)initWithDict:(NSDictionary*)dict;
+ (instancetype)xxxWithDict:(NSDictionary*)dict;

2.2 字典转模型的进程

  • [选用KVC(keyValueCoding)完成] plist文件解析-》字典数组(NSDictionary)-》模型数组(AppInfo)

运用字典实例化模型 (把代码放在它最应该呆的当地)

- (instancetype) initWithDictionary:(NSDictionary *)appDictionary{
    //self is object
    self = [super init];
    if (self) {//已然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较,由于YES被界说为1
        //init local vars 将plist文件的信息在此处进行字典转模型
        //KVC (key value coding) 键值编码:是一种直接修正、读取目标特点的一种办法;KVC被称为cocoa的大招
        [self setValuesForKeysWithDictionary:appDictionary];     
    }
    return self;
}
//运用类办法完成“字典实例化模型”--地道的代码
+ (instancetype) appInfoWithDictionary:(NSDictionary *)appDictionary{
    //self is class
    return [[self alloc]initWithDictionary:appDictionary];//+ (instancetype)alloc    Description    Returns a new instance of the receiving class.   
}
@end
  • 运用KVC的注意事项:

1、plist文件中的键值称号有必要与模型目标的特点称号一致
2、模型中的特点,能够不悉数出现在plist文件中

III、 xib的运用

用于描绘“某一块局部的”UI界面

1、在开发阶段面向开发者的是xib文件,当把应用装到iPhone时,xib文件会转成nib文件
2、xib 在Xcode 4.0 之前是首要的图形界面建立东西,现在,xib仍是干流的界面开发技能;适合于开发小块的自界说视图

3.1xib 的加载办法

办法一

NSArray *objs = [[NSBundle mainBundle] loadNibNamed:@"KNAppView" owner:nil options:nil];//这个办法会创立xib中的所有目标,而且将目标按顺序放到objs数组中

办法二

UINib *nib = [UINib nibWithNibName:@"MJAppView" bundle:[NSBundle mainBundle]];//bundle参数能够为nil,默许便是main bundle
NSArray *objs = [nib instantiateWithOwner:nil options:nil];

3.2 xib与storyBoard的异同点

相同点:

都是用于描绘UI界面,运用InterfaceBuilder东西来修正

不同点:

1、xib是 轻量级的,用于描绘局部的UI界面;
2、storyBoard是重量级的,用来描绘整个应用软件的多个界面,并能展示多个界面间的跳转关系。

IV、自界说View

4.1 View的封装

经过 自界说视图来减少代码对xib视图层次的依赖性

1)若一个view的内部子控件比较多,一般会考虑自界说一个view

把内部的子控件创立屏蔽起来,不让外界关怀

2) 外界能够传入对应的模型数据给view,view拿到模型数据之后,进行“内部子控件的数据”安装

4.2 UIView的常见特点和办法

设置按钮的文字字体

//设置按钮的文字字体,需求拿到按钮内部的label
btn.titleLabel.font = [UIFont systemFontOfSize:13];//
/**
常见的字体有:UIFont代表字体,常见创立办法有以下几个:
+ (UIFont *)systemFontOfSize:(CGFloat)fontSize; //体系默许字体
+ (UIFont *)boldSystemFontOfSize:(CGFloat)fontSize;  //粗体
+ (UIFont *)italicSystemFontOfSize:(CGFloat)fontSize;  //斜体
*/

V、建立九宫格

确认开发思路->建立界面、编写代码->九宫格的布局->
字典装模型(模型数据的处理,plist文件的加载)->完成按钮的监听办法->运用类办法加载xib,简化代码建立界面->自界说视图,运用数据模型安装视图内容

5.1 建立九宫格的进程

1)清晰每一块用的是什么View;
2)清晰每一个view之间的父子关系
3)先尝试逐个增加格子,最后考虑运用for循环进行建立‘
4)加载app数据,依据数据数量来创立对应个数的格子
5)增加格子内部的子控件
6)给格子内部的子控件安装数据

5.2 代码示例

模型的完成代码

//
//  KNAppInfo.m
#import "KNAppInfo.h"
@implementation KNAppInfo
/**
 问题: 运用readonly润饰成员变量的话,将不会出产带下划线的成员变量 undeclared identifier '_image',
 解决办法如下:@synthesize 合成指令 --自动指定特点运用的成员变量称号
 */
@synthesize image =_image;//@synthesize 中能够界说 与变量名不相同的getter和setter的命名,籍此来维护变量不会被不恰当的拜访
/**
 懒加载模型的图片目标特点
 */
- (UIImage *)image{
    if (nil == _image) {//
        _image = [UIImage imageNamed:self.icon] ;
    }
    return _image;
}
//运用字典实例化模型 -- 把代码放在它最应该呆的当地
/**
 1.Init办法应该遵从Apple生成代码模板的命名规矩,回来类型应该运用instancetype而不是id。
 当类结构办法被运用时,它应该回来类型是instancetype而不是id。这样保证编译器正确地揣度结果类型。
 IDE提早报警:Incompatible pointer types initializing 'NSString *' with an expression of type 'KNAppInfo *
 --防止运行时才报错。
 2.关于instancetype的小结
 1》instancetype 首要用于类办法实例化目标时,让编译器自动推进目标的实践类型,以防止运用ID,形成开发中不必要的费事
 2》OC中,在IOS7之后主推instancetype
    swift言语中,绝大数的类的实例化,也都不需求指定类型
    C++的11版别中,也有类似的关键字auto类型--》所有的言语都致力于,使言语更简单运用
 3》instancetype 只能运用于回来值类型,不能当参数适用
 */
- (instancetype) initWithDictionary:(NSDictionary *)appDictionary{
    //self is object
    self = [super init];
    if (self) {//已然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较,由于YES被界说为1
        //init local vars 将plist文件的信息在此处进行字典转模型
        //KVC (key value coding) 键值编码:是一种直接修正、读取目标特点的一种办法;KVC被称为cocoa的大招
        [self setValuesForKeysWithDictionary:appDictionary];//本质上是调用        self setValue:<#(nullable id)#> forUndefinedKey:(nonnull NSString *)
    }
    return self;
}
//运用类办法完成“字典实例化模型”--地道的代码
+ (instancetype) appInfoWithDictionary:(NSDictionary *)appDictionary{
    //self is class
    return [[self alloc]initWithDictionary:appDictionary];//+ (instancetype)alloc    Description    Returns a new instance of the receiving class.   
}
/**
 回来plist的数据模型数组
 */
+ (NSArray *)appList{
    //解析plist,得到字典数组
    NSArray *tmpDictionaryArray= [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"app" ofType:@"plist"]];
    //可变数组,用于临时存储模型数组
    NSMutableArray *tmpAppInfoArray = [NSMutableArray array];
    for (NSDictionary *dict in tmpDictionaryArray) {
        [tmpAppInfoArray addObject:[self appInfoWithDictionary:dict]];
    }
    return tmpAppInfoArray;
}
@end

模型的头文件

//
//  KNAppInfo.h
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
//模型数据
@interface KNAppInfo : NSObject
/**
 运用KVC的注意事项:
 1》plist中的键称号必学与模型特点称号保持一致
 2》模型中的特点能够不悉数出现在plist中
 */
@property (nonatomic,copy) NSString *icon;//特点称号与plist文件的NSDictionary 的key保持一致,以便选用KVC(键值编码)
@property (nonatomic,copy) NSString *name;
/**
 @property
 1>生成getter、setter、带下划线的成员变量(纪录特点内容)
 2>运用readonly润饰成员变量的话,将不会出产带下划线的成员变量 undeclared identifier '_image'
 */
@property (nonatomic,strong,readonly) UIImage *image;//存储字典对应的图片目标
/**
 一般完成字典实例化模型,都完成了以下模型的实例化办法
*/
//运用字典实例化模型
- (instancetype) initWithDictionary :(NSDictionary *) appDictionary;
//类办法能够快速实例化一个目标--把代码放在它最应该呆的当地
+ (instancetype) appInfoWithDictionary : (NSDictionary *) appDictionary;
//回来plist文件对应的模型数组 ,运用懒加载
+ (NSArray *)appList;
@end

控制器的代码

//
#import "ViewController.h"
#import "KNAppInfo.h"
#import "KNAppView.h"
#define kAppViewWidth 80 //视图宽度
#define KAppViewHeight  90 //视图高度
#define kColumnCount 3 //每行的视图个数--总列数
#define kRowCount 4 // 每一列的视图个数--总行数
#define kStartY 1 //为了适配埋伏笔
@interface ViewController ()
@property (nonatomic,strong) NSArray *appInfoList;
@end
@implementation ViewController
//懒加载appInfoList,首要用于安装视图的数据,与视图控制器ViewController 关系不大,能够进行抽离
- (NSArray *)appInfoList{
    if (nil == _appInfoList) {
        _appInfoList = [KNAppInfo appList];//获取plist的数据模型数组
    }
    return _appInfoList;
}
- (void)viewDidLoad {
    [super viewDidLoad]; 
    //建立界面,九宫格(以View为单元,内含UILabel、UIButton、UIImageView,同行和同列的方位关系 center.x = x+ width*0.5)
    //1.view 的封装,带有数据模型的结构器以便进行内部控件的数据安装 ;数据模型(plist-》字典-》模型)
    //水平距离
    CGFloat marginX =(CGRectGetWidth(self.view.frame)- kColumnCount * kAppViewWidth)/(kColumnCount+1);
    //笔直距离
    CGFloat marginY = (CGRectGetHeight(self.view.frame)- kRowCount* KAppViewHeight)/(kRowCount+1);
    //用plist文件的数据安装xib 的控件
    for (int i=0; i<self.appInfoList.count; i++) {
        //行号
        int  row = i/kColumnCount;
        //列号
        int column =i%kColumnCount;
        //方位
        CGFloat x=marginX+(marginX+kAppViewWidth)*column;//x值决议视图所在的列;
        CGFloat y=kStartY+marginY+(marginY+KAppViewHeight)*row; //y值决议视图所在的行
        //实例化视图
        KNAppView *appView = [KNAppView appViewWithAppInfo:self.appInfoList[i]];//运用类办法,进行加载xib,获取app view的一起,对appview 进行数据安装( 运用模型安装视图  --完成视图内部的细节)
        //设置方位
        [appView setFrame:CGRectMake(x, y, kAppViewWidth, KAppViewHeight)];
        //将子视图增加至父视图
        [self.view addSubview:appView];
    }   
}
@end

视图的头文件

//
#import <UIKit/UIKit.h>
@class KNAppInfo;
@interface KNAppView : UIView
//自界说视图的现实的数据来源于模型,即运用模型安装自界说视图的显示内容
@property (nonatomic,strong) KNAppInfo *appInfo;//视图对应的模型,是视图提供给外界的接口
+ (instancetype) appView;//运用类办法加载xib
+ (instancetype) appViewWithAppInfo:(KNAppInfo *) appInfo;//运用类办法加载xib,参数用于视图的数据安装
@end

视图的完成文件

//
#import "KNAppView.h"
#import "KNAppInfo.h"
@interface KNAppView ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *appLable;
@end
@implementation KNAppView
/**
 运用类办法加载xib,并对视图进行数据安装
 */
+(instancetype)appViewWithAppInfo:(KNAppInfo *)appInfo {
    KNAppView *appview  = [self appView];//实例化视图
    [appview setAppInfo:appInfo];//运用模型安装app view
    return appview;//回来视图目标
}
/**
 运用类办法进行加载xib 视图,即实例化视图
 */
+(instancetype)appView{
    return [[[NSBundle mainBundle] loadNibNamed:@"KNAppView" owner:nil options:nil]lastObject];
}
//利用setter办法,完成视图的数据安装
- (void)setAppInfo:(KNAppInfo *)appInfo{
    _appInfo = appInfo;
    [self.appLable setText:_appInfo.name];
    [self.imageView setImage:_appInfo.image];
}
//弹出对应的app‘name
- (IBAction) downloadBUttonSelector:(UIButton *)button{
    //获取button对应的父视图的子控件label对应的text特点值
    //增加一个UILabel到界面上
    UILabel *appNameLabel = [[UILabel alloc] initWithFrame:CGRectMake(100,500, 160, 40)];
    [appNameLabel setText:self.appInfo.name];
    [appNameLabel setTextAlignment:NSTextAlignmentCenter];
    //RGB  0 表明黑色;1 表明白色 The grayscale value of the color object, specified as a value from 0.0 to 1.0.     参数二: alpha 表明通明度 The opacity value of the color object, specified as a value from 0.0 to 1.0.
    [appNameLabel setBackgroundColor:[UIColor colorWithWhite:0.8 alpha:0.5]];
    [appNameLabel setNumberOfLines:2];
    [[self superview]  addSubview:appNameLabel];
    //禁用按钮
    [button setEnabled:NO];   
    //首尾式动画:修正目标的特点,frame、bounds、alpha;但其不简单监听动画的完成时间、不简单完成动画的嵌套
    // 初始透明度
    [appNameLabel setAlpha:0.0];//完全透明
    //^ 表明为block, 是一个预先准备好的代码块;能够当作参数传递,在需求的时分履行;块代码在oc中,运用十分遍及
    //块动画
    [UIView animateWithDuration:1.0f animations:^{
        //修正目标的特点
        [appNameLabel setAlpha:1.0];//动画内容
    } completion:^(BOOL finished) {
        //动画结束之后,删去控件
        [UIView animateWithDuration:1.0f animations:^{
            [appNameLabel setAlpha:0.0];
        } completion:^(BOOL finished) {
            [appNameLabel removeFromSuperview];
        }];
    }];
}
@end

VI、see also

Xcode运用小技巧

kunnan.blog.csdn.net/article/det…

联络作者: iOS逆向(公号:iosrev)


作者简介:CSDN 博客专家认证丨全站 Top 50、华为云云享专家认证、iOS逆向公号号主


简历模板、技能互助。重视我,都给你。