add UIPickerView as subview, very weired - ios

recently I need a solution to show a present-like pickerView for choosing data for some property, like profile.
what I did is
1, create a empty project
2, create a AViewController which will be used as subview. And I add a pickerView and a navigation bar into that view and adjust the view's height to around 260.
3, write code in that AViewController's class.
class APickerViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
#IBOutlet weak var pickerView:UIPickerView!
let dataList = [["a","b","c","d"]]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//pickerView.dataSource = self
//pickerView.delegate = self
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return dataList.count
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dataList[component].count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return dataList[component][row]
}
4, write code in Main ViewController to add the AViewController's View as subview.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let pickerViewController = storyboard?.instantiateViewController(withIdentifier: String(APickerViewController.self)) as! APickerViewController
self.view.addSubview(pickerViewController.view)
self.view.bringSubview(toFront: pickerViewController.view)
pickerViewController.view.frame = CGRect(x: 0, y: self.view.frame.height - 300, width: self.view.frame.width, height: 300)
}
and also one question here actually I'm not that sure the difference.
A:
let pickerViewController = storyboard?.instantiateViewController(withIdentifier: String(APickerViewController.self)) as! APickerViewController
B:
let pickerViewController = APickerViewController()
What's the different of A and B ? If I do some #IBOutlet settings by Interface Builder will be affected or what ?
5, go and ... get weired ...
the function
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dataList[component].count
}
sometime's get called many times and sometimes not get called.
and the data was not show in pickerView and sometimes only show row 1,2 and no others and if I flick up or down those 1,2 row data disappeared.
if anyone have a better solution of using pickerView as option popup window ?
Help.
Thanks

class EditViewController: UIViewController,UITextFieldDelegate,UIPickerViewDelegate{
var picker : UIPickerView = UIPickerView()
var toolBar : UIToolbar = UIToolbar()
#IBOutlet var txtFieldState: UITextField!
var stateArray = ["Andaman & Nicobar Island","Andhra Pradesh","Arunachal Pradesh","Assam","Bihar","Chandigarh","Chhattisgarh","Dadra & Nagar"]
override func viewDidLoad() {
super.viewDidLoad()
txtFieldState.delegate = self
}
func textFieldDidBeginEditing(textField: UITextField) {
if textField == txtFieldState{
txtFieldState.inputView = picker
txtFieldState.inputAccessoryView = toolBar
self.configurePicker()
}
}
func configurePicker()
{
picker.alpha = 1.0
picker.backgroundColor = UIColor(red:241/255.0, green:241/255.0, blue:241/255.0, alpha: 1.0)
picker.showsSelectionIndicator = true
picker.delegate = self
picker.selectedRowInComponent(0)
let screenRect = self.view.frame
let pickerSize = picker.sizeThatFits(CGSizeZero)
let x = screenRect.origin.x + (screenRect.size.width / 2) - (pickerSize.width / 2)
let pickerRect = CGRectMake(x,
screenRect.origin.y + (screenRect.size.height) - (pickerSize.height),
pickerSize.width,
pickerSize.height)
picker.frame = pickerRect
let toolbarSize = toolBar.sizeThatFits(CGSizeZero)
toolBar.frame = CGRectMake(x,pickerRect.origin.y, // right under the picker
pickerSize.width, // make them the same width
toolbarSize.height)
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int){
dropState = row
txtFieldState.text = "\(stateArray[row])"
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
{
return stateArray[row]
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
{
return stateArray.count
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int
{
return 1
}
}

Related

UIPickerView did pass by row

I have this demo app:
As you can see, the alpha of the background is changing to black according to the value.
But the problem is that there is no smooth transition:
As you can see from the GIF, the background is only changing after the scrolling is over. And I don't want it to be like that.
this is my code:
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var backView: UIView!
let max = 100
override func viewDidLoad() {
super.viewDidLoad()
pickerView.delegate = self
pickerView.dataSource = self
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return max + 1 // To include '0'
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
let l = UILabel(frame: .zero)
l.text = String(max - row)
l.textColor = .white
l.font = UIFont.preferredFont(forTextStyle: .title3)
l.textAlignment = .center
return l
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
backView.alpha = CGFloat(max - row) / 100
}
}
I am giving the delegate UIView instead of String, because I had an idea: to test every time the location of each row is changing, and when the row is in the middle of the screen I should update the background. But unfortunately I don't know how to do that.
Maybe you can help? or perhaps suggest any other ideas?
Thank you!
The delegate method didSelectRow is only called when the rolling stops, so this is not the place you should update your alpha. UIPickerView has no delegate method which will notify you about the change during the rolling, however the UIPickerView will call your data source and delegate methods to get the title or in your case the view for a given row so it can be displayed as the user scrolls. So what you should do is just move your alpa changing logic there:
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
backView.alpha = CGFloat(max - row) / 100
}
Note that this delegate method will be called when the UIPickerView is loaded, so maybe you should disable the alpha changing until view is not layout out correctly(maybe viewDidAppear will do it).
As the delegate method can sometimes behave unexpectedly(calling not just the next lines, but any line from the picker), we should store and also check if the row is just one step ahead or behind the last saved value, otherwise we should just ignore that. I made a simple demo to demonstrate how it works:
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
private let pickerView = UIPickerView()
private var isPickerReady = false
private var lastValue = 0
private let max = 100
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(pickerView)
pickerView.translatesAutoresizingMaskIntoConstraints = false
pickerView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
pickerView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
pickerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
pickerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
pickerView.delegate = self
pickerView.dataSource = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
isPickerReady = true
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return max
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
// Do not update the value until the view is not loaded
// Only consider delegate methods which are one step ahead or behind the last value
if row + 1 == lastValue && isPickerReady || row - 1 == lastValue && isPickerReady {
lastValue = row
view.alpha = CGFloat(max - lastValue ) / 100
}
return "Tiltle \(row)"
}
}

