IBOutlet isn't connected in awakeFromNib

2019-04-17 22:20发布

问题:

The sample code linked here works great and allows a UIScrollView to display images with paging and preview of the images before and after the current image:

  • http://blog.proculo.de/archives/180-Paging-enabled-UIScrollView-With-Previews.html
  • https://github.com/alexrepty/Paging-Enabled-UIScrollView-With-Previews

I'm trying to encapsulate the example in a single control that can be reused. So I created a PagingScrollView:

@interface PagingScrollView : UIView {
    IBOutlet UIScrollView * scrollView;
}

@property (nonatomic,retain) IBOutlet UIScrollView * scrollView;

@end

with dirt simple implementation

- (void) awakeFromNib
{
    [super awakeFromNib];

    NSLog(@"awake from nib");
}

In PagingScrollView.xib, I put a View containing a UIScrollView and a ARScrollViewEnhancer exactly as in the original ScrollViewPagingExampleViewController xib. Its File's Owner's class is set to PagingScrollView, and its scrollView outlet is set to the child UIScrollView.

In ScrollViewPagingExampleViewController, I simply declare:

IBOutlet PagingScrollView   *pagingScrollView;

And in IB, I drag a View, set its class to PagingScrollView, and hook up its pagingScrollView outlet to the PagingScrollView.

In awakeFromNib, the scrollView property is nil. I would think that since the scrollView is hooked up in IB in the same nib, it would be available by this point.

Confusingly, in ScrollViewPagingExampleViewController.xib, there is an empty outlet named scrollView. This might indicate that there is a different instance of PagingScrollView than the one defined in PagingScrollView.xib.

As a result, I can't populate the UIScrollView with the actual child views. What am I doing wrong here?

回答1:

There is a very good answer to this question here: Accessing View in awakeFromNib?

Long story short, the view may be loaded in awakeFromNib, but its contents are loaded lazily, which is why you should use viewDidLoad instead of awakeFromNib for what you are trying to achieve.



回答2:

For anyone finding this looking for the appropriate answer in Swift 4, here you go.

override func awakeAfter(using aDecoder: NSCoder) -> Any? {
    guard subviews.isEmpty else { return self }
    return Bundle.main.loadNibNamed("MainNavbar", owner: nil, options: nil)?.first
}

Although, I found the real secret to be that you don't subclass the main view of a .xib file. Instead make the file owner the class you want it to be and add the Nib like so inside of the classes init:

let bundle: Bundle = Bundle(for: type(of: self))
let nib: UINib = UINib(nibName: "<Nib File Name>", bundle: bundle)
if let view = nib.instantiate(withOwner: self, options: nil).first as? UIView {
    view.frame = bounds
    view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    view.translatesAutoresizingMaskIntoConstraints = true
    addSubview(view)
}


回答3:

I was having this problem and like you the other articles didn't apply. I had a ViewController, which had its own view. In that ViewController's .xib file I put a view and gave it the class of CustomView. This would work great except for the fact that CustomView was defined in its own .xib file, so naturally in awakeFromNib all the IBOutlets were nil. This is because there is no way in interface builder to link .xib files together. So when my ViewController's view gets put on screen it tries to instantiate CustomView, rather than loading the CustomView.xib file.

There is a work around detailed here http://cocoanuts.mobi/2014/03/26/reusable/, but the gist is I had to implement the following in my CustomView class

@implementation CustomView

    - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
    {
        if (![self.subviews count])
        {
            NSBundle *mainBundle = [NSBundle mainBundle];
            NSArray *loadedViews = [mainBundle loadNibNamed:@"CustomView" owner:nil options:nil];
            return [loadedViews firstObject];
        }
        return self;
    }

@end


回答4:

Using Swift you can use item inside your custom view after it's set:

class PointsTableViewCell: UITableViewCell {

    @IBOutlet weak var pointsTitleLabel: UILabel! {
        didSet {
            self.pointsTitleLabel.text = "Points"
        }
    }
}