Swift - Assign a NIB to self in class

2019-02-14 14:05发布

问题:

I am creating a UIView subclass for a notification dropdown banner. I am using a XIB to build out the view and want to assign that xib to the class when it initializes (i.e. avoiding having to do this from the calling ViewController).

Since you can't assign to 'self' in swift, how do I properly do this from within the class itself?

class MyDropDown: UIView
{
     func showNotification()
     {
          self = UINib(nibName: nibNamed, bundle: bundle).instantiateWithOwner(nil, options: nil)[0] as? UIView
     }
}

回答1:

For anyone looking on how to initialize a xib from it's own class in swift, here is the best approach using a custom class initializer:

class MyCustomView: UIView
{
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!

    class func initWithTitle(title: String, image: UIImage? = nil) -> MyCustomView
    {
        var myCustomView = UINib(nibName: "MyCustomView", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as? MyCustomView

        myCustomView.titleLabel.text = title

        if image != nil
        {
            myCustomView.imageView.image = image
        }

        //...do other customization here...
        return myCustomView
    }
}


回答2:

My usual approach is the following:

  1. Set my custom-view class as FileOwner in the nib
  2. Set all required outlets and actions
  3. Set outlet to the view in nib and name it 'contentView' in the class
  4. In the init* method - instantiate nib with owner and add subview to my custom-view object.

Here is the example of the collection header view implemented this way.

@interface Header : UITableViewHeaderFooterView

@property (nonatomic) IBOutlet UIView *contentView;

@property (weak, nonatomic) IBOutlet UILabel *labelTitle;
// other required outlets & actions
// ... 

@end


#import "Header.h"

@implementation Header

- (id)initWithReuseIdentifier:(NSString *)reuseIdentifier
{
    if (self = [super initWithReuseIdentifier:reuseIdentifier]) {
        [[NSBundle mainBundle] loadNibNamed:@"Header"
                                      owner:self
                                    options:nil];
        [self.contentView addSubview:self.content];

        UIView *subview = self.content;
        self.content.translatesAutoresizingMaskIntoConstraints  = NO;
        NSDictionary *viewsDictionary =  NSDictionaryOfVariableBindings(subview);
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|" options:0 metrics: 0 views:viewsDictionary]];
        [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[subview]|" options:0 metrics: 0 views:viewsDictionary]];

    }
    return self;
}

@end

// UPDATE Swift example

The main idea is not to load view from the nib directly to self but add it /view from nib/ as a subview to the self

import UIKit

class CustomView: UIView {

    @IBOutlet var contentView: UIView!
    @IBOutlet weak var labelTitle: UILabel!

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.__loadContent()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.__loadContent()
    }

    private func __loadContent() {
        // create nib
        let nib = UINib(nibName: "CustomView", bundle: NSBundle.mainBundle())
        // load outlets to self
        nib.instantiateWithOwner(self, options: nil)
        // add content view as a subview
        self.addSubview(self.contentView)
        // disable autoresizing
        self.contentView.setTranslatesAutoresizingMaskIntoConstraints(false)
        // manually create constraints to fix content view inside self with 0 padding
        let viewsDict: NSDictionary = ["content": self.contentView]
        let vertical = NSLayoutConstraint.constraintsWithVisualFormat("V:|[content]|",
            options: NSLayoutFormatOptions.allZeros,
            metrics: nil,
            views: viewsDict)
        let horizontal = NSLayoutConstraint.constraintsWithVisualFormat("H:|[content]|",
            options: NSLayoutFormatOptions.allZeros,
            metrics: nil, views: viewsDict)
        self.addConstraints(vertical)
        self.addConstraints(horizontal)
    }
}