UIPickerView won't select the first row unless I select a different row first?

It seems that I cannot select the very first row in the UIPickerView when I first load it. I want the textfield to update immediately when I load the picker view. It only updates when I select something else, and then go back to the first row.
var platformData = ["XBOX", "PS4", "PC"]
var picker = UIPickerView()
#IBOutlet var platformSelected: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
picker.dataSource = self
platformSelected.inputView = picker
let toolbar = UIToolbar()
toolbar.sizeToFit()
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneAction))
toolbar.items = [doneButton]
platformSelected.inputAccessoryView = toolbar
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return platformData.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
platformSelected.text = platformData[row]
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return platformData[row]
}
Improve your code.
platformSelected.delegate = self
......
// MARK: - UITextFieldDelegate
extension YourUIViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
// TODO: - Specify index what you need. After editing it changed and you should get selected index using its position in platformData Array
let index = 0
picker.selectRow(index, inComponent: 0, animated: true)
}
}

hiding the keyboard when I used PickerView with textField

I want to realize one simple app, but I have some trouble. I add textField and PickerView. When I tab at textField, PickerView is appeared. But standard keyboard is appeared too (I dont want it). And when I again tab on textField, PickerView don't appear. How can I receive this problem? Thank's a lot!
This is my code:
import UIKit
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
#IBOutlet weak var pickerView1: UIPickerView!
#IBOutlet weak var textField1: UITextField!
var age = ["8", "18", "28", "38", "48"]
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return age.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if pickerView == pickerView1 {
}
return age[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == pickerView1 {
self.textField1.text = self.age[row]
self.pickerView1.isHidden = true
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == self.textField1 {
self.pickerView1.isHidden = false
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
why you are not try the simple way, just add your pickerview as inputview of your textfield
textField1.inputView = pickerView1
This will not give you a direct way to dismiss the view since your UIPickerView has no return button, which is why I recommend to use the inputAccessoryView property to display a toolbar with a done button
let myToolbar = UIToolbar(frame: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(320), height: CGFloat(44)))
//should code with variables to support view resizing
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.inputAccessoryViewDidFinish))
//using default text field delegate method here, here you could call
//myTextField.resignFirstResponder to dismiss the views
myToolbar.setItems([doneButton], animated: false)
lblcurrentText.inputAccessoryView = myToolbar
and call the function as
func inputAccessoryViewDidFinish() {
lblcurrentText.resignFirstResponder()
}
at the same time after selection on your pickerview hide the text field like
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == pickerView1 {
self.textField1.text = self.age[row]
self.inputAccessoryViewDidFinish()
}
}
Tutorial
for example you can get the step by step tutorial in here

UIPickerView text plus image

