Skip to content

Instantly share code, notes, and snippets.

@KrisRJack
Last active January 2, 2021 16:13
Show Gist options
  • Save KrisRJack/1dbc796a4a2984e7ac26ce28adf3ab16 to your computer and use it in GitHub Desktop.
Save KrisRJack/1dbc796a4a2984e7ac26ce28adf3ab16 to your computer and use it in GitHub Desktop.
Labeled Picker View In Swift
//
// LabeledPickerView.swift
// Created by Kristopher Jackson
//
import UIKit
protocol LabeledPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int
}
protocol LabeledPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, labelForComponent component: Int) -> String?
func pickerView(_ pickerView: UIPickerView, textAlignmentForComponent component: Int) -> NSTextAlignment?
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String?
}
class LabeledPickerView: UIView {
var delegate: LabeledPickerViewDelegate?
var dataSource: LabeledPickerViewDataSource?
var pickerView: UIPickerView = UIPickerView()
var labelFont: UIFont = .boldSystemFont(ofSize: 18)
var font: UIFont = .systemFont(ofSize: 20, weight: .regular)
var textEdgeInsets: UIEdgeInsets {
get {
return self.insets
}
set {
self.insets = newValue
}
}
private var labels: [UILabel] = []
private var insets: UIEdgeInsets = .zero
private var labelWidthAnchors: [NSLayoutConstraint] = []
init() {
super.init(frame: .zero)
self.setView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setView()
}
private func setView() {
self.backgroundColor = .clear
self.pickerView.delegate = self
self.pickerView.dataSource = self
self.addSubview(self.pickerView)
NSLayoutConstraint.activate([
self.pickerView.widthAnchor.constraint(equalTo: self.widthAnchor),
self.pickerView.heightAnchor.constraint(equalTo: self.heightAnchor),
self.pickerView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
self.pickerView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
])
}
private func setLabels() {
for component in 0..<self.pickerView.numberOfComponents {
/// Skip iteration if label was created
if self.labels.indices.contains(component) { continue }
/// Create label
let label: Label = Label()
label.font = self.labelFont
label.textAlignment = .right
label.paddingTop = self.insets.top
label.paddingLeft = self.insets.left
label.paddingRight = self.insets.right
label.paddingBottom = self.insets.bottom
label.translatesAutoresizingMaskIntoConstraints = false
label.text = self.delegate?.pickerView(self.pickerView, labelForComponent: component)
/// Add label to picker view
self.pickerView.addSubview(label)
self.labelWidthAnchors.append(label.widthAnchor.constraint(equalToConstant: 0))
NSLayoutConstraint.activate([
self.labelWidthAnchors[self.labelWidthAnchors.count - 1],
label.centerYAnchor.constraint(equalTo: self.pickerView.centerYAnchor),
self.labels.count == 0 ? (label.leftAnchor.constraint(equalTo: self.pickerView.leftAnchor))
: (label.leftAnchor.constraint(equalTo: self.labels[component - 1].rightAnchor, constant: 5)),
])
self.labels.append(label)
}
}
}
extension LabeledPickerView: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return self.dataSource?.numberOfComponents(in: pickerView) ?? 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.dataSource?.pickerView(pickerView, numberOfRowsInComponent: component) ?? 0
}
}
extension LabeledPickerView: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
self.setLabels()
var attributedString: NSMutableAttributedString!
let labelString: String? = self.labels[component].text
let titleString: String = self.delegate?.pickerView(pickerView, titleForRow: row, forComponent: component) ?? ""
var pickerLabel: Label?
if (view as? UILabel) == nil {
pickerLabel = Label()
pickerLabel?.font = self.font
pickerLabel?.textAlignment = .right
pickerLabel?.paddingTop = self.insets.top
pickerLabel?.paddingLeft = self.insets.left
pickerLabel?.paddingRight = self.insets.right
pickerLabel?.paddingBottom = self.insets.bottom
}
if let label = labelString {
let string = "\(titleString) \(label)"
attributedString = NSMutableAttributedString(string: string)
attributedString.addAttributes([
.font : self.labelFont,
.foregroundColor : UIColor.clear,
], range: NSRange(location: string.count - label.count, length: label.count))
} else {
attributedString = NSMutableAttributedString(string: titleString)
pickerLabel?.textAlignment = self.delegate?.pickerView(pickerView, textAlignmentForComponent: component) ?? .center
}
pickerLabel?.attributedText = attributedString
self.labelWidthAnchors[component].constant = pickerView.rowSize(forComponent: component).width
return pickerLabel!
}
}
@IBDesignable
class Label: UILabel {
var textEdgeInsets = UIEdgeInsets.zero {
didSet { invalidateIntrinsicContentSize() }
}
open override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let insetRect = bounds.inset(by: textEdgeInsets)
let textRect = super.textRect(forBounds: insetRect, limitedToNumberOfLines: numberOfLines)
let invertedInsets = UIEdgeInsets(top: -textEdgeInsets.top,
left: -textEdgeInsets.left,
bottom: -textEdgeInsets.bottom,
right: -textEdgeInsets.right)
return textRect.inset(by: invertedInsets)
}
override func drawText(in rect: CGRect) {
super.drawText(in: rect.inset(by: textEdgeInsets))
}
@IBInspectable
var paddingLeft: CGFloat {
set { textEdgeInsets.left = newValue }
get { return textEdgeInsets.left }
}
@IBInspectable
var paddingRight: CGFloat {
set { textEdgeInsets.right = newValue }
get { return textEdgeInsets.right }
}
@IBInspectable
var paddingTop: CGFloat {
set { textEdgeInsets.top = newValue }
get { return textEdgeInsets.top }
}
@IBInspectable
var paddingBottom: CGFloat {
set { textEdgeInsets.bottom = newValue }
get { return textEdgeInsets.bottom }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment