Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Button labels disappearing in XIB-backed view

I have a serious issue with a view that is defined by a XIB-file. It's a simple numeric entry pad with buttons for 0-9:

Screenshot

The problem is that in rare cases (as can be seen in the screenshot above), a button label is missing. It's not always the same label (in the example above, the "0" is missing). Usually closing the view and recreating it fixes the problem.

The XIB file's buttons are attached to UIButton controls in the view. The view is constructed like this:

@property (nonatomic,strong) IBOutlet UIButton *button0;
// etc...
@property (nonatomic,strong) IBOutlet UIButton *button9;

- (id)initWithFrame:(CGRect)frame
{
    self = [[UINib nibWithNibName:[[self class] description] bundle:nil]
                instantiateWithOwner:nil 
                options:nil
           ][0];

    if (self)
    {
        self.frame = frame;

        // ...
    }

    return self;
}

There's no controller involved and I also don't want that.

The view is created in code like this:

CustomView *view = [[CustomView alloc] initWithFrame:frame];

When the issue occurs (which is not often and random), and a user taps on the button with the missing label, the titleLabel.text property of the button is nil:

- (IBAction) numberButtonPressed:(UIButton *)button
{
   NSLog("button label = %@",button.titleLabel.text);

   // output: button label = (null)
}

The XML for the XIB file:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="5056" systemVersion="13C64" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
    <dependencies>
        <deployment defaultVersion="1536" identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3733"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view contentMode="scaleToFill" id="1" customClass="AmountEntryView">
            <rect key="frame" x="0.0" y="0.0" width="242" height="345"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="3" customClass="UIButton">
                    <rect key="frame" x="0.0" y="56" width="80" height="56"/>
                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                    <color key="backgroundColor" white="0.80112777219999998" alpha="1" colorSpace="calibratedWhite"/>
                    <fontDescription key="fontDescription" type="boldSystem" pointSize="40"/>
                    <state key="normal" title="7">
                        <color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
                        <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
                    </state>
                    <connections>
                        <action selector="numberButtonPressed:" destination="1" eventType="touchUpInside" id="142"/>
                    </connections>
                </button>

                <!-- ALL OTHER BUTTONS (omitted, as they are identical) -->

                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="0.00" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="107">
                    <rect key="frame" x="0.0" y="0.0" width="242" height="56"/>
                    <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                    <color key="backgroundColor" white="0.91557459679999997" alpha="1" colorSpace="calibratedWhite"/>
                    <fontDescription key="fontDescription" type="boldSystem" pointSize="32"/>
                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
                    <nil key="highlightedColor"/>
                </label>
            </subviews>
            <color key="backgroundColor" white="0.72580645160000001" alpha="1" colorSpace="calibratedWhite"/>
            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
            <connections>
                <outlet property="button0" destination="86" id="108"/>
                <outlet property="button1" destination="58" id="109"/>
                <outlet property="button2" destination="60" id="110"/>
                <outlet property="button3" destination="59" id="111"/>
                <outlet property="button4" destination="30" id="112"/>
                <outlet property="button5" destination="32" id="113"/>
                <outlet property="button6" destination="31" id="114"/>
                <outlet property="button7" destination="3" id="115"/>
                <outlet property="button8" destination="19" id="116"/>
                <outlet property="button9" destination="8" id="117"/>
                <outlet property="buttonBack" destination="105" id="118"/>
                <outlet property="buttonDone" destination="106" id="119"/>
                <outlet property="buttonDot" destination="94" id="120"/>
                <outlet property="labelAmount" destination="107" id="133"/>
            </connections>
        </view>
    </objects>
</document>

I have no clue why this issue is occurring and I can't find anything similar reported here or anywhere else on the web.

like image 791
Philippe Leybaert Avatar asked Nov 10 '22 03:11

Philippe Leybaert


2 Answers

When setting the labels use:

[self.button0 setTitle:@"0" forState:UIControlStateNormal];

not:

self.button0.titleLabel.text = @"0";

This method will not retain the title set, i.e. the title disappears after the device is rotated etc.

Other possible reasons:

  • Reduce the size of the font in the UIButtons. If a user uses the settings to use large dynamic font size, then the text may become to large to fit.

  • Set up the UIView in code without the XIB.

Sidenote: You may also refer to the buttons by tags, then your code will become simpler as you don´t have to connect each button individually.

like image 125
Sverrisson Avatar answered Nov 15 '22 11:11

Sverrisson


You're problem is really weird as I said in the comments, but this not the first time that I need to recreate a xib from scratch, cause of some strange problems.
Once someone told me to do not swap self inside the init method, even if I don't think this will solve the problem I'd like to give you two different ways to load xibs.
The first one:
Probably is the one that you can try just changing one line of code. Supposing that in your xib the view has as custom class CustomView and the outlets are connected here and File owner is nil. Try to load that like this:

CustomView *view =[[UINib nibWithNibName:[NSStringFromClass(self.class) bundle:nil] instantiateWithOwner:nil options:nil][0];

This will trigger the loading process from a xib and it takes its root object, is the same that probably used with cell prototypes in table views. If you need to do some sort of initialization do it inside -awakeFromNib 'cause initWithFrame will not be called.
The proper method to get the class name is not -description since it just gaves info about the object, in the future it can change (unlikely), the is the NSStringFromClass to get just the class name as a string.
The second:
Create a xib, with the file's owner set to an instance of CustomView, make your connect with the fil's owner, load the xib and add it as a subview.
Here is a small class that I use as an abstract class to create view that uses a xib.

@interface NibbedView : UIView
@property (strong, nonatomic) IBOutlet UIView * nibView;

- (instancetype) initWithNibName: (NSString*) nibViewName;
- (instancetype) initWithFrame:(CGRect)frame  andNibName: (NSString*) nibViewName;

- (void) loadNibWithName: (NSString*) nibName;


@end

and implementation:

@interface NibbedView ()
@end

@implementation NibbedView

-(void) setContstraints {
    _nibView.translatesAutoresizingMaskIntoConstraints = NO;
    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_nibView);
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_nibView]|" options:0 metrics: 0 views:viewsDictionary]];
    [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_nibView]|" options:0 metrics: 0 views:viewsDictionary]];
}

- (instancetype) initWithNibName: (NSString*) nibViewName{
    if (self = [self initWithFrame:CGRectZero]) {
        if (nibViewName) {
            [self loadNibWithName:nibViewName];
        }

    }
    return self;
}

- (instancetype) initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        //
    }
    return self;
}

- (instancetype) initWithFrame:(CGRect)frame  andNibName: (NSString*) nibViewName{
    if (self = [self initWithFrame:frame]) {
        if (nibViewName) {
            [self loadNibWithName:nibViewName];
        }
    }
    return self;
}

- (void) loadNibWithName: (NSString*) nibName {
    if (_nibView) {
        [_nibView removeFromSuperview];
    }
    else {
        [[NSBundle mainBundle] loadNibNamed: nibName owner: self options: nil];
        _nibView.frame = self.bounds;
        [self addSubview:_nibView];
        [self setContstraints];
    }

}


@end

It uses auto layout to make the xib view to stretch all over the CustomView root view.
As I said try in the debugger to make a -recursiveDescription of the root view, even if is not connected maybe is somewhere in the hierarchy.

like image 37
Andrea Avatar answered Nov 15 '22 11:11

Andrea