is it possible to create UIPickerView where there will be text and image in one row?
I know how to create it with text only:
class FirstViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
var currArray = ["USD","GBP"]
var flags = ["USD.jpg", "GBP.jpg"]
var picker = UIPickerView()
#IBOutlet weak var currencySelectorLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
picker.dataSource = self
currencySelectorLabel.inputView = picker
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return currArray.count
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
currencySelectorLabel.text = currArray[row]
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return currArray[row]
}
}
But I would like to have something like this:
Obviously instead of image files' names should be real images.
And I want to assign label text from UIPickerView as it is right now.
With help of this post How can I get images to appear in UI PickerView Component in Swift? everything seems to work!
Try this to implement title and image in PickerView
#IBOutlet weak var pickerView: UIPickerView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
picker.delegate = self
picker.dataSource = self
currencySelectorLabel.inputView = picker
}
// MARK: UIPickerViewDataSource
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return currArray.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return currArray[row] as? String
}
// MARK: UIPickerViewDelegate
func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView {
let myView = UIView(frame: CGRect(x: 0, y: 0, width: pickerView.bounds.width - 50, height: 40))
let myImageView = UIImageView(frame: CGRect(x: 60, y: 0, width: 30, height: 30))
let countryLabel = UILabel(frame: CGRect(x: 110, y: 0, width: pickerView.bounds.width - 90, height: 40 ))
myImageView.image = flags[row]
countryLabel.font = UIFont.boldSystemFont(ofSize: 16.0)
countryLabel.textColor = UIColor.white
countryLabel.text = currArray[row] as? String
myView.addSubview(countryLabel)
myView.addSubview(myImageView)
return myView
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// do something with selected row
}

Swift - Programmatically create and display UIPickerView when BarButtonItem is pressed

Initially I wanted to add a hidden UIPickerView onto the Main.storyboard alongside my existing UISearchBar and when a BarButtonItem is clicked, the UIPickerView should be displayed; but it appears I cannot have them both at once in a given space.
So instead, my best alternative was to create it programmatically. I've followed existing tutorials (http://sourcefreeze.com/ios-uipickerview-example-using-swift/) and similar questions (Programmatically Create and Show UIPickerView) and seems like I do (?) have a UIPickerView as the description of it is being printed and I get the following:
<UIPickerView: 0x7f86425b1fb0; frame = (100 100; 100 162); layer = <CALayer: 0x7f8642543a20>>
Here is part of my current code which may be of help:
AnimalTableViewController.swift
import UIKit
class AnimalTableViewController: UITableViewController, UINavigationControllerDelegate, UISearchBarDelegate, UISearchDisplayDelegate, UISearchResultsUpdating, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet var segmentedSortOption: UISegmentedControl!
var array : NSArray = Animal.animalStruct.jsonResult["animal"] as NSArray
var filteredArray = [[String:AnyObject]]()
var timer = NSTimer()
var counter:Int = 1
var typePickerView: UIPickerView = UIPickerView()
#IBOutlet var typeBarButton: UIBarButtonItem!
var resultSearchController = UISearchController()
var indexArray:String!
#IBAction func refresh(sender: AnyObject) {
self.tableView.reloadData()
println("refreshed")
}
override func viewDidLoad() {
super.viewDidLoad()
self.typePickerView.hidden = true
self.typePickerView.dataSource = self
self.typePickerView.delegate = self
self.typePickerView.frame = CGRectMake(100, 100, 100, 162)
self.typePickerView.backgroundColor = UIColor.blackColor()
self.typePickerView.layer.borderColor = UIColor.whiteColor().CGColor
self.typePickerView.layer.borderWidth = 1
timer = NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: Selector("result"), userInfo: nil, repeats: true)
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return array.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return array[row]["type1"] as String
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
typeBarButton.title = array[row]["type1"] as? String
typePickerView.hidden = false
}
func pickerView(pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
return 36.0
}
func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
return 36.0
}
#IBAction func typePickerViewSelected(sender: AnyObject) {
typePickerView.hidden = false
println(typePickerView.description)
}
}
Please could you help me display the programmatically created UIPickerView when the BarButtonItem is pressed? If you have any more questions, please do ask.
Many thanks.
You never add the pickerView as a subview of the ViewController, which you can do in viewDidLoad() since you're hiding it. Then when you unhide it your view should be there.
EDIT: Added Code
override func viewDidLoad() {
super.viewDidLoad()
self.typePickerView.hidden = true
//other pickerView code like dataSource and delegate
self.view.addSubview(pickerView) //will add the subview to the view hierarchy
}
With the above code, now when you unhide it on button press the view will show up.

Resources