Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IBOutlet isn't connected in awakeFromNib

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?

like image 519
Sofi Software LLC Avatar asked Aug 02 '11 02:08

Sofi Software LLC


4 Answers

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.

like image 129
ashack Avatar answered Nov 09 '22 20:11

ashack


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)
}
like image 7
Sethmr Avatar answered Nov 09 '22 19:11

Sethmr


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
like image 4
odyth Avatar answered Nov 09 '22 19:11

odyth


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"
        }
    }
}
like image 3
Nadzeya Avatar answered Nov 09 '22 20:11

Nadzeya