在 .xib 中使用自定义 UIView 作为 IBOutlet
Posted
技术标签:
【中文标题】在 .xib 中使用自定义 UIView 作为 IBOutlet【英文标题】:Use Custom UIView as IBOutlet in .xib 【发布时间】:2019-10-18 10:20:09 【问题描述】:我有一个继承自 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 文件的方式 文件所有者的类是空的,没有出路
插座改为连接到视图,我为 .xib 文件的视图设置 StatusBarView
类,如下所示
现在在不同的 ViewController 中,我想将此类用作像这样的 IBOutlet,而不需要任何更多的初始化:
但结果只是一个灰色的视图。 有可能做这样的事情吗?如果是,请告诉我哪里出错了?
【问题讨论】:
有可能,但不是你这样做的方式。不要对self
进行评价并使用owner
@Sulthan 你能解释更多吗?我应该在哪里进行此更改?
@taratandel - 这看起来是一个非常好的分步教程:medium.com/@brianclouser/… ... 如果您想在 Storyboard 中布局视图时查看 xib 的按钮/内容,你还需要实现IBDesignable
@taratandel 简而言之,您不能从StatusBarView
的初始化程序加载类StatusBarView
。相反,您通常使用 self = [super init...]
创建 self
,然后从 nib 加载 view 并将其添加为 StatusBarView
实例的子视图。这意味着您必须将 self
作为所有者传递,并且在 xib 中,容器视图的类型为 UIView
,所有者的类型为 StatusBarView
。
@DonMag 感谢本教程正是我所需要的。
【参考方案1】:
从 XIB 加载自定义视图并不像看起来那么简单。我为此创建了一个自定义类,其中包含一堆方便的方法。因此,要完成这项工作,您必须让您的自定义视图从此类继承并为其设置 bundle
属性,默认情况下,您的类名应与 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
【讨论】:
以上是关于在 .xib 中使用自定义 UIView 作为 IBOutlet的主要内容,如果未能解决你的问题,请参考以下文章
如何将 UITableView 作为子视图添加到 xib 中的 UIView?