๐Ÿ”ธ A customizable alternative to UIPickerView in Swift.

Overview

PickerView

CocoaPods CocoaPods Version License Platform

PickerView is an easy to use and customize alternative to UIPickerView written in Swift. It was developed to provide a highly customizable experience, so you can implement your custom designed UIPickerView.

Requirements

It requires Xcode 8.0+ and Swift 3.0.

NOTE: When PickerView was first built it wasn't thought to support Objective-C projects, but the community demanded it and recently we've released the version 0.2.0 which supports Objective-C projects. After some tests we noticed some bugs when using from Objective-C, so we've this issue open and we need some help to fix that, so if you are making some adjustments to run in your Objective-C project, please, contribute with us to fix these problems. Thanks.

Your project deployment target must be iOS 8.0+

Installation

CocoaPods

PickerView is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "PickerView"

Manually

You can also install it manually just draging PickerView file to your project.

Usage

To run the example project, clone the repo, and run pod install from the Example directory first.

PickerView is very simple to use, you can use it with Interface Builder or through code, it works similar as UIPickerView you need to implement PickerViewDataSource and PickerViewDelegate protocols to provide the basic information and make it works.

You can customize the text appearance, the scrolling style (default or infinite scrolling) and the selection style (selection indicators and overlays with custom UIView's or even an UIImage to highlight the selected row).

Interface Builder

Drag a UIView to your view controller (and put the constraints if you want).

Putting PickerView on Interface Builder

Change the class to PickerView on the Identity Inspector and make the @IBOutlet connection.

Change the custom class.

From Code

Create a new PickerView.

let examplePicker = PickerView()
examplePicker.translatesAutoresizingMaskIntoConstraints = false

Add to your view and put your custom constraints.

view.addSubview(examplePicker)
view.addConstraints([yourCustomConstraints])

Set the PickerViewDataSource and PickerViewDelegate

Don't forget to set the datasource and delegate of your PickerView:

// ...
// The `examplePicker` below is the same that you created through code or connected via @IBOutlet
examplePicker.dataSource = self
examplePicker.delegate = self
// ...

Implement PickerViewDataSource

The PickerViewDataSource protocol consists in two required methods:

@objc public protocol PickerViewDataSource: class {
    func pickerViewNumberOfRows(_ pickerView: PickerView) -> Int
    func pickerView(_ pickerView: PickerView, titleForRow row: Int) -> String
}

You need to return the pickerViewNumberOfRows as we see below:

func pickerViewNumberOfRows(_ pickerView: PickerView) -> Int {
    return itemsThatYouWantToPresent.count 
}

And the title for each row:

func pickerView(_ pickerView: PickerView, titleForRow row: Int) -> String {
    let item = itemsThatYouWantToPresent[row]
    return item.text
}

Done. Now you'll need to implement one more protocol and then we are all set.

Implement PickerViewDelegate

The PickerViewDelegate consists in five methods:

@objc public protocol PickerViewDelegate: class {
    func pickerViewHeightForRows(_ pickerView: PickerView) -> CGFloat
    optional func pickerView(_ pickerView: PickerView, didSelectRow row: Int)
    optional func pickerView(_ pickerView: PickerView, didTapRow row: Int)
    optional func pickerView(_ pickerView: PickerView, styleForLabel label: UILabel, highlighted: Bool)
    optional func pickerView(_ pickerView: PickerView, viewForRow row: Int, highlighted: Bool, reusingView view: UIView?) -> UIView?
}

Firstly you must provide the pickerViewHeightForRows(_:):

func pickerViewHeightForRows(_ pickerView: PickerView) -> CGFloat {
    return 50.0 // In this example I'm returning arbitrary 50.0pt but you should return the row height you want.
}

Then is the method where you can do something with the row selected in PickerView:

func pickerView(_ pickerView: PickerView, didSelectRow row: Int) {
    let selectedItem = itemsThatYouWantToPresent[row] 
    print("The selected item is \(selectedItem.name)") 
}

PickerView enable the user to tap a visible row to select it. We've a delegate method to track this tap behavior, so pickerView(_: PickerView, didTapRow row: Int) is called only when the user selects a row by tapping it:

func pickerView(_ pickerView: PickerView, didTapRow row: Int) {
    print("The row \(row) was tapped by the user") 
}

The following method allows you to customize the label that will present your items in PickerView. Use the flag `highlighted' to provide a differrent style when the item is selected:

func pickerView(_ pickerView: PickerView, styleForLabel label: UILabel, highlighted: Bool) {
    label.textAlignment = .Center
    
    if highlighted { 
        label.font = UIFont.systemFontOfSize(25.0)
        label.textColor = view.tintColor
    } else {
        label.font = UIFont.systemFontOfSize(15.0)
        label.textColor = .lightGrayColor()
    }
}

If you want to provide a totally customized view instead of presenting just a row with a text label inside

func pickerView(_ pickerView: PickerView, viewForRow row: Int, highlighted: Bool, reusingView view: UIView?) -> UIView? {
    var customView = view
    
    // Verify if there is a view to reuse, if not, init your view. 
    if customView == nil {
        // Init your view
        customView = MyCustomView()
    }
    
    // **IMPORTANT**: As you are providing a totally custom view, PickerView doesn't know where to bind the data provided on PickerViewDataSource, so you will need to bind the data in this method. 
    customView.yourCustomTextLabel.text = itemsThatYouWantToPresent[row].text
    
    // Don't forget to make your style customizations for highlighted state 
    let alphaBasedOnHighlighted: CGFloat = highlighted ? 1.0 : 0.5
    customView.yourCustomTextLabel.alpha = alphaBasedOnHighlighted
    
    return customView
}

Label or Custom View?

Even if func pickerView(_ pickerView: PickerView, styleForLabel label: UILabel, highlighted: Bool) and func pickerView(_ pickerView: PickerView, viewForRow row: Int, highlited: Bool, reusingView view: UIView?) are optional methods in PickerViewDelegate you must implement at least one of them. If you want to present your data using only a label (which you can customize too), implement the first one, but if you want to present your data in a totally customized view, you should implement the second one.

NOTE: If you implement the two methods mentioned above, PickerView will always present your data using the custom view you provided.

Cool! You are ready to build and run your application with PickerView, it ill works with the default configurations and the text appearance you provided in PickerViewDelegate on the next section we will cover the scrolling and selection style customizations.

Custom Appearance

As you can customize the text appearance through PickerViewDelegate, you have two more options to customize your PickerView: scrolling style and selection style.

Scrolling Style

Is defined by the scrollingStyle property of PickerView that is of type ScrollingStyle a nested enum that has two member values: .Default and .Infinite. It is explicit and you might already know what it does, but let me explain better:

.Default: The default scrolling experience, the user can scroll through your PickerView item's but he will reach the bottom or the top of your picker when it doesn't have more items to show.

.Infinite: With this scrolling style the user will loop through the items of your PickerView and never reach the end, it provides an infinite scrolling experience.

Selection Style

Is defined by the selectionStyle property of PickerView that is of type SelectionStyle a nested enum that has four member values: .None, .DefaultIndicator, .Overlay and .Image. For your better understanding, follow the explanation below:

.None: Don't uses any aditional view to highlight the selection, only the highlighted label style customization provided by delegate.

.DefaultIndicator: Provides a simple selection indicator on the bottom of the highlighted row with full width and 2pt of height. The default color is its superview tintColor but you have free access to customize the DefaultIndicator through the defaultSelectionIndicator property.

.Overlay: Provide a full width and height (the height you provided on delegate) view that overlay the highlighted row. The default color is its superview tintColor and the alpha is set to 0.25, but you have free access to customize it through the selectionOverlay property.

Tip: You can set the alpha to 1.0 and background color to .clearColor() and add your custom selection view as a selectionOverlay subview to make it looks as you want (don't forget to properly add the constraints related to selectionOverlay to keep your experience with any screen size).

.Image: Provide a full width and height image view selection indicator (the height you provided on delegate) without any image. You must have a selection indicator as an image and set it to the image view through the selectionImageView property.

Who's Using It?

See what projects are using PickerView on our wiki page.

Contribute

Feel free to submit your pull request, suggest any update, report a bug or create a feature request.

Before you start contributing, please take a look on our contributing guide.

Just want to say hello? -> ofilipealvarenga at gmail.com

Contributors

Author: @filipealva

See the people who helps to improve this project: Contributors โ™ฅ

Special thanks to: @bguidolim and @jairobjunior.

Starring is caring ๐ŸŒŸ

If you liked the project and want it to continue, please star it. It is a way for me to know the impact of the project. Thank you <3

License

PickerView is available under the MIT license. See the LICENSE file for more info.

Donate

Comments
  • iOS 11, iPhone 7 Simulator, Xcode Version 9.0 beta 6 (9M214v) - Initial selected values not aligned.

    iOS 11, iPhone 7 Simulator, Xcode Version 9.0 beta 6 (9M214v) - Initial selected values not aligned.

    Selected values don't line up. Not sure if it is the PickerView or Apple, but here it is, none-the-less.

    screen shot 2017-08-29 at 4 22 32 pm

    When I click on the number 9 in the first picker it does this.

    screen shot 2017-08-29 at 4 30 40 pm

    If i scroll all pickers to the top (0) then back to the correct value, then it looks correct.

    screen shot 2017-08-29 at 4 35 26 pm

    Thereafter it seems to work correctly.

    opened by neilt 9
  • removed asynchonous code in setup method

    removed asynchonous code in setup method

    I had a crash with bad access in the setup method, where it's running in the background thread.

    Unfortunately I didn't save the exception.

    It was on the simulator and only happened once out of maybe 30 starts but I really don't want this to happen in my app on a customer's device. Especially because it was so rare. Those bugs are hard / impossible to debug.

    IMO it's a mistake to do anything asynchronously that has to do with views. Views must be modified in the main thread. They are single threaded. I recall this being mentioned in Apple's documentation somewhere too.

    Submitting this as change request - integrate it if you agree, or not if you don't ;)

    I am grateful you wrote this code, so this is giving back.

    opened by n13 7
  • Custom View Items

    Custom View Items

    The main goal when I started to change PickerView control was to implement custom view items for a project. I ended up implementing a lot more than that. I don't know whether you'll agree with all the changes that I did but I'm sending this pull request because I really like the result.

    Summary of what I've done:

    • Added support for custom view items
    • Added compatibility with Objective-C
    • Added optional for function delegates that aren't mandatory
    • For every function delegate that has row parameter added an index parameter. This parameter is pretty handy when dealing with an infinite PickerView. row is the indexPath.row from the internal tableView while index is the index considering indexPath.row % items.count
    • Added a new function delegate to know when a row was tapped: pickerView(PickerView, didTapRow row: Int, index: Int)
    • Changed the name of some function delegates to conform with Apple's UIPickerViewDelegate. The names of function delegates were working fine but I prefer to work with their names as close as Apple's.

    I know, there's a lot of changes. I should have separated each implementation but I changed the control directly while I was developing the project that I mentioned before. Sorry for that.

    I added the custom view option in the Example project for better reference and testing.

    Resolves #4

    opened by felipowsky 7
  • Programmatically Select Row

    Programmatically Select Row

    Is there a way to programmatically scroll to a row or index, after the picker has loaded? I need to scroll to specific items, after the view has loaded, based on what the user selects in that View Controller

    I've tried objectPicker.currentSelectedRow = 0

    Any help is appreciated.

    opened by dhruveonmars 6
  • Objective.c version

    Objective.c version

    This project is amazing! It's very cool believe me! Maybe is possible have a version in objective.c or a demo project how to implement swift in objective.c?Many now use swift but I'm not so quick to learn๐Ÿ˜ฉ

    Thanks!

    enhancement 
    opened by alfio86 6
  • Infinite List on iOS 11

    Infinite List on iOS 11

    On iOS 11, an infinite list does not show the selected item anymore, and pressing on items does a really long scroll to the item.

    This happens even on the example project included.

    opened by dhruveonmars 5
  • [BUG] The default selected row is not the middle one.

    [BUG] The default selected row is not the middle one.

    Hi, thank you so much for this, it's really helpful! I noticed that the default selected row is not the middle one. I think the problem is in this code:

    Int(ceil(Float(numberOfRowsByDataSource) / 2.0))

    Since the row is an index, you have to subtract 1 to numberOfRowsByDataSource, to get the middle one. For example if we have 5 items, ceil(2.5) is 3, but the middle item index is actually 2.

    It would be great to have a public function (or delegate function) that lets you set the first selected item.

    Thanks

    bug 
    opened by vanuccif 5
  • Crash Sometimes

    Crash Sometimes

    Hi ! I meet a strange problem that "sometimes" it will crash due to beyond the data array.

    Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'

    and the " bounds[0..1] " will be different every time, sometimes it's [0..0], sometimes it's [0..2] and [0..3],

    hope you can help me !

    bug 
    opened by Hinsverson 4
  • `reloadPickerView()` Acting Weird

    `reloadPickerView()` Acting Weird

    I'm using with iOS 11 and Xcode 9 and when reloadPickerView() seems to be called my pickerView starts to act strange. The selection is off center from the overlay I have on with the standard style and my conditions for highlighted aren't met. Everything seems to get shifted up in position also.

    opened by niazoff 3
  • Stuck in endless while loop, 0 rows for datasource.

    Stuck in endless while loop, 0 rows for datasource.

    In the PickerView.swift, I am in an endless loop here, which locks up my app. I'm returning 0 for the pickerViewNumberOfRows delegate, because the user has not yet entered any data. I set the delegate and datasource in viewDidLoad().

    `fileprivate func selectedNearbyToMiddleRow(_ row: Int) { currentSelectedRow = row tableView.reloadData()

        repeat {
            // This line adjust the contentInset to UIEdgeInsetZero because when the PickerView are inside of a UIViewController 
            // presented by a UINavigation controller, the tableView contentInset is affected.
            tableView.contentInset = UIEdgeInsets.zero
            
            let indexOfSelectedRow = visibleIndexOfSelectedRow()
            tableView.setContentOffset(CGPoint(x: 0.0, y: CGFloat(indexOfSelectedRow) * rowHeight), animated: false)
            
            delegate?.pickerView?(self, didSelectRow: currentSelectedRow, index: currentSelectedIndex)
            
        } while !(numberOfRowsByDataSource > 0 && tableView.numberOfRows(inSection: 0) > 0)
    }`
    
    opened by bvelasquez 3
  • Need to scroll pickerView to see updated values.

    Need to scroll pickerView to see updated values.

    Whenever I update the list of values I want in the PickerView I have to scroll the current value out of view before they update.

    I'm changing the PresentationType to updater the list, is this correct or am I missing something?

    enhancement 
    opened by TheSlenderMan 3
  • SwiftUI -  cellForRowAtIndexPath never called in UIViewRepresentable

    SwiftUI - cellForRowAtIndexPath never called in UIViewRepresentable

    I'm trying to use this library in SwiftUI but there seems to be an issue. When I present the picker, nothing is shown. I set breakpoint and cellForRowAtIndexPath is never called.

    I searched online and another person is having the same exact issue: https://stackoverflow.com/questions/57297895/swiftui-uitableview-cellforrowatindexpath-never-called-in-uiviewrepresentable

    Here is the code in StackOverflow that is very similar to mine that doesn't work:

    struct FruitPicker: UIViewRepresentable {
        @Binding var fruits: [Fruit]
        @Binding var selectedFruit: Fruit
    
        class Coordinator: NSObject, PickerViewDelegate, PickerViewDataSource {
            @Binding var fruits: [Fruit]
            @Binding var selectedFruit: Fruit
    
            init(fruits: Binding<[Fruit]>, selectedFruit: Binding<Fruit>) {
                self._fruits = fruits
                self._selectedFruit = selectedFruit
            }
    
            func pickerViewNumberOfRows(_ pickerView: PickerView) -> Int {
                return self.fruits.count
            }
    
            func pickerViewHeightForRows(_ pickerView: PickerView) -> CGFloat {
                return 50
            }
    
            func pickerView(_ pickerView: PickerView, titleForRow row: Int) -> String {
                return self.fruits[row].name
            }
    
            func pickerView(_ pickerView: PickerView, didSelectRow row: Int) {
                self.selectedFruit = self.fruits[row]
            }
        }
    
        func makeCoordinator() -> FruitPicker.Coordinator {
            return Coordinator(fruits: self.$fruits, selectedFruit: self.$selectedFruit)
        }
    
        func makeUIView(context: UIViewRepresentableContext<FruitPicker>) -> PickerView {
            let pickerView = PickerView()
    
            pickerView.delegate = context.coordinator
            pickerView.dataSource = context.coordinator
    
            return pickerView
        }
    
        func updateUIView(_ pickerView: PickerView, context: UIViewRepresentableContext<FruitPicker>) {
    
        }
    }
    

    Could you please help out with this?

    opened by spike-hue 2
  • [FEATURE] Animated item selection

    [FEATURE] Animated item selection

    I would like to animate highlighting in styleForLabel, so i've overrides this method like this: if highlighted { UIView.animate(withDuration: 0.5, animations: { label.transform = CGAffineTransform(scaleX: 1, y: 1) }) } else { UIView.animate(withDuration: 0.5, animations: { label.transform = CGAffineTransform(scaleX: 1/2.5, y: 1/2.5) }) } But this gives me negative effect - it tries to animate labels before the view appears... So i decided to give my VC an extra property - var beginAnimating = false, and set this to true in viewDidApear. Unfortunately this is not working as I expect =( Please give me some advices with this issue. Thanks!

    feature 
    opened by D-Link13 1
  • [IMPROVEMENT] Some properties are not working when using from Objective-C project.

    [IMPROVEMENT] Some properties are not working when using from Objective-C project.

    Some people reach me out reporting about some properties which aren't working when PickerView is implemented in an Objective-C project.

    The known properties with issues:

    • scrollingStyle: When its set to .Infinite, or rawValue 1, it causes a crash.
    • selectionStyle: Not working.
    • currentSelectedRow: Usually it sets a initial selected value to PickerView but its not appearing as a public property when exposed to Objective-C (even if it is declared as public in PickerView).

    Unfortunately I can't fix this right now, so I'm opening this issue as a [help-wanted].

    If you can help, please interact here and/or submit a pull request.

    Thanks.

    help wanted improvement objc-support 
    opened by filipealva 1
Owner
Filipe Alvarenga
iOS Software Engineer | @apple @wwdc 15 Scholar | @apple Developer Academy alumni | UX enthusiast
Filipe Alvarenga
Quickly reproduce the dropdown UIPickerView / ActionSheet functionality on iOS.

ActionSheetPicker-3.0 Important update Now I fixed most of the things and merge PR' (thanks to ). I did much work to support this library from iOS 5.

Petr Korolev 3.4k Dec 21, 2022
Elegant manager to easily deal with UIPickerView

PickL PickL is an elegant manager to easily deal with UIPickerView. You don't have to implement a logic for UIPickerViewDataSource and UIPickerViewDel

Rosberry 5 Jun 24, 2021
FYPhoto is a photo/video picker and image browser library for iOS written in pure Swift. It is feature-rich and highly customizable to match your App's requirements.

FYPhoto is a photo/video picker and image browser library for iOS written in pure Swift. It is feature-rich and highly customizable to match your App's requirements.

null 10 Dec 11, 2022
A fully customizable iOS Horizontal PickerView library, written in pure swift

ADDatePicker is Horizontal Date Picker Library written in Swift Requirements Communication Installation Usage Demo Customization Credits License Requi

Abhishek Dave 166 Dec 21, 2022
A simple, customizable view for efficiently collecting country information in iOS apps.

CountryPickerView CountryPickerView is a simple, customizable view for selecting countries in iOS apps. You can clone/download the repository and run

Kizito Nwose 459 Dec 27, 2022
A simple yet customizable horizontal and vertical picker view

WheelPicker A simple yet customizable horizontal and vertical picker view Features Vertical or Horizontal picker Image or Text data Configure UILabel

Mind Studios 74 Sep 26, 2022
LocationPicker - A ready for use and fully customizable location picker for your app

LocationPicker A ready for use and fully customizable location picker for your app. Features Installation Cocoapods Carthage Swift Package Manager Qui

Zhuoran 397 Nov 16, 2022
๐ŸŽฏ Swift country and phone code Picker

CountryPicker Picker code Swift 3 / 4 / 5. Example To run the example project, clone the repo, and run pod install from the Example directory first. U

null 207 Dec 22, 2022
Elegant and Easy-to-Use iOS Swift Date Picker

D2PDatePicker Example To run the example project, clone the repo, and run pod install from the Example directory first. Example Code: Programmatical I

Pradheep Rajendirane 292 Nov 6, 2022
A ฮผlibrary in Swift containing all the countries with their localized name, ISO code, phone code, country code, flag image and emoji.

CountryKit CountryKit A ฮผlibrary in Swift containing all the countries with their localized name, ISO code, phone code, country code, flag image and e

Alessandro 24 Nov 11, 2021
McPicker is a customizable, closure driven UIPickerView drop-in solution with animations that is rotation ready.

McPicker About McPicker is a UIPickerView drop-in solution with animations that is rotation ready. The more string arrays you pass, the more picker co

Kevin McGill 207 Dec 13, 2022
Quickly reproduce the dropdown UIPickerView / ActionSheet functionality on iOS.

ActionSheetPicker-3.0 Important update Now I fixed most of the things and merge PR' (thanks to ). I did much work to support this library from iOS 5.

Petr Korolev 3.4k Dec 21, 2022
This is an iOS control for selecting something using UIPickerView in an UIAlertController like manner

RMPickerViewController This framework allows you to pick something with a picker presented as an action sheet. In addition, it allows you to add actio

Roland Moers 382 Dec 19, 2022
TextField with DropDown support using UIPickerView

IQDropDownTextField TextField with DropDown support using UIPickerView Installing Install using cocoapods. Add in your Podfile: pod 'IQDropDownTextFie

Mohd Iftekhar Qurashi 301 Jun 29, 2022
Elegant manager to easily deal with UIPickerView

PickL PickL is an elegant manager to easily deal with UIPickerView. You don't have to implement a logic for UIPickerViewDataSource and UIPickerViewDel

Rosberry 5 Jun 24, 2021
TryCustomDatePicker - Customize UIDatePicker via UIPickerView

TryCustomDatePicker This is just test code Customize UIDatePicker via UIPickerVi

lydsnm 2 Nov 14, 2022
๐Ÿ’ฅ Beautiful, animated and highly customizable UIPageControl alternative for iOS.

PageControl Requirements iOS 9.0+ Xcode 7.0+ Installation CocoaPods: Add folowing line to Podfile and run 'pod instal'. pod 'Sevruk-PageControl' Or j

Sevruk Development 30 May 2, 2022
Schedule timing task in Swift using a fluent API. (A friendly alternative to Timer)

Schedule(็ฎ€ไฝ“ไธญๆ–‡) Schedule is a timing tasks scheduler written in Swift. It allows you run timing tasks with elegant and intuitive syntax. Features Elega

Luo Xiu 1.8k Jan 7, 2023
Schedule timing task in Swift using a fluent API. (A friendly alternative to Timer)

Schedule(็ฎ€ไฝ“ไธญๆ–‡) Schedule is a timing tasks scheduler written in Swift. It allows you run timing tasks with elegant and intuitive syntax. Features Elega

Luo Xiu 1.8k Dec 21, 2022