IBOutlet isn't connected in awakeFromNib
Asked Answered
L

4

30

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:

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?

Lipstick answered 2/8, 2011 at 2:18 Comment(2)
This is not related to your question but I implemented the github project you've provided in a different way. I think you might be interested. It's uploaded at (github.com/aakash272/pagingScroll)Amuse
Did you ever find a solution to this problem, Sofi? I have a similar problem where I've got a ViewController and an associated View, but am trying to load a subview I've built in Interface Builder from a xib into my parent view w/ controller. The outlets for the subview are always null even during the ViewWillAppear in the view controller for the parent view. However, the default values for the controls do show up at runtime. I just haven't found a way to initialize them.Blitzkrieg
S
14

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.

Song answered 3/8, 2011 at 13:59 Comment(5)
A UIView doesn't have viewDidLoad, this is a method in UIViewController. In my example code above, PagingScrollView is a UIView. Perhaps the question comes down to, how do you embed a custom control that uses IBOutlets inside a custom control that also has IBOutlets.Lipstick
I'd never seen this behaviour on any project ... until I started using Storyboards. It seems to defeat the purpose of the awakeFromNib method? Very odd.Irina
Hey Adam, I ran into exactly the same issue now. Objects containing IBOutlets that are nil in awakeFromNib when using the object in a storyboard while with regular nibs it worked fine.Presurmise
This answer is absolutely correct. Never, ever do animations in awakeFromNib, it's that simple. It won't work. the view in question is still being animated, etc, as part of it's coming to life. (It makes no difference at all if using storyboards, etc, it is a normal part of iOS development since time immemorial.) Regarding Sofi's question, yes Views (as such) do not have a viewDidLoad method. In that situation where you "don't have" a view controller, you must manually call a method you have written (like "youAreLoadedPleaseRunMyAnimationsNow") from whatever is the boss of the view.Knoll
@SofiSoftwareLLC I struggled with essentially the same problem and was able to solve it using this video: youtube.com/watch?v=FAxtWtqeMIMGuesthouse
V
7

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)
}
Valenti answered 29/11, 2017 at 17:55 Comment(0)
S
4

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
Shetrit answered 21/6, 2015 at 20:55 Comment(0)
A
3

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"
        }
    }
}
Adlay answered 21/3, 2018 at 14:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.