[英]Show UIPickerView like a keyboard, without UITextField
I'm looking for a way to present a UIPickerView
when the user taps on a UIBarButtonItem
.我正在寻找一种在用户点击UIBarButtonItem
时呈现UIPickerView
的UIBarButtonItem
。 Imagine a filter for the table view results.想象一下表格视图结果的过滤器。
I know I could use a UITextField
inputView, but this would not be the case -- all I have is a UIBarButtonItem
and a UITableView
.我知道我可以使用UITextField
inputView,但事实并非如此——我只有一个UIBarButtonItem
和一个UITableView
。
I've seen into using a UIActionSheet , but it does not look natural, specially when it's animating to show.我已经看到使用UIActionSheet ,但它看起来不自然,特别是在动画显示时。
Would animating the UIView
on and off the screen manually the only option?手动打开和关闭屏幕的UIView
动画是唯一的选择吗?
The app is iOS 6+ and iPhone only, so I don't need to keep compatibility with any other versions/idioms.该应用程序仅适用于 iOS 6+ 和 iPhone,因此我不需要保持与任何其他版本/习惯用法的兼容性。
Attach a UIPickerView as the inputView of a 0-sized UITextField that you have added to your view.附加一个 UIPickerView 作为您添加到视图中的 0 大小 UITextField 的 inputView。
let picker = UIPickerView()
picker.dataSource = self
picker.delegate = self
let dummy = UITextField(frame: CGRectZero)
view.addSubview(dummy)
dummy.inputView = picker
dummy.becomeFirstResponder()
A better option is still to use the UITextField
.更好的选择仍然是使用UITextField
。 You don't have to actually show it on screen.您不必实际在屏幕上显示它。 Just put it in a 0x0 UIView
so that it is not visible, set it's inputView
to your UIPickerView
and call becomeFirstResponder
on it to show the picker and resignFirstResponder
to hide it.只需将它放在 0x0 UIView
中使其不可见,将它的inputView
设置为您的UIPickerView
并在其上调用becomeFirstResponder
以显示选择器并resignFirstResponder
以隐藏它。
It is convenient to have a UIView
subclass that implements all of that along with UIPickerViewDelegate
and UIPickerViewDataSource
methods.拥有一个实现所有这些以及UIPickerViewDelegate
和UIPickerViewDataSource
方法的UIView
子类是很方便的。
I went with borisgolovnev 's suggestion of an invisible UITextField
and came up with the following Swift 2.3 implementation:我采纳了 borisgolovnev的关于不可见UITextField
的建议,并提出了以下Swift 2.3实现:
import UIKit
class PickerViewPresenter: UITextField {
// MARK: - Initialization
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init() {
super.init(frame: CGRect.zero)
inputView = pickerView
inputAccessoryView = pickerInputAccessoryView
}
// MARK: - Public
var pickerDelegate: UIPickerViewDelegate? {
didSet {
pickerView.delegate = pickerDelegate
}
}
var pickerDataSource: UIPickerViewDataSource? {
didSet {
pickerView.dataSource = pickerDataSource
}
}
var selectButtonAction: (() -> Void)?
var currentlySelectedRow: Int {
return pickerView.selectedRowInComponent(0)
}
func selectRowAtIndex(index: Int) {
pickerView.selectRow(index, inComponent: 0, animated: false)
}
func showPicker() {
self.becomeFirstResponder()
}
func hidePicker() {
self.resignFirstResponder()
}
// MARK: - Views
private let pickerView = UIPickerView(frame: CGRect.zero)
private lazy var pickerInputAccessoryView: UIView = {
let frame = CGRect(x: 0.0, y: 0.0, width: 0.0, height: 48.0)
let pickerInputAccessoryView = UIView(frame: frame)
// Customize the view here
return pickerInputAccessoryView
}()
func selectButtonPressed(sender: UIButton) {
selectButtonAction?()
}
}
PickerViewPresenter can then conveniently be used as such:然后可以方便地使用PickerViewPresenter :
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(pickerViewPresenter)
}
private let dataModel = [10, 20, 30, 40, 50]
private lazy var pickerViewPresenter: PickerViewPresenter = {
let pickerViewPresenter = PickerViewPresenter()
pickerViewPresenter.pickerDelegate = self
pickerViewPresenter.pickerDataSource = self
pickerViewPresenter.selectButtonAction = { [weak self] () -> Void in
guard let strongSelf = self else {
return
}
let result = strongSelf.dataModel[pickerViewPresenter.currentlySelectedRow]
pickerViewPresenter.hidePicker()
// ...
}
return pickerViewPresenter
}()
private func presentPickerView {
let index = 0 // [0..dataModel.count-1]
pickerViewPresenter.selectRowAtIndex(index)
pickerViewPresenter.showPicker()
}
// MARK: - UIPickerViewDataSource
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return dataModel.count
}
// MARK: - UIPickerViewDelegate
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return "\(dataModel[row]) drunken sailors"
}
}
Hope someone finds this useful =)希望有人觉得这很有用 =)
The cleanest way at least for me, and for Swift4 + Autolayout solution, without a UITextField
is adding a UIView
as a container of the UIPickerView
and a UIToolbar
for the top views/buttons.的干净的方式至少对我来说,和用于Swift4 +自动布局溶液,没有一个UITextField
正在添加UIView
作为的容器UIPickerView
和UIToolbar
的顶视图/按钮。
Then toggle the inset/offset of that container view with animation if you want, to hide and unhide the picker.如果需要,然后使用动画切换该容器视图的插入/偏移,以隐藏和取消隐藏选择器。
PROPERTIES特性
private lazy var view_PickerContainer: UIView = {
let view = UIView()
view.backgroundColor = .white
view.addSubview(self.timePicker)
view.addSubview(self.toolbar_Picker)
return view
}()
private lazy var timePicker: UIDatePicker = {
let picker = UIDatePicker()
picker.minimumDate = Date()
picker.datePickerMode = .time
picker.setDate(Date(), animated: true)
return picker
}()
private let timeFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "hh:mm a"
return formatter
}()
private lazy var toolbar_Picker: UIToolbar = {
let toolbar = UIToolbar()
toolbar.barStyle = .blackTranslucent
toolbar.barTintColor = .blueDarkText
toolbar.tintColor = .white
self.embedButtons(toolbar)
return toolbar
}()
I use SnapKit
to layout my views programmatically.我使用SnapKit
以编程方式布局我的视图。 You can use the timePicker
's bounds.size as reference for your layout.您可以使用timePicker
的 bounds.size 作为布局的参考。
IMPLEMENTATION执行
In viewDidLoad()在 viewDidLoad()
// Setup timepicker and its container.
self.view.addSubview(self.view_PickerContainer)
self.view_PickerContainer.snp.makeConstraints { (make) in
make.height.equalTo(self.timePicker.bounds.size.height + 50.0)
make.leading.trailing.equalToSuperview()
self.constraint_PickerContainerBottom = make.bottom.equalToSuperview().inset(-500.0).constraint
}
self.timePicker.snp.makeConstraints { (make) in
make.height.equalTo(self.timePicker.bounds.size.height)
make.width.equalToSuperview()
make.bottom.equalToSuperview()
}
// Add toolbar for buttons.
self.toolbar_Picker.snp.makeConstraints { (make) in
make.height.equalTo(40.0)
make.top.leading.trailing.equalToSuperview()
}
Embedding top views嵌入顶视图
private func embedButtons(_ toolbar: UIToolbar) {
func setupLabelBarButtonItem() -> UIBarButtonItem {
let label = UILabel()
label.text = "Set Alarm Time"
label.textColor = .white
return UIBarButtonItem(customView: label)
}
let todayButton = UIBarButtonItem(title: "Today", style: .plain, target: self, action: #selector(self.todayPressed(_:)))
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.donePressed(_:)))
let flexButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
toolbar.setItems([todayButton, flexButton, setupLabelBarButtonItem(), flexButton, doneButton], animated: true)
}
And the result looks like this:结果如下所示:
It might not be your only option but animating the UIPickerView should be relatively easy for you to do.它可能不是您唯一的选择,但动画 UIPickerView 对您来说应该相对容易。 Add the picker view so it's displayed off the bottom of the screen.添加选择器视图,使其显示在屏幕底部之外。 When it's time to animate it in:什么时候动画它:
[UIView animateWithDuration:0.3 animations:^{
self.datePicker.frame = CGRectMake(0, self.view.bounds.size.height - datePicker.bounds.size.height, datePicker.bounds.size.width, datePicker.bounds.size.height);
}];
And when it's time to hide it:当需要隐藏它时:
[UIView animateWithDuration:0.3 animations:^{
self.datePicker.frame = CGRectMake(0, self.view.bounds.size.height, datePicker.bounds.size.width, datePicker.bounds.size.height);
}];
Based on borisgolovnev's suggestion and CodeMonkey's answer , I have implemented the idea with an invisible UITextField
in the following way in Swift 5 .基于borisgolovnev 的建议和CodeMonkey 的回答,我在Swift 5中以以下方式使用不可见的UITextField
实现了这个想法。
import UIKit
class PickerViewPresenter: UITextField, UIPickerViewDataSource, UIPickerViewDelegate {
private lazy var doneToolbar: UIToolbar = {
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonTapped))
let items = [flexSpace, doneButton]
toolbar.items = items
toolbar.sizeToFit()
return toolbar
}()
private lazy var pickerView: UIPickerView = {
let pickerView = UIPickerView()
pickerView.dataSource = self
pickerView.delegate = self
return pickerView
}()
var items: [Item] = []
var didSelectItem: ((Item) -> Void)?
private var selectedItem: Item?
init() {
super.init(frame: .zero)
setupView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
inputView = pickerView
inputAccessoryView = doneToolbar
}
@objc private func doneButtonTapped() {
if let selectedItem = selectedItem {
didSelectItem?(selectedItem)
}
resignFirstResponder()
}
func showPicker() {
becomeFirstResponder()
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return items.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return items[row].name
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
selectedItem = items[row]
}
}
Usage in your ViewController:在您的 ViewController 中的用法:
private var selectedItem: Item?
private lazy var pickerViewPresenter: PickerViewPresenter = {
let pickerViewPresenter = PickerViewPresenter()
pickerViewPresenter.didSelectItem = { [weak self] item in
self?.selectedItem = item
}
return pickerViewPresenter
}()
view.addSubview(pickerViewPresenter)
// e.g. on a button tap
@objc private func buttonTapped() {
pickerViewPresenter.showPicker()
}
Example:例子:
I have attached it to a UITapGestureRecognizer
.我已将它附加到UITapGestureRecognizer
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.