Search code examples
iosswiftios9uipickerviewscreen-rotation

Programmatically create an UIPickerView that works in landscape and portrait


I wanted to create a smallest UIPickerView that looks good in portrait and landscape format, and it worked ... sort of. I add the pickerView after pushing a button, and hide it when done. The pickerView works correctly:

  • When I start the application in portrait or in landscape and push button without changing orientation
  • When I have the pickerView on screen, changing the orientation works fine, the pickerView change size

But it doesn't work well when launching in one orientation and changing orientation before clicking the button to show the pickerView, it then displays this sort of things: in portait orientation in paysage orientation

Here is the code (Single View Application, with just a button in the storyboard : myButton, with touch down action touchMyButton)

class myViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {

@IBOutlet weak var myButton: UIButton!
var myPickerView: UIPickerView! = UIPickerView(frame:CGRectMake(0.0, 0.0, UIScreen.mainScreen().bounds.width, 162))
var activePickerView: Int = 0

override func viewDidLoad() {
    super.viewDidLoad()
    myPickerView.tag = 1
    myPickerView.delegate = self
    myPickerView.autoresizingMask = [.FlexibleWidth]
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}


@IBAction func touchMyButton(sender: AnyObject) {
    if (activePickerView == 0)
    {
        activePickerView = myPickerView.tag
        myPickerView.selectRow(5, inComponent:0, animated:false)
        myPickerView.backgroundColor = UIColor.init(white:0.80, alpha:0.80)
        myPickerView.frame = CGRectMake(0.0, 100.0, UIScreen.mainScreen().bounds.width, 0.0)
        self.view.layoutIfNeeded()
        self.view.addSubview(myPickerView)
        myPickerView.setNeedsDisplay()

        UIView.animateWithDuration(0.5, delay:0.0, options:UIViewAnimationOptions.CurveEaseInOut, animations:{
            self.view.layoutIfNeeded()
            self.myPickerView.frame = CGRectMake(0.0, 100.0, self.myPickerView.frame.width, 162)
            }, completion: { _ in
        })
    }
    else
    {
        self.selectPickerView()
    }
}

func selectPickerView(){
    if (activePickerView == myPickerView.tag)
    {
        let _ = myPickerView.selectedRowInComponent(0)
        let framePV = self.myPickerView.frame

        UIView.animateWithDuration(0.5, delay:0.0, options:UIViewAnimationOptions.CurveEaseInOut, animations:{
            self.view.layoutIfNeeded()
            self.myPickerView.frame = CGRectMake(0.0, framePV.origin.y, framePV.width, 0.0 )
            }, completion:{ _ in
                self.myPickerView.removeFromSuperview()
        })
    }
    activePickerView = 0
}

// MARK: UIPickerViewDataSource methods
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int
{   return 1    }

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
{   return 10   }

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
{   return "Row \(row)" }

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    activePickerView = pickerView.tag
    selectPickerView()
}


override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation)
{   self.view.layoutIfNeeded()  }
}

I'm lost, I don't see what I can change to make it work


Solution

  • Thanks, I did it with constraint (it's just not very simple), like that

    import UIKit
    
    class myViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
    
    @IBOutlet weak var myButton: UIButton!
    var myPickerView: UIPickerView! = UIPickerView()
    var activePickerView: Int = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        myPickerView.tag = 1
        myPickerView.delegate = self
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    @IBAction func touchMyButton(sender: AnyObject) {
        if (activePickerView == 0){
            activePickerView = myPickerView.tag
            myPickerView.selectRow(5, inComponent:0, animated:false)
            myPickerView.backgroundColor = UIColor.init(white:0.80, alpha:0.80)
            myPickerView.translatesAutoresizingMaskIntoConstraints = false
    
            self.view.addSubview(myPickerView)
            let margins = self.view.layoutMarginsGuide
    
            let topC = myPickerView.topAnchor.constraintEqualToAnchor(topLayoutGuide.topAnchor, constant: 100.0)
            let leadingC = myPickerView.leadingAnchor.constraintEqualToAnchor(margins.leadingAnchor)
            let trailingC = myPickerView.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor)
            let heightC = myPickerView.heightAnchor.constraintEqualToConstant(0.0)
            heightC.identifier = "height"
            NSLayoutConstraint.activateConstraints([topC, leadingC, trailingC, heightC])
    
            self.view.layoutIfNeeded()
            myPickerView.setNeedsLayout()
            heightC.constant = 162.0
    
            UIView.animateWithDuration(0.5, delay:0.0, options:UIViewAnimationOptions.CurveEaseInOut, animations:{
                self.view.layoutIfNeeded()
                }, completion: { _ in
            })
        }
        else {
            self.selectPickerView()
        }
    }
    
    func selectPickerView(){
        if (activePickerView == myPickerView.tag)
        {
            let _ = myPickerView.selectedRowInComponent(0)
    
            for constraint in myPickerView.constraints
            {
                if constraint.identifier == "height"
                {   constraint.constant = 0.0   }
            }
            myPickerView.setNeedsLayout()
    
            UIView.animateWithDuration(0.5, delay:0.0, options:UIViewAnimationOptions.CurveEaseInOut, animations:{
                self.view.layoutIfNeeded()
                }, completion:{ _ in
                    self.myPickerView.removeConstraints(self.myPickerView.constraints)
                    self.myPickerView.removeFromSuperview()
            })
        }
        activePickerView = 0
    }
    
    // MARK: UIPickerViewDataSource methods
    func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int
    {   return 1    }
    
    func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
    {   return 10   }
    
    func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
    {   return "Row \(row)" }
    
    func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        activePickerView = pickerView.tag
        selectPickerView()
    }
    
    }
    

    I don't like the animation that discards the pickerView (I don't know why it isn't the exact opposite of the one to display it), but it works. I'll probably change it using an opaque view to cover the pickerViewin the future.