Xib复用和UI经验

一:Xib复用

1.Xib简介

  • File's Owner是控制对象,可以说是nib文件的所有者,控制管理可视化对象。这个和使用代码loadNibNamed中的owner是一样的意思。

  • custom class是视图或者控制器绑定的类名,xib、sb长加载的时候在调用-(instancetype)initWithCoder:(NSCoder *)aDecoder方法

File'owner和View的所属类的介绍

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类。

拉控件UIView,同时给UIView绑定所属类为自定义UIView子类

代码加载

代码加载有两种方法,一种是用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高宽比(按图片高宽比),不然图片要失真。


    按住Conrol键和鼠标选中视图,最后鼠标在原视图内松开显示该图,选中Aspect Ratio,然后修改比例
  • 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事件。
      • 运用场景
    1. 视图布局,init时用自动布局无法获取frame,在改方法中可以获取 frame。
    2. 在该方法中给子视图布局,避免自动布局手动布局混合使用时造成布局混乱。该方法中父类frame已知。
    3. 改变一个视图的frame和约束时将调用该方法,给改变视图的图片文本样式将激活调用该方法。
    4. 前后调用这两个方法(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;
}

Demo示例

Xib复用Demo
AutoLayout详解Demo
Masonry第三方

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 《白鹿原》收官了,而其中人物的走向却引发大片争议。最令人痛心的就是白嘉轩的子女:白孝文和白灵,一个没有主见、一个又...
    大亲家阅读 516评论 0 0
  • 不少会计专业和非会计专业的大学生毕业之后,都纷纷踏上了会计这个行业。成为了一名会计小白。西安会计速成班认为,是由于...
    下雨天_西夕阅读 392评论 0 0
  • 今天聚会,我急急忙忙地翻箱倒柜。闺蜜打趣:“这是有多想在林洋面前好好表现留好印象啊?”顿时我动作停下来,好一会才反...
    雅妍阅读 534评论 6 3