iOS Subview's button loading but not showing

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
I have a subview that is laid out in it's parent view's XIB. The subview has a button that's showing up in the view debugger but not on my device's screen. A logging statement says the button's frame is (0,0,240,128) - exactly as expected.Additionally, this is just one of many similar views, and their logging statements have the button sizes correctly set. Edit: In fact, when I break in layoutSubviews, the code debugger says the button's size is what I want it to be.
There are no Autolayout constraints.
Edit: Also, I don't manipulate the frame outside of layoutSubviews.
Edit #3:

OK, so just a clarification. Sorry for the missing details:

The "menu" is a bunch of Play buttons. These play buttons correspond to levels. When the user finishes a level, a different play button pops up. I looked into using a UICollectionView but decided it wouldn't give me the flexibility I wanted to choose where the buttons pop up.
Anyway, each button has a superview, one of which is the problem view. Now, I was able to dance around the issue by kind of swapping one view out for another, but the problem remains. I may be able to work around this by hand-coding the problem subview, but of course I really would like to know how to actually fix the problem.
Edit #2: Here is my new code:

Code:
    override func awakeFromNib() {

        super.awakeFromNib()

        // Give the button its image.

        let mainBundle = Bundle(for: MenuItemView.self)

        let playURL = mainBundle.path(forResource: "play-button", ofType: "png", inDirectory:"pictures")

    

        let playImage = UIImage(contentsOfFile: playURL!)

        button.setBackgroundImage(playImage, for: UIControlState.normal)

    

        // Add button to self.

        self.addSubview(button)

        // Respond to button taps

        button.addTarget(self, action: #selector(MenuItemView.buttonPressed), for: UIControlEvents.touchUpInside)

    }

    override func layoutSubviews() {

        super.layoutSubviews()

        // Make sure button doesn't accidentally resize

        button.translatesAutoresizingMaskIntoConstraints = false

        // Size button to fill view.

        button.frame = self.bounds

    }
 
Last edited:

bjet767

macrumors 6502a
Oct 2, 2010
967
313
I've looked over your code, I normally do not use Swift been an objective c for a very long time, but I think the problem is with how you have initialized the image for you button.

I suggest you just add the image to the apps resources and then use this:

button.setImage(UIImage(named: "play.png"), for: UIControlState.normal)

or

button.setBackgroundImage(UIImage(named: "play.png"), for: UIControlState.normal)

Don't use the URL from bundle, waste of time and CPU process.

The problem is probably your image is not loading properly onto your device and is not in the app's file bundle. The code, or something like it, is what I believe most people use.

BTW, stack overflow is your friend for questions like these.
http://stackoverflow.com
 

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
I've looked over your code, I normally do not use Swift been an objective c for a very long time, but I think the problem is with how you have initialized the image for you button.

I suggest you just add the image to the apps resources and then use this:

button.setImage(UIImage(named: "play.png"), for: UIControlState.normal)

or

button.setBackgroundImage(UIImage(named: "play.png"), for: UIControlState.normal)

Don't use the URL from bundle, waste of time and CPU process.

The problem is probably your image is not loading properly onto your device and is not in the app's file bundle. The code, or something like it, is what I believe most people use.

BTW, stack overflow is your friend for questions like these.
http://stackoverflow.com
Well, except that this view is, for all intents and purposes, a menu. I have other menus that I built and render in the same way, minus a few differences that don't seem to matter here.
 

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
I tried deleting and readding the vew in IB, as well as changing the background color to white - I still don't see my view.
Other answers suggest it's the size class, but the other menu views I mentioned have the exact same size, so the size class isn't relevant here.

Edit: I may have messed with the wrong view? But I think the statement stands
 
Last edited:

bjet767

macrumors 6502a
Oct 2, 2010
967
313
OK

Trying to solve someones coding issues is really difficult through these short blog threads.

After looking at you code again I don't see a "setNeedsLayout" or "layoutIfNeeded" call after you add the subview as a button.

It seems you want the button to be the size of the view it is a subview of.

Did you check to see if the subview actually is there by using the "didAddSubview" method?
How about the "Z" order of you views? The code looks correct but one never knows.

Did you add the first subview in the ViewController's "viewDidLoad" method?

I normally do not use "awakeFromNib" either, so here's some help from Stack (sorry not in Swfit but it will help give the idea bout the order of your Views:

http://stackoverflow.com/questions/...rom-nib-all-subviews-contained-in-nib-are-nil

"You may be misunderstanding how nib loading works. If you define a custom UIView and create a nib file to lay out its subviews you can't just add a UIView to another nib file, change the class name in IB to your custom class and expect the nib loading system to figure it out. You need to modify initWithCoder of your custom UIView class to programmatically load the nib that defines its subview layout. e.g.:

- (id)initWithCoder(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[[NSBundle mainBundle] loadNibNamed@"CustomView" owner:self options:nil];
[self addSubview:self.toplevelSubView];
}
return self;
}
Your custom view's nib file needs to have the 'File's owner' class set to your custom view class and you need to have an outlet in your custom class called 'toplevelSubView' connected to a view in your custom view nib file that is acting as a container for all the subviews. Add additional outlets to your view class and connect up the subviews to 'File's owner' (your custom UIView).

