一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情。
自定义View
自定义View的基本步骤
- 重写
- (instancetype)initWithFrame方法,在此方法中创建并添加子控件。 - 提供一个便利的构造方法,通常为 类方法,快速创建一个实例对象
- 重写
- (void)layoutSubviews方法,在此方法中设置子控件的frame,一定要调用[super layoutSubviews] - 设置模型属性,在set方法中,给对应的子控件赋值。
设置数据的方案1:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface DukeShopView : UIView
//@property (nonatomic, weak, readonly) UIImageView *iconView;
//@property (nonatomic, weak, readonly) UILabel *titleLabel;
- (void)setIcon: (NSString *)icon;
- (void)setName: (NSString *)name;
@end
NS_ASSUME_NONNULL_END
#import "DukeShopView.h"
@interface DukeShopView ()
@property (nonatomic, weak) UIImageView *iconView;
@property (nonatomic, weak) UILabel *titleLabel;
@end
@implementation DukeShopView
// 初始化子控件(不要设置frame)
- (instancetype)init{
if (self = [super init]) {
// 1. 创建商品的UIImageView对象
UIImageView *iconView = [[UIImageView alloc] init];
iconView.backgroundColor = [UIColor redColor];
[self addSubview:iconView];
_iconView = iconView;
// 2. 创建商品标题对象
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.backgroundColor = [UIColor yellowColor];
titleLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:titleLabel];
_titleLabel = titleLabel;
}
return self;
}
// 布局子控件(可以拿到frame)
- (void)layoutSubviews
{
[super layoutSubviews];
// 1. 获取当前控件的尺寸
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
// 2. 设置子控件的frame
self.iconView.frame = CGRectMake(0, 0, width, height);
self.titleLabel.frame = CGRectMake(0, width, width, height - width);
}
- (void)setIcon: (NSString *)icon{
self.iconView.image = [UIImage imageNamed:icon];
}
- (void)setName: (NSString *)name{
self.titleLabel.text = name;
}
@end
设置数据的方案2:
#import <UIKit/UIKit.h>
@class DukeShop;
NS_ASSUME_NONNULL_BEGIN
@interface DukeShopView : UIView
//@property (nonatomic, weak, readonly) UIImageView *iconView;
//@property (nonatomic, weak, readonly) UILabel *titleLabel;
//- (void)setIcon: (NSString *)icon;
//- (void)setName: (NSString *)name;
@property (nonatomic, strong) DukeShop *shop;
- (instancetype)initWithShop: (DukeShop *)shop;
+ (instancetype)shopViewWithShop: (DukeShop *)shop;
@end
NS_ASSUME_NONNULL_END
#import "DukeShopView.h"
#import "DukeShop.h"
@interface DukeShopView ()
@property (nonatomic, weak) UIImageView *iconView;
@property (nonatomic, weak) UILabel *titleLabel;
@end
@implementation DukeShopView
// 初始化子控件(不要设置frame)
//- (instancetype)init{
// if (self = [super init]) {
// [self setUp];
// }
// return self;
//}
//注意:创建对象用init方法和 initWithFrame: 都会调到这个方法里。
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setUp];
}
return self;
}
- (instancetype)initWithShop: (DukeShop *)shop{
if (self = [super init]) {
// 注意:先创建后赋值
[self setUp];
// 赋值
self.shop = shop;
}
return self;
}
+ (instancetype)shopViewWithShop: (DukeShop *)shop{
return [[self alloc] initWithShop:shop];
}
// 初始化
- (void)setUp{
// 1. 创建商品的UIImageView对象
UIImageView *iconView = [[UIImageView alloc] init];
iconView.backgroundColor = [UIColor redColor];
[self addSubview:iconView];
_iconView = iconView;
// 2. 创建商品标题对象
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.backgroundColor = [UIColor yellowColor];
titleLabel.textAlignment = NSTextAlignmentCenter;
[self addSubview:titleLabel];
_titleLabel = titleLabel;
}
// 布局子控件(可以拿到frame)
- (void)layoutSubviews
{
[super layoutSubviews];
// 1. 获取当前控件的尺寸
CGFloat width = self.frame.size.width;
CGFloat height = self.frame.size.height;
// 2. 设置子控件的frame
self.iconView.frame = CGRectMake(0, 0, width, height);
self.titleLabel.frame = CGRectMake(0, width, width, height - width);
}
//- (void)setIcon: (NSString *)icon{
// self.iconView.image = [UIImage imageNamed:icon];
//}
//- (void)setName: (NSString *)name{
// self.titleLabel.text = name;
//}
- (void)setShop:(DukeShop *)shop
{
_shop = shop;
self.iconView.image = [UIImage imageNamed:shop.icon];
self.titleLabel.text = shop.name;
}
@end
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface DukeShop : NSObject
@property (nonatomic, copy) NSString *icon;
@property (nonatomic, copy) NSString *name;
//- (instancetype)initWithIcon:(NSString *)icon name:(NSString *)name;
//+ (instancetype)shopWithIcon:(NSString *)icon name:(NSString *)name;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)shopWithDict:(NSDictionary *)dict;
@end
NS_ASSUME_NONNULL_END
#import "DukeShop.h"
@implementation DukeShop
- (instancetype)initWithIcon:(NSString *)icon name:(NSString *)name{
if(self = [super init]) {
self.icon = icon;
self.name = name;
}
return self;
}
+ (instancetype)shopWithIcon:(NSString *)icon name:(NSString *)name{
return [[self alloc] initWithIcon:icon name:name];
}
- (instancetype)initWithDict:(NSDictionary *)dict{
if(self = [super init]) {
self.icon = dict[@"icon"];
self.name = dict[@"name"];
}
return self;
}
+ (instancetype)shopWithDict:(NSDictionary *)dict{
return [[self alloc] initWithDict:dict];
}
@end
View 的封装
如果一个view内部的子控件比较多,一般会考虑自定义一个view,把它内部子控件的创建屏蔽起来,不让外界关心。
外界可以传入对应的模型数据给view,view拿到模型数据后给内部的子控件设置对应的数据。
封装控件的基本步骤: 在initWithFrame: 方法中添加子控件,提供便利构造方法 在layoutSubViews方法中设置子控件的frame(一定要调用super的layoutSubviews)
增加模型属性,在模型属性set方法中设置数据到子控件上。
以上是纯代码实现的View的封装,有些麻烦。
开发中另一种常用的封装方式是Xib
Xib 自定义view
Xib和StoryBoard对比
-
共同点: 都用来描述软件界面 都用Interface Builder工具来编辑 本质都是转换成代码去创建控件
-
不同点: Xib是轻量级的,用来描述局部的UI界面 StoryBoard是重量级的,用来描述整个软件的多个界面,并且能展示多个界面之间的跳转关系。
Xib的加载
方法1:
NSArray *views = [[NSBundle mainbundle] loadNibNamed:@"xib文件名" owner:nil options: nil]
方法2:
UINib *nib = [UINib nibWithNibName:@"xib文件名" bundle:nil];
NSArray *views = [nib instantiateWithOwner:nil options: nil];
控件有两种创建方式
-
通过代码创建
- 初始化一定会调用
-(instancetype)initWithFrame:方法
- 初始化一定会调用
-
通过Xib\StoryBoard创建
- 初始化不会调用
-(instancetype)initWithFrame:方法,只会调用-(instancetype)initWithCoder:方法 - 初始化完成之后,回调用
awakeFromNib方法
- 初始化不会调用
通过两种加载方式,可以发现:有时候我们希望在控件初始化时做一些初始化的操作,如添加子控件,设置属性等,这时候需要根据控件的加载方式来选择-(instancetype)initWithFrame:,-(instancetype)initWithCoder:,awakeFromNib三个方法中的哪个方法进行初始化。