Resolved UIButton not responding to touch events

Discussion in 'iOS Programming' started by moonman239, Apr 18, 2017.

  1. moonman239 macrumors 68000

    Joined:
    Mar 27, 2009
    #1
    I have a button that is showing but not responding to touches. This button is created in code, inside a UIView that is also created in code. On my device screen, the button appears to be off from where it is supposed to be, however, I cannot find any reason to suspect I (I'm a team of one) am at fault for this. I was unable to successfully use the view debugger. In case this is a bug, I have already filed a report with Apple. I already reinstalled Xcode. No views are set to overlap.

    Here's my code.

    Code:
    //
    //  MenuItemView.swift
    //  Reading Expressway
    //
    //  Created by Montana Burr on 8/10/16.
    //  Copyright © 2016 Montana. All rights reserved.
    //
    
    import UIKit
    
    enum ActivityStatus {
        case NotThereYet
        case Next
        case Completed
    }
     class MenuItemView: UIView {
        var currentStatus : ActivityStatus = ActivityStatus.NotThereYet
        var starNum : Int = 1
         let button: UIButton! = UIButton()
        @IBOutlet var starImageView: UIImageView? {
            didSet {
               
                // Load star.
                let mainBundle = Bundle(for: MenuItemView.self)
                let starURL = mainBundle.path(forResource: "\(starNum)star", ofType: "png", inDirectory:"pictures")
                let starImage = UIImage(contentsOfFile: starURL!)
                starImageView!.image = starImage!
                button.bringSubview(toFront: button)
                // Hide view
                self.starImageView!.isHidden = true
                // Set star so that it doesn't stretch.
                self.starImageView!.contentMode = UIViewContentMode.scaleAspectFit
            }
        }/** Star image view. Defined in storyboard **/
        func buttonPressed() {
            (superview as! ExerciseMenuView).buttonPressed(sender: self)
        }
        init(frame: CGRect,_tag: Int)
        {
            super.init(frame: frame)
                    tag = _tag;
                    starImageView = UIImageView(frame: bounds)
                    addSubview(starImageView!)
                    addPlayButton()
        }
        func addPlayButton() // Adds the "Play" button
        {
            // Add button to self.
            self.addSubview(button)
            // 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)
            button.isUserInteractionEnabled = false
            // 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 fit in view.
            let buttonOrigin = CGPoint(x:0, y:bounds.maxY)
            let buttonSize = CGSize(width: bounds.size.width, height: 128)
            button.frame = CGRect(origin: buttonOrigin, size: buttonSize)
            // Size button image to fit
            button.contentMode = UIViewContentMode.scaleAspectFit
        }
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
           addPlayButton()
        }
        override init(frame: CGRect)
        {
            super.init(frame: frame)
        }
    
        func setActivityStatus(newStatus : ActivityStatus) {
            self.isHidden = false
            currentStatus = newStatus
            switch newStatus {
            case .Next:
                button.isHidden = false
                button.isUserInteractionEnabled = true
            case .Completed:
                starImageView!.isHidden = false
                button.isUserInteractionEnabled = false
                button.isHidden = true
            default:
                button.isHidden = true
                starImageView!.isHidden = true
                break
               
            }
            // Log
            if (self.tag == 2)
            {
                print("setActivityStatus: Star image view superview is \(starImageView?.superview)")
            }
        }
        /*
        // Only override drawRect: if you perform custom drawing.
        // An empty implementation adversely affects performance during animation.
        override func drawRect(rect: CGRect) {
            // Drawing code
        }
        */
    }
    
     
  2. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #2
    OK, so maybe the problem isn't Interface Builder. I've added the view manually. Hopefully someone can tell me if I'm doing everything right. Here's my new view controller code:
    Code:
    //
    
    //  ExerciseMenuViewController.m
    
    //  Reading Expressway
    
    //
    
    //  Created by Montana Burr on 7/11/14.
    
    //  Copyright (c) 2014 Montana. All rights reserved.
    
    //
    
    
    #import "ExerciseMenuViewController.h"
    
    #import "LevelObject.h"
    
    #import "WordBuildingExerciseViewController.h"
    
    #import "Reading_Expressway-Swift.h"
    
    #import "TextCell.h"
    
    #import "OtherTableViewCell.h"
    
    #import "PlaceEntity+CoreDataProperties.h"
    
    #define ChildKey @"child"
    
    #define SectionKey @"section"
    
    #define WordFamilyObjKey @"wordObject"
    
    
    @interface ExerciseMenuViewController ()
    
    
    @end
    
    @implementation ExerciseMenuViewController
    
    {
    
        XMLDataObject *dataObject;
    
        PlaceEntity *entity;
    
        NSArray *menuViews;
    
        ExerciseMenuView *levelView; // The view corresponding to the current level.
    
    }
    
    @synthesize elements;
    
    - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    
    {
    
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    
        if (self) {
    
           
    
        }
    
        return self;
    
    }
    
    
    -(void)viewDidLoad
    
    {
    
        NSInteger level = [LevelObject currentLevel] + 1;
    
        // Load menu view corresponding to current level
    
        if (level == 1)
    
        {
    
            // Hard-code view
    
            levelView = [[ExerciseMenuView alloc] initWithFrame:self.view.frame];
    
            UIImageView *backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"level1homepic.png"]];
    
            backgroundView.frame = self.view.frame;
    
            levelView.backgroundImageView = backgroundView;
    
            [levelView addSubview:backgroundView];
    
            CGRect view0frame = CGRectMake(320, 491, 211, 128);
    
            MenuItemView *view0 = [[MenuItemView alloc] init];
    
            view0.frame = view0frame;
    
            [view0 addPlayButton];
    
            view0.tag = 0;
    
            [levelView addSubview:view0];
    
        }
    
        else {
    
        NSString *nibName = [NSString stringWithFormat:@"%i",level];
    
        NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
    
        levelView = [nibViews firstObject];
    
        [levelView setFrame:self.view.bounds];
    
        [self setMenuView:levelView];
    
       
    
        }
    
        [self.view addSubview:levelView];
    
        [levelView setViewController:self];
    
    }
    
    
    - (void)viewWillAppear:(BOOL)animated
    
    {
    
        [super viewWillAppear:animated];
    
        // Get place entity for current level
    
        entity = [[UserEntity sharedEntity] placeEntityForLevel:[LevelObject currentLevel]];
    
        elements = [self.element elementChildren];
    
        NSAssert([[elements.firstObject elementName] isEqualToString:@"wordFamily"], @"Expected word families.");
    
        // Pass relevant information to levelView
    
        levelView.wordFamilyIndex = entity.familyNum;
    
        levelView.activityIndex = entity.activityNum;
    
        levelView.viewController = self;
    
        [levelView update];
    
        }
    
    
    
    -(IBAction)goBack:(id)sender {
    
    [(AppViewController *)self.presentingViewController setElement:[self.element parentObject]];
    
    [self.navigationController popViewControllerAnimated:false];
    
    }
    
    
    -(IBAction)activityButtonPressed:(id)sender
    
    {
    
        NSMutableArray *segues = [[NSMutableArray alloc] init];
    
        ElementObject *wordFamily = [self.elements objectAtIndex:entity.familyNum]; // The object representing the current word family.
    
        ElementObject *activity; // Represents the current activity.
    
        if (wordFamily.elementChildren.count > 0)
    
        {
    
            activity = [wordFamily.elementChildren objectAtIndex:entity.activityNum];
    
        }
    
        else {
    
            activity = wordFamily;
    
        }
    
        [sender setElementObject:activity];
    
        if (![activity.elementName isEqualToString:@"storyRhyme"]) {
    
            [segues addObject:@"WordBuildingSegue"];
    
            ElementObject *currentWordFamily = wordFamily;
    
            NSString *wordFamilyName = [currentWordFamily.attributeDictionary valueForKey:@"name"];
    
            if ([entity level] > 0 || [wordFamilyName isEqualToString:@"at"]) {
    
                [segues addObjectsFromArray:@[@"WordBuildingSegue",@"SentenceBuildingSegue"]];
    
                }
    
            else
    
            {
    
                // ap
    
               
    
                [segues addObject:@"SentenceBuildingSegue"];
    
            }
    
            }
    
            else
    
            {
    
                [segues addObject:@"StoryRhymeSegue"];
    
            }
    
        NSString *segueIdentifier = [segues objectAtIndex:entity.activityNum];
    
        [self performSegueWithIdentifier:segueIdentifier sender:sender];
    
    }
    
    
    - (void)didReceiveMemoryWarning
    
    {
    
        [super didReceiveMemoryWarning];
    
        NSLog(@"Exercise menu view received memory warning");
    
        // Dispose of any resources that can be recreated.
    
    }
    
    
    @end
    
    And here's the code for the view that contains the view that in turn contains the button:
    Code:
    //
    
    //  MenuItemView.swift
    
    //  Reading Expressway
    
    //
    
    //  Created by Montana Burr on 8/10/16.
    
    //  Copyright © 2016 Montana. All rights reserved.
    
    //
    
    
    import UIKit
    
    
    enum ActivityStatus {
    
        case NotThereYet
    
        case Next
    
        case Completed
    
    }
    
     class MenuItemView: UIView {
    
        var currentStatus : ActivityStatus = ActivityStatus.NotThereYet
    
        var starNum : Int = 1
    
        let button: UIButton! = UIButton()
    
        @IBOutlet var starImageView: UIImageView! {
    
            didSet {
    
               
    
                // Load star.
    
                let mainBundle = Bundle(for: MenuItemView.self)
    
                let starURL = mainBundle.path(forResource: "\(starNum)star", ofType: "png", inDirectory:"pictures")
    
                let starImage = UIImage(contentsOfFile: starURL!)
    
                starImageView!.image = starImage!
    
                button.bringSubview(toFront: button)
    
                // Hide view
    
                self.starImageView!.isHidden = true
    
                // Set star so that it doesn't stretch.
    
                self.starImageView!.contentMode = UIViewContentMode.scaleAspectFit
    
            }
    
        }/** Star image view. Defined in storyboard **/
    
        func buttonPressed() {
    
            (superview as! ExerciseMenuView).buttonPressed(sender: self)
    
        }
    
        init(frame: CGRect,_tag: Int)
    
        {
    
            super.init(frame: frame)
    
                    tag = _tag;
    
                    starImageView = UIImageView(frame: bounds)
    
                    addSubview(starImageView!)
    
                    addPlayButton()
    
        }
    
        func addPlayButton() // Adds the "Play" button
    
        {
    
            // Add button to self.
    
            self.addSubview(button)
    
            // 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)
    
            button.isUserInteractionEnabled = false
    
            // 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 fit in view.
    
            let buttonOrigin = CGPoint(x:0, y:bounds.maxY)
    
            let buttonSize = CGSize(width: bounds.size.width, height: 128)
    
            button.frame = CGRect(origin: buttonOrigin, size: buttonSize)
    
            // Size button image to fit
    
            button.contentMode = UIViewContentMode.scaleAspectFit
    
        }
    
        required init?(coder aDecoder: NSCoder) {
    
            super.init(coder: aDecoder)
    
          addPlayButton()
    
        }
    
        override init(frame: CGRect)
    
        {
    
            super.init(frame: frame)
    
        }
    
    
        func setActivityStatus(newStatus : ActivityStatus) {
    
            self.isHidden = false
    
            currentStatus = newStatus
    
            switch newStatus {
    
            case .Next:
    
                button.isHidden = false
    
                button.isUserInteractionEnabled = true
    
            case .Completed:
    
                starImageView!.isHidden = false
    
                button.isUserInteractionEnabled = false
    
                button.isHidden = true
    
            default:
    
                button.isHidden = true
    
                starImageView!.isHidden = true
    
                break
    
               
    
            }
    
            // Log
    
            if (self.tag == 2)
    
            {
    
                print("setActivityStatus: Star image view superview is \(String(describing: starImageView?.superview))")
    
            }
    
        }
    
        /*
    
        // Only override drawRect: if you perform custom drawing.
    
        // An empty implementation adversely affects performance during animation.
    
        override func drawRect(rect: CGRect) {
    
            // Drawing code
    
        }
    
        */
    
    }
    
     
  3. bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
  4. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #4
    I tried using an XIB. It worked for a while, and then something happened unexpectedly. What you see here is an attempt to work around the previous issue.
    Anyways, I've been meaning to try setting the MenuItemView's userInteractionEnabled property to true. I'll see if that fixes it, and I'll post the results back ASAP.
    --- Post Merged, Apr 20, 2017 ---
    And...that did nothing.
     
  5. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #5
    You're supposed to use UIButton(type: theType) to create buttons, not UIButton(). You can set the frame or constraints later.
     
  6. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #6
    Thanks for the tip. I have another thread going on in Apple's developer forum . Someone on that forum mentioned that as well.

    Here's the link to the other thread: https://forums.developer.apple.com/message/224313#224313

    What's worse - if I go back to a previous build that uses XIBs, I see nothing - which is strange.
     
  7. bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
    #7
    So why aren't you using the Storyboard and then just link a button to what you visually add to your view?

    The only reason to build it from code is because you want it to not be part of the view when it appears.

    You do know any view can be made invisible and then appear when it is needed?

    The first rule of iOS programming is, when possible, to only code what is needed for functionality and appearances. Leave the layouts and segue building to the Storyboard. Even then most apps will grow to many lines of code.
     
  8. moonman239, Apr 24, 2017
    Last edited: Apr 24, 2017

    moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #8
    OK, so as I said before, I went back to using an XIB. Tried to fix my problem by adding the button in the XIB file (and subsequently commenting out code that adds the button), and I made sure to have the button show up inside the superview's frame in IB.
    Now, I changed the button's superview background color to "white" so I could see that the view was appearing on the screen.

    Edit: I realize now that I've missed a line that also triggers creation of a button. I've commented out and am running the app now, though, to see what happens.

    Edit #2: Also, sorry for all the threads I've created. My app seems to be so inexplicably erroneous that some of my codebase has increased in complexity.
     
  9. bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
    #9
    Moonman

    Are you relatively new to iOS programming? I'm asking you from a serious position and not to put you down.

    The reason I ask is because you seem to be doing things contrary to normal iOS app programming methodology.
    Apple has a huge document base with examples that covers much of what you keep posting about and stack overflow is a great place to search out solutions where others have gone before you.

    I have an app in beta which uses Bluetooth, location services, WatchKit, HealthKit, animation, iCloud for file storage, UIDocument for record saving, printing, user defaults, tab navigation, hidden views and a bit more. In it all I have never used or found the need to use an XIB, just plain old story boarding with uiviewcontrollers gets the job done. Yes I have added sub-views through coding and sub-layers for animation speed and have a number of NSObject based objects, but at the heart of it all is a Storyboard.

    What I'm encouraging you to do is embrace the Storyboard for laying out you app, from entry to the most complicated views. It's the Apple preferred method.

    Enjoy the adventure!
     
  10. moonman239 thread starter macrumors 68000

    Joined:
    Mar 27, 2009
    #10
    I actually fixed the issue. I noticed I was missing a call to [super viewDidLoad].
    In any case, I stick as close to XIBs as I can. In fact, most/all of my interface is built using a combination of XIBs and storyboards. It's when I don't know why something doesn't work when I tend to take a different path.
     
  11. bjet767 macrumors 6502a

    Joined:
    Oct 2, 2010
    #11
    OK, just seems a bit harder (more code) and, in my opinion, doesn't add any real flexibility or speed to the mix.

    The idea of UIViewControllers as the main workhorse is they do a whole lot of for you and that means less coding/typing.

    In OSX I was happy when they added storyboarding like iOS to the xCode interface. What it did in particular was separated out code which really should not have been included in certain files. My best example would be working with NSDocuments.

    Before OSX storyboarding the NSDocument included a whole bunch of interface stuff that really should have been in a view controller. With view controllers I now have almost a transferrable code between NSDocuments and UIDocuments so file compatibility is almost 100% (some of the data types, like images, are on fully compatible) and concentrates on the document's I/O while leave all the interface stuff to the View Controller.

    One possible down side to view controllers is it can be forgotten they include a view that is initialized with the controller. Making custom changes to the view are often not as obvious as when the view is initialized separately.


    However, to each his own and press on with your project!
     

Share This Page