iOS自定义UIView

XIB自定义UIView

在XIB中使用autolayout是非常方便的,所以有的时候我们需要使用XIB来创建自定义UIView。但是不像UITableViewCell,创建Class的时候可以选择勾选自动创建XIB,UIView只能单独创建XIB,下面整理一下通过XIB创建自定义UIView的步骤:

  • 创建自定义的UIView,例如MyXibView extends UIView;
  • 创建不相同名字的XIB,使用View模板,例如MyXib.xib;
  • 在XIB中设置View的Class为MyXibView;
  • 在MyXibView.m中实现一个类方法,代码如下:
+ (MyXibView*) myXibView {
  NSArray* array = [[NSBundle mainBundle] loadNibNamed:@"MyXib" owner:nil options:nil];
  return [array objectAtIndex:0]; 
}

后续的操作就如你正常的进行,在XIB中放置控件,并设置autolayout约束,关联代码文件中的IBOutlet和IBAction,进行相关的业务逻辑处理;

这里XIB与Class的必须要使用不同的名字,否则会出各种问题:

  1. 在XIB的ViewController中frame问题,设置了frame无效,必须要在viewWillLayoutSubviews方法重设。
  2. 在纯代码的ViewController中会奔溃,[UIViewController _loadViewFromNibNamed:bundle:] loaded the "MyView" nib but the view outlet was not set.。猜测原因是[NSBundle loadNibNamed]方法会寻找Nib同名的Class,默认认为这个Class是UIViewController,需要一个IBOutlet,但是这里没有连接IBOutlet,而且对应的也只是一个UIView。

initWithCoder

在测试上面自定义方法的时候我有想到initWithCoder方法,也参考了initWithFrame方法的实现,结果就死循环了,loadNibNamed方法会调用initWithCoder方法,而initWithCoder方法又调用了loadNibNamed方法。

initWithCoder方法是当前控件被其他的XIB或者Storyboard文件引用的时候调用的,自己有一个对应的XIB文件,然而外部的XIB或者Storyboard文件中也有相应的布局,那么这两个布局冲突了。如果不实现initWithCoder方法,XIB或者Storyboard引用的时候自己的XIB中的内容和布局都不会存在。

所以如果需要在外部的XIB或者Storyboard文件中使用自定义的控件,不要使用XIB这种方式创建,应该试用纯代码的方式创建。

参考链接:iPhone-load-a-uiview-from-a-nib-file

IB_DESIGNABLE和IBInspectable

使用纯代码自定义UIView的时候可以使用这两个属性,让使用者在XIB或者Storyboard中实时看到修改属性的效果。

具体使用方法是在Interface上加上IB_DESIGNABLE,在属性上加上IBInspectable,重写属性的setter方法,对UIView的样式进行修改。

#import <UIKit/UIKit.h>

IB_DESIGNABLE
/**
 自定义导航栏
 */
@interface CustomNavi : UIView

/**
 是否需要返回按钮
 */
@property (nonatomic, assign) IBInspectable BOOL needBackButton;
/**
 显示的标题
 */
@property (nonatomic, copy) IBInspectable NSString *title;
/**
 背景色
 */
@property (nonatomic, copy) IBInspectable UIColor *bgColor;

@end

// ------------------------- 我是分割线 ------------------------- 
#import "CustomNavi.h"

@implementation CustomNavi {
    UIButton *_backButton;
    UILabel *_titleLabel;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self initUI];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self initUI];
    }
    return self;
}

/**
 初始化,代码创建标题Label
 */
- (void)initUI {
    _titleLabel = [[UILabel alloc] init];
    [self addSubview:_titleLabel];
}


/**
 设置标题,居中显示

 @param title 标题
 */
- (void)setTitle:(NSString *)title {
    [_titleLabel setText:title];
    [_titleLabel sizeToFit];
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    _titleLabel.center = CGPointMake(screenWidth/2.0f, 22);
}

/**
 设置是否需要返回按钮
 */
- (void)setNeedBackButton:(BOOL)needBackButton {
    if (needBackButton) {
        self.backButton.hidden = !needBackButton;
    } else {
        _backButton.hidden = !needBackButton;
    }
}

/**
 懒加载方式创建返回按钮
 */
- (UIButton *)backButton {
    if (_backButton == nil) {
        _backButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _backButton.frame = CGRectMake(8, 6, 28, 28);
        [_backButton setTitle:@"<" forState:UIControlStateNormal];
        [self addSubview:_backButton];
    }
    return _backButton;
}

/**
 设置背景颜色
 */
- (void)setBgColor:(UIColor *)bgColor {
    self.backgroundColor = bgColor;
}

@end

XIB创建UIView方式2

XIB创建UIView除了上面的方法外,还有一种方式。

  1. 创建XIB文件,创建Class文件,两个文件名随意,可以相同也可以不相同。
  2. 选择XIB的File's Owner,设置Class为创建的Class文件名。
  3. 在Class中定义一个IBOutlet的view,将XIB中的View连线至这个IBOutlet。
  4. 在XIB中放置控件,并设置autolayout约束,关联代码文件中的IBOutlet和IBAction。
  5. 重写Class中的init方法:
@interface CustomXibView : UIView

@property (nonatomic, weak) IBOutlet UIView *contentView;

@end

@implementation CustomXibView

- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super initWithCoder:coder];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self commonInit];
    }
    return self;
}

- (void)commonInit {
    // 这里因为与XIB名字相同,所以使用[self class]
    [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
    [self addSubview:self.contentView];
    self.contentView.frame = self.bounds;
    // 把内容放到最下面
    [self sendSubviewToBack:self.contentView];
}

参考链接:Creating a Custom View using a xib in Objective-C

这种方式创建的UIView其实外面会套个壳,frame修改的时候需要同步到contentView,但是优点是可以同时在存代码和XIB中使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容