0

I am currently building an application's UI without using IB or storyboard. I have the following hierarchy: 1. ViewController: which is the main view controller. 2. DrawerViewController: which is the view controller for the lower drawer menu. The main view controller adds as a subview the drawerViewController's view. 3. Add Button: this is a UIButton created and added programatically to the drawerViewController's view.

The problem is if I clicked on the add button, the application crashes, giving absolutely no error messages in the output, except (lldb).

I have tried the following:

  1. Change the selector name and the method name.
  2. Checked the name of the selector method.
  3. Added and removed the parameter of the selector method.
  4. Changed the name of the button.
  5. Added the button inside another view.

There's no way to trace the bug using breakpoints, because the application is compiling just fine, and the app crashes only if you click on the button.

Here's a link to a testing project I created to show the problem:

GitHub

ViewController:

class ViewController: UIViewController //, DrawerDelegate
{

//Lower Drawer.
var drawerView: DrawerView!

var drawerViewHidden: Bool = false
var addButton: UIButton!
var viewHeight: CGFloat! = 0
var viewWidth : CGFloat! = 0
var shownDrawerViewY: CGFloat!
var hiddenDrawerViewY: CGFloat!

init()
{
    super.init(nibName: nil, bundle: nil)
}

required init(coder aDecoder: NSCoder)
{
    super.init(coder: aDecoder)
}



override func viewDidLoad()
{
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    //Setup UI
    setUpUI()
}

override func viewWillAppear(animated: Bool)
{
    super.viewWillAppear(animated);


}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


//MARK:- UI initialization.
/**
Private function that setsup the UI.
*/
private func setUpUI()
{
    setUpDrawer()
}

//MARK: Drawer setup.

/**
A private function that programatically creataes a drawer and add it to the view.
*/
private func setUpDrawer()
{
    var controller = DrawerViewController(width: self.view.frame.width, height: self.view.frame.height)

    self.view.addSubview(controller.view)
    //Hide
    // toggleDrawer()
}



//MARK:- Delegates.

//MARK: DrawerDelegate methods.
func addButtonClicked(sender: UIButton)
{
    //  toggleDrawer()
}   
}

DrawerViewController

class DrawerViewController: UIViewController
{
    //Drawer variables.
    var drawerViewHidden: Bool = false
    var addPinDrawer: AddPinDrawer!



    var viewHeight: CGFloat! = 0
    var viewWidth : CGFloat! = 0
    var shownDrawerViewY: CGFloat?
    var hiddenDrawerViewY: CGFloat?

    var addButton: UIButton?

    init(width: CGFloat, height: CGFloat)
    {
        super.init(nibName: nil, bundle: nil)

        viewWidth  = width
        viewHeight = height

        setUpUI()
    }

    override func viewDidLoad()
    {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

    }

