一:Xib复用
1.Xib简介
File's Owner是控制对象,可以说是nib文件的所有者,控制管理可视化对象。这个和使用代码loadNibNamed中的owner是一样的意思。
custom class是视图或者控制器绑定的类名,xib、sb长加载的时候在调用-(instancetype)initWithCoder:(NSCoder *)aDecoder方法
2.Xib复用。分为代码加载和xib、sb中加载。
Xib、sb中加载
调用-(instancetype)initWithCoder:(NSCoder *)aDecoder方法。该方法无frame参数,因此我可以自定添加约束,也可以不加约束(那么显示的结果为所画即所得)。
- 第一步 将file's owner设置为UIView的属性关联类,注意一定不要设置View的custom class为属性关联类否则会报错could not load any Objective-C class information. This will significantly reduce the quality of type information available.。这个错误是因为循环调用加载方法。
- 第二步,在initWithCoder中写加载方法,这里是先找到视图,然后再讲找到的视图添加到该view类中,如需要自动布局,可用masony第三方。若用系统的自动约束,应该先取消停靠模式自动转化为约束。代码如下:
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
if ([super initWithCoder:aDecoder]) {
UIView *contentView =[[[NSBundle mainBundle]loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] firstObject];
[self addSubview:contentView];
//不将停靠模式转化为自动布局
self.translatesAutoresizingMaskIntoConstraints=NO;
contentView.translatesAutoresizingMaskIntoConstraints=NO;
// 这里不能没有约束 及时xib里面有约束 如果这里不写约束,出来的效果也是奇形怪状,只有在xib 之前尝试用masonry布局修改这部分代码但是没有成功。
[self addConstraint: [NSLayoutConstraint constraintWithItem: contentView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem: self attribute:NSLayoutAttributeLeft multiplier: 1.0 constant: 0]];
[self addConstraint: [NSLayoutConstraint constraintWithItem: contentView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem: self attribute:NSLayoutAttributeRight multiplier: 1.0 constant: 0]];
[self addConstraint: [NSLayoutConstraint constraintWithItem: contentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem: self attribute:NSLayoutAttributeTop multiplier: 1.0 constant: 0]];
[self addConstraint: [NSLayoutConstraint constraintWithItem: contentView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem: self attribute:NSLayoutAttributeBottom multiplier: 1.0 constant: 0]];
}
//这里改属性要放在加载nib之后
_leftButton.backgroundColor = [UIColor grayColor];
return self;
*第三步,在xib,sb中创建View,同时给View关联所属类为自定义View类。
代码加载
代码加载有两种方法,一种是用initWithFrame加载,一种是用loadNibNamed:加载。
- initWithFrame加载,第一步:设置File's owner为View类。第二步加载视图:用loadNibNamed:加载到view视图上。
-(instancetype)initWithFrame:(CGRect)frame{
if ([super initWithFrame:frame]) {
UIView *subView = [[NSBundle mainBundle]loadNibNamed:@"CustomView2" owner:self options:nil].firstObject;
[self addSubview:subView];
subView.frame = CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame));
}
self.frame = frame;
//加载了过后才可以修改属性
self.segementController.backgroundColor = [UIColor redColor];
return self;
}
//调用的时候
CustomView2 *customView2 = [[CustomView2 alloc]initWithFrame:CGRectMake(0, 100, 120, 100)];
[self.view addSubview:customView2];
该方法的好处是由于设置的是file's owner,所以如果再写initWithCode那么也可以在xib、sb中直接添加。
- loadNibNamed方法加载,直接绑定View的所属类,关联属性就可以了。
CustomView3 *customView3 = [[NSBundle mainBundle]loadNibNamed:@"CustomView3" owner:self options:nil].firstObject;
customView3.frame = CGRectMake(100, 400, 70, 50);
[customView3.mySwitch addTarget:self action:@selector(switchChangeAction:) forControlEvents:UIControlEventAllEvents];
customView3.mySwitch.tintColor = [UIColor grayColor];
[self.view addSubview:customView3];
该方法的缺点是由于绑定了View的所属类,所以不能再xib,sb中直接画。若直接画,显示出来的也只不过是一个空白的视图。
综上:xib的复用建议使用绑定file's owner,同时写initWithCode和initWithFrame方法。这样就可以代码和xib,sb都可以达到复用。
二:UI经验。
1) UIView
- -(init)initWithCode在该方法中加载xib
- -(void)awakeFromNib方法是在视图加载xib加载完成后调用。通常cell视图在该方法中修改设置子视图的属性。
2) UIViewController的生命周期
- 创建显示过程
1、 alloc 创建对象,分配空间
2、init (initWithNibName) 初始化对象,初始化数据
3、loadView 从nib载入视图 ,通常这一步不需要去干涉。除非你没有使用xib文件创建视图
4、viewDidLoad 载入完成,可以进行自定义数据以及动态创建其他控件
5、viewWillAppear 视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了
6、viewDidAppear 视图已在屏幕上渲染完成 - 消失移除过程
1、viewWillDisappear 视图将被从屏幕上移除之前执行
2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了
3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放
3)系统控件,不能满足UI需求。
解决方法:
1.自定义控件。系统UI扩展度低,有些效果难以达到,且可能有奇怪bug。
例如共享对象UINavigationBar、SearchBar等。
2.继承基类控件,重写initWithFrame、layoutSubviews。
参考:https://juejin.im/post/5a30f70f6fb9a044fe465cfc
4) 自动约束
可以在sb,xib中拉约束,可以是用masonry手动写约束。
sb,xib中拉约束
- 建立约束的时候必须有一个UIView的大小是可变的,这样才可以适应屏幕变化。同时UIView的宽度和高度与屏幕高度和宽度成比例。
- labe,Button,UIImageView,UISegmentControl,UISwitch等可以根据图片和文字自适应大小,当图片大小变化,文字长短变化时,视图大小也变化。而像UIView,UIScrollView都需要确定确定大小。
-
设置视图的高宽比,对于图片高度建立约束,最好设置视图高宽比等于图片高宽比,这样显示的图片才不会失真。比如自定义TabBar要设置TabBarItem高宽比(按图片高宽比),不然图片要失真。
- tabBarItem要使用镂空图,不然要着色成一个颜色(UIBarButtonItem也是如此)。若没有镂空图,建议使用显示图片的原图模式着色。代码如下:
UIImage *image = [UIImage imageNamed:@"add"];
[image imageWithRenderingMode:UIImageRenderingModeAutomatic];
UIImage *selectedImage = [UIImage imageNamed:@"add"];
[selectedImage imageWithRenderingMode:UIImageRenderingModeAutomatic];
UITabBarItem *tabItem =[[UITabBarItem alloc]initWithTitle:@"设置" image:image selectedImage:selectedImage];
AVMyInfoTableViewController *myTableVC = [[AVMyInfoTableViewController alloc]init];
myTableVC.tabBarItem = tabItem;
图片的ViewMode其中三种Aspect Fill当UIImageView比UIImage小时显示原图,当然可以用clip或者masktobounce切割边缘,这样只显示UIImageView大小的切割而成的UIImageView。当UIImageView比UIImage大时图片放大填充。Aspect fit当图片比视图小时显示原图,当图片比视图大时显示按比例缩小的图片。Sacele 模式就是按试图大小,缩放图片填充试图。一般来说我们给图片宽度一个相对于屏幕的宽度的值,然后获取image,获取他的宽高比,再讲UIIImageView的高宽比设为它。最后设置UIImageView为scalel模式,这样图片缩放的时候是按图片高宽比缩放的就不会失去真。当然也可以用fill模式,那么视图小时要切割。之所以设置视图的高宽比为图片的高宽比,且根据图片宽度与视图宽度大小关系改变视图显示模式,是因为ViewModel只是改变显示模式,实际上视图的高度还是适应图片的高度
UIScrollView排UIView的最上面那么UIScrollView坐标会自动下移64个单位长度,可以用**self.automaticallyAdjustsScrollViewInsets **取消下移。
对应超过屏幕大小的视图,放在UIScrollView中,即使UIView下面又很多分隔线也不要放到静态UITableViewController中,除非每个View结构和静态cell结构非常相似。
UIScrollView 在 Auto Layout 是一个很特殊的 view,对于 UIScrollView 的 subview 来说,它的 leading/trailing/top/bottom space 是相对于 UIScrollView 的 contentSize 而不是 bounds 来确定的,所以当你尝试用 UIScrollView 和它 subview 的 leading/trailing/top/bottom 来互相决定大小的时候,就会出现「Has ambiguous scrollable content width/height」的 warning。正确的姿势是用 UIScrollView 外部的 view 或 UIScrollView 本身的 width/height 确定 subview 的尺寸,进而确定 contentSize。因为 UIScrollView 本身leading/trailing/top/bottom 变得不好用,所以我习惯的做法是在 UIScrollView 和它原来subviews 之间增加一个 content view,这样做的好处有:
不会在 storyboard 里留下 error/warning为 subview 提供leading/trailing/top/bottom,方便 subview 的布局。
通过调整 content view 的 size(可以是 constraint 的 IBOutlet)来调整 contentSize不需要 hard code 与屏幕尺寸相关的代码更好地支持 rotation。
5)手写约束
系统约束
- 建立系统约束
[self.button_1 setTranslatesAutoresizingMaskIntoConstraints:NO];
NSArray * b1_image = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[button1]-[imageview]"
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:@{@"button1":self.button_1,
@"imageview":self.imageview}];
[self.view addConstraints:b1_image];
[self.view addSubview:self.button_2];
[self.button_2 setTranslatesAutoresizingMaskIntoConstraints:NO];
NSArray * b2_image = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[button2]-[imageview]"
options:NSLayoutFormatAlignAllRight
metrics:nil
views:@{@"button2":self.button_2,
@"imageview":self.imageview}];
[self.view addConstraints:b2_image];
//或者
// 参数说明:
// 第一个参数:指定约束左边的视图view1
// 第二个参数:指定view1的属性attr1,具体属性见文末。
// 第三个参数:指定左右两边的视图的关系relation,具体关系见文末。
// 第四个参数:指定约束右边的视图view2
// 第五个参数:指定view2的属性attr2,具体属性见文末。
// 第六个参数:指定一个与view2属性相乘的乘数multiplier
// 第七个参数:指定一个与view2属性相加的浮点数constant
//
// 这个函数的对照公式为:
// view1.attr1 <relation> view2.attr2 * multiplier + constant
//如果你想设置的约束里不需要第二个view,要将第四个参数设为nil,第五个参数设为NSLayoutAttributeNotAnAttribute
[self.imageview setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint * hc = [NSLayoutConstraint
constraintWithItem:self.view
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.imageview
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
NSLayoutConstraint * vc = [NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.imageview
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
NSLayoutConstraint * equalW = [NSLayoutConstraint constraintWithItem:self.imageview
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:0
multiplier:1.0
constant:CGRectGetWidth(self.imageview.frame)];
NSLayoutConstraint * equalH = [NSLayoutConstraint constraintWithItem:self.imageview
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:0
multiplier:1.0
constant:CGRectGetHeight(self.imageview.frame)];
[self.view addConstraints:@[hc,vc,equalH,equalW]];
[self.view addSubview:self.button_1];
[self.button_1 setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint * b1_image_v = [NSLayoutConstraint constraintWithItem:self.imageview
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.button_1
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:8.0];
NSLayoutConstraint * b1_image_h = [NSLayoutConstraint constraintWithItem:self.button_1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.imageview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0];
[self.view addConstraints:@[b1_image_h,b1_image_v]];
[self.view addSubview:self.button_2];
[self.button_2 setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint * b2_image_v = [NSLayoutConstraint constraintWithItem:self.button_2
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.imageview
attribute: NSLayoutAttributeBottom
multiplier:1.0
constant:8.0];
NSLayoutConstraint * b2_image_h = [NSLayoutConstraint constraintWithItem:self.button_2
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.imageview
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
[self.view addConstraints:@[b2_image_h,b2_image_v]];
- 更新属性约束,NSLayoutConstraint的constant可直接更改,multiplier是只可读属性。所以当我们要修改multiplier的时候应该先移除这个约束,再添加新的约束。代码如下
- (IBAction)buttonAction:(id)sender {
[self.button removeConstraint:self.buttonWidthAndHeight];
self.buttonWidthAndHeight = [NSLayoutConstraint constraintWithItem:self.button attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.button attribute:NSLayoutAttributeHeight multiplier:0.5 constant:0];
[self.button addConstraint: self.buttonWidthAndHeight];
}
- 示例代码
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
masonry和系统约束的关系
MASViewAttribute NSLayoutAttribute
view.mas_left NSLayoutAttributeLeft
view.mas_right NSLayoutAttributeRight
view.mas_top NSLayoutAttributeTop
view.mas_bottom NSLayoutAttributeBottom
view.mas_leading NSLayoutAttributeLeading
view.mas_trailing NSLayoutAttributeTrailing
view.mas_width NSLayoutAttributeWidth
view.mas_height NSLayoutAttributeHeight
view.mas_centerX NSLayoutAttributeCenterX
view.mas_centerY NSLayoutAttributeCenterY
view.as_baseline NSLayoutAttributeBaseline
masonry约束
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
//或者
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
** 同时masonry在设置约束时自己调用了view1.translatesAutoresizingMaskIntoConstraints = NO;**
- edges
// make top, left, bottom, right equal view2
make.edges.equalTo(view2);
// make top = superview.top + 5, left = superview.left + 10,
// bottom = superview.bottom - 15, right = superview.right - 20
make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))
- size
// make width and height greater than or equal to titleLabel
make.size.greaterThanOrEqualTo(titleLabel)
// make width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))
- center
// make centerX and centerY = button1
make.center.equalTo(button1)
// make centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))
- 其他方式创建约束
// All edges but the top should equal those of the superview
make.left.right.and.bottom.equalTo(superview);
make.top.equalTo(otherView);
更新约束
- 赋值的方法建立约束,更改类属性约束
// in public/private interface
@property (nonatomic, strong) MASConstraint *topConstraint;
...
// when making constraints
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];
...
// then later you can call
[self.topConstraint uninstall];
- mas_updateConstraints 更新约束
// this is Apple's recommended place for adding/updating constraints
// this method can get called multiple times in response to setNeedsUpdateConstraints
// which can be called by UIKit internally or in your code if you need to trigger an update to your constraints
- (void)updateConstraints {
[self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self);
make.width.equalTo(@(self.buttonSize.width)).priorityLow();
make.height.equalTo(@(self.buttonSize.height)).priorityLow();
make.width.lessThanOrEqualTo(self);
make.height.lessThanOrEqualTo(self);
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
- mas_remakeConstraints重置约束, 删除了原来该试图的所有约束,重建约束。
- (void)changeButtonPosition {
[self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
make.size.equalTo(self.buttonSize);
if (topLeft) {
make.top.and.left.offset(10);
} else {
make.bottom.and.right.offset(-10);
}
}];
}
- 创建约束的地方
@implementation DIYCustomView
- (id)init {
self = [super init];
if (!self) return nil;
// --- Create your views here ---
self.button = [[UIButton alloc] init];
return self;
}
// tell UIKit that you are using AutoLayout
+ (BOOL)requiresConstraintBasedLayout {
return YES;
}
// this is Apple's recommended place for adding/updating constraints
- (void)updateConstraints {
// --- remake/update constraints here
[self.button remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@(self.buttonSize.width));
make.height.equalTo(@(self.buttonSize.height));
}];
//according to apple super should be called at end of method
[super updateConstraints];
}
- (void)didTapButton:(UIButton *)button {
// --- Do your changes ie change variables that affect your layout etc ---
self.buttonSize = CGSize(200, 200);
// tell constraints they need updating
[self setNeedsUpdateConstraints];
}
@end
- UIView布局刷新
- layoutSubviews 调用时机
1.直接调用[self setNeedsLayout];(这个在上面苹果官方文档里有说明)
2.addSubview的时候。
3.当view的size发生改变的时候。
4.滑动UIScrollView的时候。
5.旋转Screen会触发父UIView上的layoutSubviews事件。
- layoutSubviews 调用时机
- 运用场景
- 视图布局,init时用自动布局无法获取frame,在改方法中可以获取 frame。
- 在该方法中给子视图布局,避免自动布局手动布局混合使用时造成布局混乱。该方法中父类frame已知。
- 改变一个视图的frame和约束时将调用该方法,给改变视图的图片文本样式将激活调用该方法。
- 前后调用这两个方法(setNeedsLayout layoutIfNeeded)可以激活调用layoutSubView;
/**
*/
-(void)layoutSubviews{
[super layoutSubviews];
//布局子视图
}
UITableView
TableView中取消选中颜色变化,在didSelectRowAtIndexPath中写入 [tableView deselectRowAtIndePath]就可以取消选中状态。当然我们可以用dispath_after延迟选中选中。
TableView的两种类型,Plain为分隔线充满屏幕,且组头有悬浮模式。group有默认的组头,且每组开头有一个分隔线没法去除。
tableView最好使用Plain模式,设置无分割线,在cell中画分割线,取消组头悬浮模式。取消悬浮如下代码:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat sectionHeaderHeight = 40;
if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
} else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
}
}
UINavigationBar
- 全局NavigationBar
UINavigationBar *bar = [UINavigationBar appearance];
- 设置半透明效果,若是半透明的那么颜色,透明度都会有系统偏移,有半透明效果。建议设置为no。
self.navigationController.navigationBar.translucent = NO;
- 设置背景色
self.navigationController.navigationBar.barTintColor = [UIColor redColor];
- 设置背景图
UINavigationBar *bar = [UINavigationBar appearance];
[bar setBackgroundImage:[UIImage imageNamed:@"alert_error_icon"] forBarMetrics:UIBarMetricsDefault];
- 设置镂空色
UINavigationBar *bar = [UINavigationBar appearance];
[bar setTintColor:[UIColor grayColor]];
- 建议在试图创建之前设置,即appDelegate的设置,若设置了不显示可以ViewController中用
-(void)viewWillAppear:(BOOL)animated{
[self setNeedsStatusBarAppearanceUpdate];
}
UINavigationItem
- 设置标题试图的镂空色
// UITextAttributeFont - 字体
// UITextAttributeTextColor - 文字颜色
// UITextAttributeTextShadowColor - 文字阴影颜色
// UITextAttributeTextShadowOffset - 偏移用于文本阴影
UINavigationBar *bar = [UINavigationBar appearance];
[bar setTintColor:[UIColor grayColor]];
//或者
[self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName: [UIColor redColor],NSFontAttributeName:[UIFont systemFontOfSize:19.0]}];
self.title=[NSString stringWithFormat:@"第%lu页",(unsigned long)self.navigationController.viewControllers.count];
- 自定义颜色,使用tintcolor必须要是镂空图,不然会被着色成为一个颜色。所以建议使用显示原图片着色模式,代码如下:
//自定义试图
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc]initWithCustomView:button];
//或者改变图片作色模式
UIImage *image = [UIImage imageNamed:@"add"];
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc]initWithImage:[image imageWithRenderingMode:UIImageRenderingModeAutomatic] style:UIBarButtonItemStyleDone target:nil action:nil];
- 设置返回按钮
- 说一下使用pushViewController切换到下一个视图时,navigation controller按照以下3条顺序更改导航栏的左侧按钮(本段摘自网络):A->B
1、如果B视图有一个自定义的左侧按钮(leftBarButtonItem),则会显示这个自定义按钮;
2、如果B没有自定义按钮,但是A视图的backBarButtonItem属性有自定义项,则显示这个自定义项;
3、如果前2条都没有,则默认显示一个后退按钮,后退按钮的标题是A视图的标题; - 定义全局返回按钮,设置title的偏移值,常用来隐藏title。但是多push几个页面可能造成ViewControll的Title位置偏移有隐患。一般跳到第三个试图的时候就会影响控制器的title位置了。且返回按钮的图片拉升也常出现问题,常常失真。
//偏移标题位置
[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60)
forBarMetrics:UIBarMetricsDefault];
设置返回按钮的图片(然而这个方法由于放回按钮有拉升效果所以并不准确,还有影响UIViewController的嫌疑建议不使用)
UIImage *backButtonImage = [[UIImage imageNamed:@"fanhui.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 30, 0, 0)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
- 第一某个UIViewController的返回按钮
//所有的子界面返回时都变成了我们定义的文字,如果不想显示文字,直接"",就会单独显示一个系统的返回箭头图标,也是很清晰的感觉。
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = item;
最后建议返回按钮用viewController.navigationItem.leftBarButtonItem,用子控制器继承父类的方法,这样写的最简单。
- 设置NavigationItem的位置
//通过添加一个宽度为负的站位UIBarButtonItem来实现的
+(void)createBarButtonItemTitle:(NSString*)title andImageName:(NSString*)imageName andSEL:(SEL)sel onViewController:(UIViewController*)viewController andIsLeft:(BOOL)isLeft{
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(10, 0, 40, 40);
[button setBackgroundImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
button.layer.cornerRadius =20;
button.layer.masksToBounds = YES;
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc]initWithCustomView:button];
//设置返回按钮的属性
UIBarButtonItem *negativeSeperator = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
if (isLeft) {
negativeSeperator.width = -20;//此处修改到边界的距离,请自行测试
viewController.navigationItem.leftBarButtonItems = @[negativeSeperator,buttonItem];
}else{
negativeSeperator.width = -15;//此处修改到边界的距离,请自行测试
viewController.navigationItem.rightBarButtonItems = @[negativeSeperator,buttonItem];
}
[button addTarget:viewController action:sel forControlEvents:UIControlEventTouchUpInside];
}
设置tabBar 前后嵌套结构有两种,建议使用第二种。
第一种方式UITabBarController+UINavigationController,这种方式TabBarController的子ViewController push的时候要手动隐藏tabBar。
- 使用系统自带的tabBar要在设置tabBar的子视图控制器的时候设置子视图控制器的TabBarItem,因为tabBar加载子视图控制器采用的懒加载放肆。
NSMutableArray *vcs = [NSMutableArray array];
for (int i = 0; i < colors.count; i++) {
UIStoryboard *storyboard =[UIStoryboard storyboardWithName:@"Main" bundle:nil];
CDTestViewController *testVC = [storyboard instantiateViewControllerWithIdentifier:@"CDTestViewController"];
NSString *title = [NSString stringWithFormat:@"第%d栏", i + 1];
testVC.title = title;
testVC.bgColor = colors[i];
testVC.tabBarItem = [[UITabBarItem alloc]initWithTitle:title image:nil tag:nil];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:testVC];
[vcs addObject:nav];
}
_tab.viewControllers = vcs;
子控制器push的时候如何隐藏tabBar
CDTestViewControllerTwo *twoVC = [[CDTestViewControllerTwo alloc]init];
twoVC.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:twoVC animated:YES];
twoVC.hidesBottomBarWhenPushed = NO;
- 如何自定义tabBar,用xib绘制,隐藏系统自带的,在tabBarController的View上加Xib视图。才用通知的方式。
- (void)viewDidLoad {
[super viewDidLoad];
// 隐藏默认的分栏条
self.tabBar.hidden = YES;
// 第一个参数是XIB文件的名字
// 第二个参数是加载的视图的所有者
tabBarView = [[[NSBundle mainBundle] loadNibNamed:@"CDMyTabBarView" owner:self options:nil] firstObject];
tabBarView.frame = CGRectMake(0, self.view.bounds.size.height - tabBarView .frame.size.height, tabBarView.frame.size.width, tabBarView.frame.size.height);
[self.view addSubview:tabBarView];
//注意默认情况下UITabBarController在加载子视图时是懒加载的,所以这里要一次性设定所有子视图的UITabBarItem,不然默认自加载第一个。
for (UIView *tempView in tabBarView.subviews) {
if (tempView.tag >= 200 && tempView.tag <= 203) {
UIButton *tempButton = (id) tempView;
[tempButton addTarget:self action:@selector(tabButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
}
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(hideTabBar) name:@"hideTabBar" object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(showTabBar) name:@"showTabBar" object:nil];
}
-(void)hideTabBar{
tabBarView.hidden = YES;
}
-(void)showTabBar{
tabBarView.hidden = NO;
}
//在NavigationController的子ViewController中
-(void)viewDidAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter]postNotificationName:@"showTabBar" object:nil];
}
-(void)viewDidDisappear:(BOOL)animated{
[[NSNotificationCenter defaultCenter]postNotificationName:@"hideTabBar" object:nil];
}
第二种方式 UINavigationContorller+UITabBarController+UIViewController,由于tabBar后面不是Navigation,所以跳转后跳出了UITabBarController。
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = [self setRootViewController];
[self.window makeKeyAndVisible];
return YES;
}
-(UINavigationController*)setRootViewController{
LTTabBarController *tabBarController = [[LTTabBarController alloc]init];
HomeIndexController *homeIndexController = [[HomeIndexController alloc] init];
LTTourViewController *tourVC = [[LTTourViewController alloc]init];
LTAmusementViewController *amusementVC = [[LTAmusementViewController alloc]init];
LTMyViewController *myVC = [[LTMyViewController alloc]init];
UINavigationController *navigationControl = [[UINavigationController alloc]initWithRootViewController:tabBarController];
NSArray *arrarVC = @[homeIndexController,tourVC,amusementVC,myVC];
tabBarController.viewControllers = arrarVC;
NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
tabBarController.selectedIndex = [userDef integerForKey:@"selectedIndex"];
return navigationControl;
}
建议使用第二种
设置启动页面
- 首先我们无法对LaunchScreen.xib或者LaunchScreen.storyboard中View试图更新修改,xib或storyboard中的所见即所得。
- 可以在window的rootViewController(tabBarContoroller或者navigationController的View中加入启动动画)代码如下:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self launchAnimation];
}
#pragma mark - Private Methods
- (void)launchAnimation {
UIViewController *viewController = [[UIStoryboard storyboardWithName:@"LaunchScreen" bundle:nil] instantiateViewControllerWithIdentifier:@"LaunchScreen"];
UIView *launchView = viewController.view;
UIWindow *mainWindow = [UIApplication sharedApplication].keyWindow;
launchView.frame = [UIApplication sharedApplication].keyWindow.frame;
[mainWindow addSubview:launchView];
[UIView animateWithDuration:1.0f delay:0.5f options:UIViewAnimationOptionBeginFromCurrentState animations:^{
launchView.alpha = 0.0f;
launchView.layer.transform = CATransform3DScale(CATransform3DIdentity, 2.0f, 2.0f, 1.0f);
} completion:^(BOOL finished) {
[launchView removeFromSuperview];
}];
}
设置TitleView
- 低版本 这里的titleView可能会受navigationItem影响位置,建议可以在给他设置个frame。
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"appcoda-logo.png"]];
- 设置title
//设置title
self.title = @"设置";
navigationItem和navigationBar如果过于复杂,可以隐藏NavigationBar,自定义View作为NavigationBar。同时如果自定义的ViewController返回按钮影响title的位置,那么就很难更改title的位置或者所修改LeftBarButtonItem的Insets值。那么建议隐藏系统的返回按钮自定义UIBarButtonItem作为左边返回按钮,但是这种方法很苦逼,每个控制器都要写,可以采用子视图控制器继承父的方法。
设置xib customView显示在sb上,使用IB_DESIGNABLE和IBInspectable。
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface CustomView : UIView
//如果子类是UIButton 在SB中子类无法使用该类
@property(nonatomic,assign)IBInspectable NSInteger widthboard;
-(void)setWidthboard:(NSInteger)widthboard;
@end
#import "CustomView.h"
@implementation CustomView
-(void)setWidthboard:(NSInteger)widthboard{
self.layer.borderWidth = widthboard;
self.layer.borderColor = [UIColor redColor].CGColor;
}