Search code examples
iosiphoneswiftuiviewanimationtransition

adding a custom transition to presentViewController in swift


i have a viewController which has a TableView and a cell with a accessoryType of detailbutton. When pressing that accessoryType i present a semi transparent View with text.

I want this view to appear in a specific way so i created a custom view controller.

However trying to run that viewController in the detailbutton will not work.

parts of my code:

//Defined in the class
let customTransitionManager = TransitionManager()

//in tableView cellForRowAtIndexPath
if (tableView == continent){
        let cell:UITableViewCell = UITableViewCell(style:UITableViewCellStyle.Default, reuseIdentifier: "continentSelector")
        cell.textLabel?.text = "Continent"
        cell.accessoryType = UITableViewCellAccessoryType.DetailButton;
        return cell


func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath){
    println(indexPath)
    let detailController = storyboard?.instantiateViewControllerWithIdentifier("detailButtonStoryBoard") as? detailedView
    detailController?.modalPresentationStyle = .Custom
    detailController?.transitioningDelegate = customTransitionManager;
    presentViewController(detailController!, animated: true, completion: nil)
}

Running this code gives me the following error:

fatal error: unexpectedly found nil while unwrapping an Optional value

The following gets highlighted:

-> 0x100209cf0 <function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded, Arg[2] = Dead, Arg[3] = Dead> of Swift._fatalErrorMessage (Swift.StaticString, Swift.StaticString, Swift.StaticString, Swift.UInt) -> ()+44>: brk    #0x1

Solution

  • I see two places where you could have an nil value. The first is in the cell for row at index path, but that is unlikely. The second and more likely one is where you try to unwrap the detailContoller when presenting it. Have you confirmed that you are getting the controller back from the storyboard?

    UPDATE: The solution I put in the comments formatted horribly, here is a better format

    Found a solution Use a segue instead of presenting the controller manually.

    1. Call performSegue from the didSelectRowAtIndexPath
    2. in the prepareForSegue method put this:

      let toViewController = segue.destinationViewController as UIViewController
      toViewController.transitioningDelegate = self.customTransitionManager`
      

    Full credit goes to this tutorial: http://mathewsanders.com/animated-transitions-in-swift/#custom-transition-animations

    UPDATE 2: Downloaded your project and got it working. Here are the changes I made:

    1. I added a modal segue between the jeans controller and the detail controller. I called this 'DetailSegue'
    2. I updated the jeans.swift file accessoryButtonTappedForRowWithIndexPath method as follows:

      func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath){
          println(indexPath)
          self.performSegueWithIdentifier("DetailSegue", sender: self)
      }
      
    3. I then updated the TransitionManger animateTransition function as follows, basically making the to and from views optional and handling nil appropriately

      func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
          //get reference to our FROM View, To View, and container view that we should perform the transition in
          let container = transitionContext.containerView()
      
          let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
          let toView = transitionContext.viewForKey(UITransitionContextToViewKey)
      
          //setup from 2D transform that we'll use in the animation
          let offScreenRight = CGAffineTransformMakeTranslation(container.frame.width, 0)
          let offScreenLeft = CGAffineTransformMakeTranslation(-container.frame.width, 0)
      
          //start the view to the right of the screen 
          toView?.transform = offScreenRight
      
          //add both the views to our view controller
          if toView != nil {
              container.addSubview(toView!)
          }
      
          if fromView != nil {
              container.addSubview(fromView!)
          }
      
          // get the duration of the animation
          let duration = self.transitionDuration(transitionContext)
      
          //perform the animation
      
          UIView.animateWithDuration(duration, delay: 0.0, options: nil, animations: {
              fromView?.transform = offScreenLeft
              toView?.transform = CGAffineTransformIdentity
              }, completion: { finished in transitionContext.completeTransition(true)})
      }
      

    Here is a screencast of the final result: http://screencast.com/t/2mC07BLCC

    I hope this helps you move forward with your project.