Alternatively, just create your custom view programmatically and bypass IB."
 
Last edited:

PhoneyDeveloper

macrumors 68040
Sep 2, 2008
3,114
93
Can you see the view using the debugger's view debugging? Also set the background color to yellow or something prominent so you can see the view for sure.

UIImage(named: )should work fine to load the image even though it's in a subfolder. It's unclear where the button is created, in code or in IB. If in IB I would setup the image there.
 

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
Can you see the view using the debugger's view debugging? Also set the background color to yellow or something prominent so you can see the view for sure.

UIImage(named: )should work fine to load the image even though it's in a subfolder. It's unclear where the button is created, in code or in IB. If in IB I would setup the image there.
I set the background color to white and still didn't see the view. I also ran the view debugger and saw it there.

The button is actually created in code:
Code:
let button: UIButton! = UIButton()
 

PhoneyDeveloper

macrumors 68040
Sep 2, 2008
3,114
93
If you can see it in the view debugger then you know the frame and the z order. It has an image or you can set a title. So why isn't it visible?

You're supposed to create buttons with UIButton(type: ) but probably the way you're doing it will also work. Obviously you need to set the frame later.
 

bjet767

macrumors 6502a
Oct 2, 2010
967
313
Can I ask why you're using the awakefromnib method in your code?

Why not just use a button initialzed dynamically after the uiviewcontroller has made the main view visible?

Are you trying to use the Storyboard to layout the subview locations and then use those locations as your buttons by sub viewing an uibutton over them?

You could just check for touches on those subviews and build some action when they are touched.
 

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
Can I ask why you're using the awakefromnib method in your code?

Why not just use a button initialzed dynamically after the uiviewcontroller has made the main view visible?

Are you trying to use the Storyboard to layout the subview locations and then use those locations as your buttons by sub viewing an uibutton over them?

You could just check for touches on those subviews and build some action when they are touched.
Here's an updated version of my code so we're all now on the exact same page:

Code:
 required init?(coder aDecoder: NSCoder) {

        super.init(coder: aDecoder)

        // Give the button its image.

        let mainBundle = Bundle(for: MenuItemView.self)

        let playURL = mainBundle.path(forResource: "play-button", ofType: "png", inDirectory:"pictures")

       

        let playImage = UIImage(contentsOfFile: playURL!)

        button.setBackgroundImage(playImage, for: UIControlState.normal)

       

        // Add button to self.

        self.addSubview(button)

        // Respond to button taps

        button.addTarget(self, action: #selector(MenuItemView.buttonPressed), for: UIControlEvents.touchUpInside)

        // Make sure button doesn't accidentally resize

        button.translatesAutoresizingMaskIntoConstraints = true

        // Size button to fill view.

        button.frame = self.bounds

    }
 

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
I set a breakpoint at the end of my viewController's viewWillAppear method and printed recursiveDescription. I then attempted to filter out irrelevant lines using the class name and was unable to find the problem view by its tag.
 

PhoneyDeveloper

macrumors 68040
Sep 2, 2008
3,114
93
It seems we're focusing on the button. What about the frame of the superview? It's possible for a subview to have a frame larger than its superview's frame and in that case it may not be visible.

Also, I assume you're not messing with the layer on any of these views. I ran into an odd circumstance today where adding a layer made a view invisible and it made me think about this issue you're having.
 
Last edited:

bjet767

macrumors 6502a
Oct 2, 2010
967
313
First of all I would not go about adding the button in the way you are doing it. Basically I think you have an issue with how Xcode memory management is placing you button and its image. I just caught it that you assign the image before you subview it and you set the button's frame afterwards.

I would suggest initialize the button's frame with the bounds of the view you are sub viewing it to. Then add the button as a subview after which assign your image to the button.

My code (I just did this with a view) might read like this:

I apologize for objectiveC tried Swift and prefer to stay with the old ways.

-(void)viewDidAppear{

UIButton *myButton = [[UIButton alloc]initWithFrame:_subview.bounds];
[_subView addSubview:myButton];

[myButton setImage:[UIImage imageNamed: @"myimage.png"] imageforState:UIControlStateNormal];

...

}

I typed this by hand on iPad and haven't checked for errors, but that's the idea if I had to build a button on the fly in my view controller.

Normally I really like, and recommend using the storyboard to lay this kind of stuff out visually.

So I guesss I'm out of ideas and help for you. You'll discover the issue soon and it could be as simple as a misspelled file name for your image, which would cause it not to appear.

Good luck and let us know how it turns out.
 

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
It seems we're focusing on the button. What about the frame of the superview? It's possible for a subview to have a frame larger than its superview's frame and in that case it may not be visible.
Normally I'd agree, but in this instance I checked the frames in IB and didn't see a problem.
Having said that, I decided to erase the view from the XIB and simply add the view in code.
 

bjet767

macrumors 6502a
Oct 2, 2010
967
313
I know many still use XIB files, but with Storyboard and ViewControllers that has basically become uneeded.

I quit XIB years ago, but can I ask why you are still using the file especially with Swift?

I ask to learn something and not to put down your practice.

Thanks!

BTW here's the complete code to add a clear drawing layer over an imageview that was storyboarded over the image. I subclassed an UIView and have it acts as a "shapelayer" to use the graphics speed of the shapelayer for drawing through finger touch.

_myImageView was linked to an UIImageView in the Storyboard for this ViewController. The compiler voa the link does all the subclassing for _myImageView and all I have to do is subclass another UIView over the top of that one to set up the drawing function of my DrawingView.

the _drawTextItem is a button in a bar that allows selection of the drawing feature of my drawing view. It is initialized as being off.

Overall my goal is to let Xcode do as much work for me as possible.

@implementation FirstViewController


- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.





}



-(void)viewDidAppear: (BOOL)animated

{



myDrawingView = [[DrawingView alloc] initWithFrame:_myImageView.bounds];



[self.view addSubview:myDrawingView];

if(!myDrawingView.draw)

{

_drawTextItem.image = [UIImage imageNamed:"No Edit_000000_25.png"];

}





}
 
Last edited:

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
OK, problem not fixed. I put some assertions into the main view that are supposed to ensure the view is being drawn where it should be, but the problem view seems to be passing all of them. Here's the loop that has the assertions:
Code:
 for itemView in menuItemViews{

            let itemTag = itemView.tag

            var checkVisible = true

            if placeTag > itemTag {

                itemView.setActivityStatus(newStatus: ActivityStatus.Completed)

            }

            else if placeTag == itemTag {

                itemView.setActivityStatus(newStatus: ActivityStatus.Next)

            }

            else

            {

                checkVisible = false

                itemView.setActivityStatus(newStatus: ActivityStatus.NotThereYet)

            }

            if (checkVisible)

            {

                // Assert that this view is visible .

                let isInBounds = self.bounds.contains(itemView.frame)

                assert(isInBounds, "View \(itemTag) not in bounds.")

                let isZeroWidth = itemView.frame.size.width == 0

                assert (!isZeroWidth, "Width of view \(itemTag) is zero")

                let isZeroHeight = itemView.frame.size.height == 0

                assert(!isZeroHeight, "Height of view \(itemTag) is zero")

                let isHidden = itemView.isHidden

                assert (!isHidden, "Item view \(itemTag) is hidden")

                let isObscured = false // indicate whether view is obscured by image view

                assert (!isObscured, "Item view \(itemTag) is obscured")

            }

        }
 

moonman239

macrumors 68000
Original poster
Mar 27, 2009
1,524
22
OK, so turns out I had forgotten to add the subview. Now, the button displays properly, but not the star view - which is an issue for another thread.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.