Resolved Subview's button loading but not showing

Discussion in 'iOS Programming' started by moonman239, Feb 9, 2017.

Tags:
  1. moonman239, Feb 9, 2017
    Last edited: Feb 13, 2017

    moonman239 macrumors 68000

    Joined:
    Mar 27, 2009
    #1
    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
    
        }
    
     
  2. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #2
  3. bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
    #3
    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
     
  4. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #4
    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.
     
  5. moonman239, Feb 11, 2017
    Last edited: Feb 16, 2017

    moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #5
    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
     
  6. bjet767, Feb 11, 2017
    Last edited: Feb 11, 2017

    bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
    #6
    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."
     
  7. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #7
    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.
     
  8. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #8
    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()
     
  9. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #9
    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.
     
  10. bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
    #10
    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.
     
  11. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #11
    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
    
        }
    
     
  12. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #12
    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.
     
  13. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #13
    Also, gentlemen, I've clarified what's happening in my first post.
     
  14. PhoneyDeveloper, Feb 13, 2017
    Last edited: Feb 13, 2017

    PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #14
    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.
     
  15. bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
    #15
    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.
     
  16. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #16
    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.
     
  17. bjet767, Feb 14, 2017
    Last edited: Feb 14, 2017

    bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
    #17
    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"];

    }





    }
     
  18. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #18
    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")
    
                }
    
            }
    
     
  19. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #19
    I just removed the background to see if that would help - it did not.
     
  20. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #20
    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.
     

Share This Page