在 .xib 中使用自定义 UIView 作为 IBOutlet
Use Custom UIView as IBOutlet in .xib
我有一个自定义 class 继承自 UIView
@interface StatusBarView : UIView
@property (weak, nonatomic) id <ActivationStatusDelegate> delegate;
//MARK: init
- (id) initWithCustom: (struct WidgetCustom *) widget;
- (id) initWithStatus:(ActivationBtnStatus) activeStatus;
//MARK: Function
- (void) setStatus: (ActivationBtnStatus) status;
@end
这是实施的一部分
@interface StatusBarView ()
@property (strong, nonatomic) IBOutlet UIButton *button1;
@property (strong, nonatomic) IBOutlet UIButton *button2;
- (void) createDefaultWidget;
@end
@implementation StatusBarView
ActivationBtnStatus status = noStatus;
struct WidgetCustom widget;
bool isWidgetSet = false;
- (void)awakeFromNib {
[super awakeFromNib];
if (!isWidgetSet) {
[self createDefaultWidget];
}
[self createButton];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self createDefaultWidget];
[self createButton];
}
return self;
}
- (void)layoutSubviews {
CAShapeLayer * maskLayer1 = [CAShapeLayer layer];
maskLayer1.path = [UIBezierPath bezierPathWithRoundedRect: self.bounds byRoundingCorners: UIRectCornerBottomRight | UIRectCornerTopRight cornerRadii: (CGSize){10.0, 10.}].CGPath;
CAShapeLayer * maskLayer2 = [CAShapeLayer layer];
maskLayer2.path = [UIBezierPath bezierPathWithRoundedRect: self.bounds byRoundingCorners: UIRectCornerBottomLeft | UIRectCornerTopLeft cornerRadii: (CGSize){10.0, 10.}].CGPath;
_button1.layer.mask = maskLayer1;
_button2.layer.mask = maskLayer2;
}
- (id)initWithCustom:(struct WidgetCustom *) widget {
self = [[[NSBundle mainBundle] loadNibNamed:@"ActivationStatus" owner:nil options:nil] lastObject];
if (self) {
isWidgetSet = true;
widget = widget;
}
return self;
}
- (id)initWithStatus:(ActivationBtnStatus)activeStatus {
self = [[[NSBundle mainBundle] loadNibNamed:@"ActivationStatus" owner:nil options:nil] lastObject];
if (self) {
status = activeStatus;
}
return self;
}
}
这就是我管理 .xib 文件的方式
文件所有者的 class 是空的,没有出口
插座连接到视图,我为 .xib 文件的视图设置 StatusBarView
class,如下所示
现在在不同的 ViewController 中,我想将此 class 用作像这样的 IBOutlet,而不需要任何更多的初始化:
但结果只是灰色视图。
有可能这样做吗?如果是,请告诉我哪里出错了?
从 XIB 加载自定义视图并不像看起来那么简单。我为它创建了一个自定义的 class,其中包含一堆方便的方法。所以为了使这项工作你必须让你的自定义视图继承自这个 class 并为它设置 bundle
属性 ,默认情况下你的 class 名称应该与XIB 名称
界面
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface MLSXibLoadingView : UIView
- (instancetype)initWithWidth:(CGFloat)width;
- (instancetype)initWithHeight:(CGFloat)height;
- (instancetype)initWithXibClass:(Class)xibClass;
- (instancetype)initFromXib;
- (instancetype)commonInit;
- (void)setup;
- (void)layoutDone;
- (void)localize;
- (void)update;
- (NSLayoutConstraint *)constraintWithIdentifier:(NSString *)identifier;
- (void)setConstant:(CGFloat)constant forConstraintWithIdentifier:(NSString *)identifier;
@property (nonatomic) NSBundle *bundle;
@property (nonatomic) BOOL layoutIsDone;
@end
实施
#import "MLSXibLoadingView.h"
@implementation MLSXibLoadingView
- (instancetype)init
{
self = [super init];
if (self) {
self = [self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self = [self commonInit];
}
return self;
}
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
if (!self.subviews.count) {
return [self commonInit];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (!_layoutIsDone) {
_layoutIsDone = YES;
[self layoutDone];
}
}
- (instancetype)initWithWidth:(CGFloat)width
{
self = [self commonInitAssigningFrame:NO];
self.frame = [MLSUtils rect:self.frame scaledToWidth:width];
return self;
}
- (instancetype)initWithHeight:(CGFloat)height
{
self = [self commonInitAssigningFrame:NO];
self.frame = [MLSUtils rect:self.frame scaledToHeight:height];
return self;
}
- (instancetype)initWithXibClass:(Class)xibClass
{
return [self commonInitAssigningFrame:NO xibClass:xibClass];
}
- (instancetype)initFromXib
{
return [self commonInitAssigningFrame:NO];
}
- (instancetype)commonInit
{
return [self commonInitAssigningFrame:YES];
}
- (void)setup
{
}
- (void)layoutDone
{
}
- (void)localize
{
}
- (void)update
{
}
- (NSBundle *)bundle
{
return nil; // implement in subclasses
}
- (instancetype)commonInitAssigningFrame:(BOOL)assignFrame
{
return [self commonInitAssigningFrame:assignFrame xibClass:self.class];
}
- (instancetype)commonInitAssigningFrame:(BOOL)assignFrame xibClass:(Class)xibClass
{
NSBundle *b = [self bundle];
assert(b);
NSString *xibName = NSStringFromClass(xibClass);
assert([b pathForResource:xibName ofType:@"nib"]);
MLSXibLoadingView *xibView = [b loadNibNamed:xibName owner:nil options:nil][0];
xibView.frame = assignFrame ? self.frame : xibView.frame;
xibView.autoresizingMask = self.autoresizingMask;
xibView.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
for (NSLayoutConstraint *constraint in self.constraints) {
id firstItem = constraint.firstItem;
if (firstItem == self) {
firstItem = xibView;
}
id secondItem = constraint.secondItem;
if (secondItem == self) {
secondItem = xibView;
}
NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:firstItem
attribute:constraint.firstAttribute
relatedBy:constraint.relation
toItem:secondItem
attribute:constraint.secondAttribute
multiplier:constraint.multiplier
constant:constraint.constant];
newConstraint.priority = constraint.priority;
newConstraint.identifier = constraint.identifier;
[xibView addConstraint:newConstraint];
}
return xibView;
}
- (NSLayoutConstraint *)constraintWithIdentifier:(NSString *)identifier
{
for (NSLayoutConstraint *constraint in self.constraints) {
if ([constraint.identifier isEqualToString:identifier]) {
return constraint;
}
}
return nil;
}
/**
* Use this method to change constraint constant with given identifier because constraints outlets get invalid
* after view exchange in commonInitAssigningFrame: method
*/
- (void)setConstant:(CGFloat)constant forConstraintWithIdentifier:(NSString *)identifier
{
for (NSLayoutConstraint *constraint in self.constraints) {
if ([constraint.identifier isEqualToString:identifier]) {
constraint.constant = constant;
break;
}
}
}
@end
我有一个自定义 class 继承自 UIView
@interface StatusBarView : UIView
@property (weak, nonatomic) id <ActivationStatusDelegate> delegate;
//MARK: init
- (id) initWithCustom: (struct WidgetCustom *) widget;
- (id) initWithStatus:(ActivationBtnStatus) activeStatus;
//MARK: Function
- (void) setStatus: (ActivationBtnStatus) status;
@end
这是实施的一部分
@interface StatusBarView ()
@property (strong, nonatomic) IBOutlet UIButton *button1;
@property (strong, nonatomic) IBOutlet UIButton *button2;
- (void) createDefaultWidget;
@end
@implementation StatusBarView
ActivationBtnStatus status = noStatus;
struct WidgetCustom widget;
bool isWidgetSet = false;
- (void)awakeFromNib {
[super awakeFromNib];
if (!isWidgetSet) {
[self createDefaultWidget];
}
[self createButton];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self createDefaultWidget];
[self createButton];
}
return self;
}
- (void)layoutSubviews {
CAShapeLayer * maskLayer1 = [CAShapeLayer layer];
maskLayer1.path = [UIBezierPath bezierPathWithRoundedRect: self.bounds byRoundingCorners: UIRectCornerBottomRight | UIRectCornerTopRight cornerRadii: (CGSize){10.0, 10.}].CGPath;
CAShapeLayer * maskLayer2 = [CAShapeLayer layer];
maskLayer2.path = [UIBezierPath bezierPathWithRoundedRect: self.bounds byRoundingCorners: UIRectCornerBottomLeft | UIRectCornerTopLeft cornerRadii: (CGSize){10.0, 10.}].CGPath;
_button1.layer.mask = maskLayer1;
_button2.layer.mask = maskLayer2;
}
- (id)initWithCustom:(struct WidgetCustom *) widget {
self = [[[NSBundle mainBundle] loadNibNamed:@"ActivationStatus" owner:nil options:nil] lastObject];
if (self) {
isWidgetSet = true;
widget = widget;
}
return self;
}
- (id)initWithStatus:(ActivationBtnStatus)activeStatus {
self = [[[NSBundle mainBundle] loadNibNamed:@"ActivationStatus" owner:nil options:nil] lastObject];
if (self) {
status = activeStatus;
}
return self;
}
}
这就是我管理 .xib 文件的方式 文件所有者的 class 是空的,没有出口
插座连接到视图,我为 .xib 文件的视图设置 StatusBarView
class,如下所示
现在在不同的 ViewController 中,我想将此 class 用作像这样的 IBOutlet,而不需要任何更多的初始化:
但结果只是灰色视图。 有可能这样做吗?如果是,请告诉我哪里出错了?
从 XIB 加载自定义视图并不像看起来那么简单。我为它创建了一个自定义的 class,其中包含一堆方便的方法。所以为了使这项工作你必须让你的自定义视图继承自这个 class 并为它设置 bundle
属性 ,默认情况下你的 class 名称应该与XIB 名称
界面
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface MLSXibLoadingView : UIView
- (instancetype)initWithWidth:(CGFloat)width;
- (instancetype)initWithHeight:(CGFloat)height;
- (instancetype)initWithXibClass:(Class)xibClass;
- (instancetype)initFromXib;
- (instancetype)commonInit;
- (void)setup;
- (void)layoutDone;
- (void)localize;
- (void)update;
- (NSLayoutConstraint *)constraintWithIdentifier:(NSString *)identifier;
- (void)setConstant:(CGFloat)constant forConstraintWithIdentifier:(NSString *)identifier;
@property (nonatomic) NSBundle *bundle;
@property (nonatomic) BOOL layoutIsDone;
@end
实施
#import "MLSXibLoadingView.h"
@implementation MLSXibLoadingView
- (instancetype)init
{
self = [super init];
if (self) {
self = [self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self = [self commonInit];
}
return self;
}
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
if (!self.subviews.count) {
return [self commonInit];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
if (!_layoutIsDone) {
_layoutIsDone = YES;
[self layoutDone];
}
}
- (instancetype)initWithWidth:(CGFloat)width
{
self = [self commonInitAssigningFrame:NO];
self.frame = [MLSUtils rect:self.frame scaledToWidth:width];
return self;
}
- (instancetype)initWithHeight:(CGFloat)height
{
self = [self commonInitAssigningFrame:NO];
self.frame = [MLSUtils rect:self.frame scaledToHeight:height];
return self;
}
- (instancetype)initWithXibClass:(Class)xibClass
{
return [self commonInitAssigningFrame:NO xibClass:xibClass];
}
- (instancetype)initFromXib
{
return [self commonInitAssigningFrame:NO];
}
- (instancetype)commonInit
{
return [self commonInitAssigningFrame:YES];
}
- (void)setup
{
}
- (void)layoutDone
{
}
- (void)localize
{
}
- (void)update
{
}
- (NSBundle *)bundle
{
return nil; // implement in subclasses
}
- (instancetype)commonInitAssigningFrame:(BOOL)assignFrame
{
return [self commonInitAssigningFrame:assignFrame xibClass:self.class];
}
- (instancetype)commonInitAssigningFrame:(BOOL)assignFrame xibClass:(Class)xibClass
{
NSBundle *b = [self bundle];
assert(b);
NSString *xibName = NSStringFromClass(xibClass);
assert([b pathForResource:xibName ofType:@"nib"]);
MLSXibLoadingView *xibView = [b loadNibNamed:xibName owner:nil options:nil][0];
xibView.frame = assignFrame ? self.frame : xibView.frame;
xibView.autoresizingMask = self.autoresizingMask;
xibView.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
for (NSLayoutConstraint *constraint in self.constraints) {
id firstItem = constraint.firstItem;
if (firstItem == self) {
firstItem = xibView;
}
id secondItem = constraint.secondItem;
if (secondItem == self) {
secondItem = xibView;
}
NSLayoutConstraint *newConstraint = [NSLayoutConstraint constraintWithItem:firstItem
attribute:constraint.firstAttribute
relatedBy:constraint.relation
toItem:secondItem
attribute:constraint.secondAttribute
multiplier:constraint.multiplier
constant:constraint.constant];
newConstraint.priority = constraint.priority;
newConstraint.identifier = constraint.identifier;
[xibView addConstraint:newConstraint];
}
return xibView;
}
- (NSLayoutConstraint *)constraintWithIdentifier:(NSString *)identifier
{
for (NSLayoutConstraint *constraint in self.constraints) {
if ([constraint.identifier isEqualToString:identifier]) {
return constraint;
}
}
return nil;
}
/**
* Use this method to change constraint constant with given identifier because constraints outlets get invalid
* after view exchange in commonInitAssigningFrame: method
*/
- (void)setConstant:(CGFloat)constant forConstraintWithIdentifier:(NSString *)identifier
{
for (NSLayoutConstraint *constraint in self.constraints) {
if ([constraint.identifier isEqualToString:identifier]) {
constraint.constant = constant;
break;
}
}
}
@end