    override func viewWillAppear(animated: Bool)
    {
        super.viewWillAppear(animated)


    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


    required override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)
    {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    required init(coder aDecoder: NSCoder)
    {
        fatalError("init(coder:) has not been implemented")
    }


    //MARK:- UI setup
     func setUpUI()
    {
        setUpDimensions()
        setUpDrawerElements()
//        toggleDrawer()
    }

     func setUpDimensions()
    {
        //Determine the y in the case drawer is shown
        shownDrawerViewY  = viewHeight * 2.0/8.0

        //Determine the height of the drawer.
        let drawerHeight = viewHeight * 6.0/8.0

        //Determine the y in the case drawer is hidden
        hiddenDrawerViewY = viewHeight * 7.4/8.0

        //Create the frame, starting with the drawer shown.
        let frame = CGRectMake(0, shownDrawerViewY!, viewWidth, drawerHeight)
        //Create a new Drawer View.
        self.view = UIView(frame: frame)

        setUpAddButton(frame)



        //Setup the background image of the drawer.
        self.view.backgroundColor = UIColor(patternImage: UIImage(named: Constants.drawerBackgroundImage)!)
    }

    func setUpDrawerElements()
    {
        //Setup the button.
        setUpAddPinDrawer()
    }

    func setUpAddPinDrawer()
    {
        addPinDrawer = AddPinDrawer(frame: CGRectMake(0, 0, self.view.frame.width, self.view.frame.height))
//        self.view.addSubview(addPinDrawer)
    }



    //MARK: Handling drawer toggles
    func toggleDrawer()
    {
        //Toggle the variable.
        drawerViewHidden = !drawerViewHidden

        //If the drawer must be hidden.
        if drawerViewHidden
        {
            hideDrawer()
        }

        //If the drawer must be shown
        else
        {
            showDrawer()
        }
    }

    func hideDrawer()
    {
        //Hide the drawer
        UIView.animateWithDuration(0.6, animations: { () -> Void in
            self.view.frame.origin.y = self.hiddenDrawerViewY!
        })
      //  drawerView.hideDrawer()
    }

    func showDrawer()
    {
        UIView.animateWithDuration(0.6, animations: { () -> Void in
            self.view.frame.origin.y = self.shownDrawerViewY!
        })
      //  drawerView.showDrawer()
    }


    func setUpAddButton(frame: CGRect!)
    {
        //Determine the button dimensions.
        let width:CGFloat = 75.0//1.0/10.0 * viewHeight
        let x = frame.width/2.0 - width/2.0

        //Button background image.
        let background = UIImage(named: Constants.addButtonBackgroundImage)!

        //Create the button.
        addButton = UIButton(frame: CGRectMake(x, -width/2.0, width, width)) as UIButton
//        addButton!.setImage(background, forState: UIControlState.Normal)

        addButton?.backgroundColor = UIColor.yellowColor()

        //Add the event handler.
        addButton!.addTarget(self, action: "buttonAdd:", forControlEvents: .TouchUpInside)


        //Set it rotated.
//        self.addButton!.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_4))


        //Add the button to the subview.
        self.view.addSubview(addButton!)

//        println(addButton!.targetForAction("test", withSender: addButton)!)
    }

    func buttonAdd(sender: UIButton)
    {
        println("Helloooo asdf")
    }
}
Omar Kanawati
  • 59
  • 3
  • 7
  • your `DrawerViewController` is also in storyboard? try `fatalError("init(coder:) has not been implemented")` line in init(coder) to `super.init(coder: aDecoder)` – Vivek Molkar Jun 15 '15 at 13:02
  • Did you set a breakpoint? – nhgrif Jun 15 '15 at 13:07
  • @VivekMolkar DrawerViewController is not in the storyboard, nothing is in the storyboard except for the viewController class and it's totally empty in the story board. Everything is added programatically. – Omar Kanawati Jun 16 '15 at 06:39
  • @nhgrif As I have mentioned in the post, there's no use for setting up beakpoints, since the app compiles perfectly fine, and the app crashes only when you click on a button. – Omar Kanawati Jun 16 '15 at 06:40
  • I am asking if you set a breakpoint which was preventing the error message from being displayed (and therefore preventing anyone from getting any useful information). It seems most likely you have an exception breakpoint. If you took the effort to verify you had no breakpoints set, your application would carry through to the crash, and we'd get to see a useful error message. Moreover, breakpoints and compiling are completely unrelated. "The app crashes only when you click on a button" is a pretty good opportunity to use, if nothing else, a symbolic breakpoint. – nhgrif Jun 16 '15 at 11:59
  • Thank you for your feedback. No I wasn't setting any breakpoint. I have already solved the problem and posted the solution :) – Omar Kanawati Jun 17 '15 at 08:28

1 Answers1

-1

I have solved the problem. There's a very little mistake in the initiation of the DrawerViewController, In the main view controller, I am creating a variable called DrawerViewController and initiating its view. This variable will be deallocated once the method is done, which means all events will not be handled. Causing the app to crash since there's no method to handle the button's events.

The solution:

Make the DrawerViewController an instance variable and initialise it in the main view controller.

Omar Kanawati
  • 59
  • 3
  • 7
  • Had a look at your code, You can also do this by adding `DrawerViewController` as childviewcontroller in ViewController. In your `ViewController.viewDidLoad` after `self.view.addSubview(controller.view)` add following line `self.addChildViewController(controller)` – Vivek Molkar Jun 16 '15 at 07:31