简体   繁体   中英

Use Custom UIView as IBOutlet in .xib

I have a custom class inherited from 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

and this is a part of implementation

@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;
}
}

and this is how I managed the.xib file File's Owner's class is empty and there is no outlet for that

在此处输入图像描述

在此处输入图像描述

The outlets are connected to the view instead and I set StatusBarView class for the.xib file's view like below

在此处输入图像描述

在此处输入图像描述

Now In different ViewController I want to use this class as an IBOutlet like This without needing any more initialisation: 在此处输入图像描述

but the result is just a gray view. is it possible to such do such things? if yes please tell me where I'm going wrong?

Loading custom views from XIB is not as simple as it would seem to be. I created a custom class for that containing a bunch of convenience methods. So to make this work you have to let your custom view inherit from this class and set bundle property for it, also by default your class name should be the same as XIB name

Interface

#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

Implementation

#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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM