- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
为了自定义iOS原生日期选择器(UIDatePicker)的外观,我在GitHub上找到了一段代码。 (引用:https://github.com/xiaosao6/CCDatePicker)
现在我想为选择器添加一个事件监听器。所选日期将更新为 UILabel。
我怎样才能像 UIDatePicker 那样做:
picker.addTarget(self, action: #selector(timeChanged(ccPicker:)), for: .valueChanged)
@objc func timeChanged(datePicker: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = NumberUtils.format["time"]
label.text = dateFormatter.string(from: datePicker.date)
}
我的做法是:
let picker = CCDatePicker.init(minDate: minDate, maxDate: maxDate)!
picker.frame = CGRect(x: 0, y: 0, width: pickerContainer.bounds.width, height: pickerContainer.bounds.height)
picker.isUserInteractionEnabled = true
picker.addTarget(self, action: #selector(timeChanged(ccPicker:)), for: .valueChanged)
pickerContainer.addSubview(picker)
@objc func timeChanged(ccPicker: CCDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = NumberUtils.format["date"]
label.text = ccPicker.date
}
但是,当我在 CCDatePicker 实例中选择一个新行时,此函数 timeChanged(ccPicker: CCDatePicker)
从未被调用。
如何将 .valueChanged
的事件监听器添加到我的自定义日期选择器?
//
// CCDatePicker.swift
// test
//
// Created by RenYuan on 5/11/19.
// Copyright © 2019 RenYuan. All rights reserved.
//
import UIKit
protocol CCDatePickerDelegate: class {
func didSelectDate(at picker: CCDatePicker)
}
protocol CCDatePickerDataSource: class {
func datepicker(_ picker: CCDatePicker, numberOfRowsInComponent component: Int) -> Int
func datepicker(_ picker: CCDatePicker, intValueForRow row: Int, forComponent component: Int) -> Int
}
class CCDatePicker: UIControl {
weak var delegate: CCDatePickerDelegate?
weak var dataSource: CCDatePickerDataSource?
let pickerDataSize = 120_000 // for loop loading
/// 单位字符
// var unitName: (year: String?, month: String?, day: String?) = ("年", "月", "日")
var unitName: (year: String?, month: String?, day: String?) = ("", "", "")
/// 标题字体
var titleFont = UIFont.systemFont(ofSize: 20)
/// 标题颜色
var titleColor = UIColor.white
/// 中心行高
var rowHeight: CGFloat = 45
/// 分割线颜色
var separatorColor = UIColor.black {
didSet{
setNeedsLayout()
layoutIfNeeded()
}
}
/// 当前选择的日期
var currentDate: Date {
let year = currentYearInt()
let month = currentMonthInt()
let day = currentDayInt()
let date = Date.cc_defaultFormatter.date(from: "\(month)-\(day)-\(year)")
return date!
}
fileprivate let componentCount = 3
fileprivate var manager: CCDateManager?
fileprivate lazy var pickerview: UIPickerView = {
let tmpv = UIPickerView.init()
tmpv.delegate = self
tmpv.dataSource = self
return tmpv
}()
required init?(frame: CGRect = .zero, minDate: Date, maxDate: Date) {
super.init(frame: frame)
if minDate.compare(maxDate) == .orderedDescending {
return nil
}
manager = CCDateManager.init(minDate: minDate, maxDate: maxDate)
manager?.delegate = self
self.dataSource = manager
pickerview.frame = frame
self.addSubview(pickerview)
self.isUserInteractionEnabled = true
self.isEnabled = true
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder) // 暂不支持xib
}
override func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event) {
super.addTarget(target, action: action, for: controlEvents)
if controlEvents == .valueChanged {
print("123")
print(action.description)
}
}
}
//MARK: ------------------------ Public
extension CCDatePicker {
/// 设置日期,例如`"2007-8-20"`或`"2007-11-9"`
func setDate(_ dateString: String, animated: Bool = false) {
guard let date = Date.cc_defaultFormatter.date(from: dateString) else { return }
setDate(date, animated: animated)
}
func setDate(_ date: Date, animated: Bool = false) {
let rowInfo = manager?.setDate(date)
if let info = rowInfo {
pickerview.selectRow(info.mRow, inComponent: 0, animated: animated)
pickerview.selectRow(info.dRow, inComponent: 1, animated: animated)
pickerview.selectRow(info.yRow, inComponent: 2, animated: animated)
pickerview.reloadAllComponents()
}
}
}
//MARK: ------------------------ Private
extension CCDatePicker{
/// 分割线views
fileprivate var separatorLines: [UIView] {
return pickerview.subviews.filter {
$0.bounds.height < 1.0 && $0.bounds.width == pickerview.bounds.width
}
}
override func layoutSubviews() {
super.layoutSubviews()
pickerview.frame = self.bounds
separatorLines.forEach { $0.backgroundColor = separatorColor }
}
}
extension CCDatePicker: CCDateSelectionDelegate {
func currentMonthInt() -> Int {
let row = pickerview.selectedRow(inComponent: 0)
let attrStr = self.pickerView(pickerview, attributedTitleForRow: row, forComponent: 0)
let value: Int = attrStr?.string.getInt() ?? 1
return value
}
func currentDayInt() -> Int {
let row = pickerview.selectedRow(inComponent: 1)
let attrStr = self.pickerView(pickerview, attributedTitleForRow: row, forComponent: 1)
let value: Int = attrStr?.string.getInt() ?? 1
return value
}
func currentYearInt() -> Int {
let row = pickerview.selectedRow(inComponent: 2)
let attrStr = self.pickerView(pickerview, attributedTitleForRow: row, forComponent: 2)
let value: Int = attrStr?.string.getInt() ?? 1
return value
}
}
extension CCDatePicker: UIPickerViewDelegate{
func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
switch component {
case 0: return pickerView.bounds.width * 0.5 * (1 - 0.45) // 根据字符宽度测得比例
case 1: return pickerView.bounds.width * 0.5 * (1 - 0.45)
case 2: return pickerView.bounds.width * 0.45
default: return 0
}
}
func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
return rowHeight
}
func pickerView(_ pickerView: UIPickerView, attributedTitleForRow row: Int, forComponent component: Int) -> NSAttributedString? {
var ostr = ""
let intValue = self.dataSource?.datepicker(self, intValueForRow: row, forComponent: component) ?? 1
switch component {
case 0: ostr = String(intValue) + (unitName.month ?? "")
case 1: ostr = String(intValue) + (unitName.day ?? "")
case 2: ostr = String(intValue) + (unitName.year ?? "")
default: break
}
let attStr = NSMutableAttributedString(string: ostr)
attStr.addAttributes([.foregroundColor: titleColor, .font: titleFont], range: NSMakeRange(0, ostr.count))
return attStr
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
let attrText = self.pickerView(pickerView, attributedTitleForRow: row, forComponent: component)
if let label = view as? UILabel {
label.attributedText = attrText
return label
}
let newlabel = UILabel.init()
newlabel.backgroundColor = UIColor.clear
newlabel.textAlignment = .center
newlabel.attributedText = attrText
return newlabel
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch component {
case 0: // month
let position = pickerDataSize / 2 + row % (manager?.months_available.count)!
pickerView.selectRow(position, inComponent: 0, animated: false)
// call the funtion to reload the days_available array accodrig to the diffrent moth
// so that the position will fall into the right day
if let dRow = manager?.onMonthRefreshed() {
pickerView.reloadComponent(1)
let day_position = dRow % (manager?.days_available.count)!
pickerview.selectRow(day_position, inComponent: 1, animated: false)
self.pickerView(pickerView, didSelectRow: day_position, inComponent: 1)
}
case 1: // day
self.delegate?.didSelectDate(at: self)
case 2: // year
if let mRow = manager?.onYearRefreshed(){
pickerView.reloadComponent(0)
pickerview.selectRow(mRow, inComponent: 0, animated: false)
self.pickerView(pickerView, didSelectRow: mRow, inComponent: 0)
}
default: break
}
}
}
extension CCDatePicker: UIPickerViewDataSource{
func numberOfComponents(in pickerView: UIPickerView) -> Int { return componentCount }
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
let rowCount = self.dataSource?.datepicker(self, numberOfRowsInComponent: component) ?? 0
return rowCount
}
}
extension String {
fileprivate func getInt() -> Int {
let scanner = Scanner(string: self)
scanner.scanUpToCharacters(from: CharacterSet.decimalDigits, into: nil)
var number: Int = 0
scanner.scanInt(&number)
return number
}
}
protocol CCDateSelectionDelegate: class {
func currentYearInt() -> Int
func currentMonthInt() -> Int
func currentDayInt() -> Int
}
/// 日期数据管理类
class CCDateManager {
fileprivate lazy var months_: [Int] = {
var arr = [Int]()
for i in 1...12 { arr.append(i) }
return arr
}()
fileprivate lazy var days_: [Int] = {
var arr = [Int]()
for i in 1...31 { arr.append(i) }
return arr
}()
/// 最小的日期
fileprivate let minDate: Date
/// 最大的日期
fileprivate let maxDate: Date
fileprivate var months_available :[Int]
fileprivate var days_available :[Int]
weak var delegate: CCDateSelectionDelegate?
init(minDate: Date, maxDate: Date) {
self.minDate = minDate
self.maxDate = maxDate
self.months_available = []
self.days_available = []
}
}
extension CCDateManager {
@discardableResult
func setDate(_ date: Date) -> (yRow: Int, mRow: Int, dRow: Int)? {
if date.compare(minDate) == .orderedAscending || date.compare(maxDate) == .orderedDescending {
NSLog("指定日期超过了可选范围")
return nil
}
let result = refreshCurrent(year: date.year, month: date.month, day: date.day)
return result
}
/// 更新`年`的选择,返回新的`月`index
func onYearRefreshed() -> Int {
let year = self.delegate?.currentYearInt() ?? 1
let month = self.delegate?.currentMonthInt() ?? 1
handleRefreshMonthsOf(year: year)
var mRow = months_available.index(of: month) ?? 0
if let monthLast = months_available.last, let monthFirst = months_available.first {
if month < monthFirst {
mRow = 0
} else if month > monthLast {
mRow = months_available.count - 1
}
}
return mRow
}
/// 更新`月`的选择,返回新的`日`index
func onMonthRefreshed() -> Int {
let year = self.delegate?.currentYearInt() ?? 1
let month = self.delegate?.currentMonthInt() ?? 1
let day = self.delegate?.currentDayInt() ?? 1
handleRefreshDaysOf(year: year, month: month)
var dRow = days_available.index(of: day) ?? 0
if let dayLast = days_available.last, let dayFirst = days_available.first {
if day < dayFirst {
dRow = 0
} else if day > dayLast {
dRow = days_available.count - 1
}
}
return dRow
}
}
extension CCDateManager {
fileprivate func refreshCurrent(year: Int, month: Int, day: Int) -> (yRow: Int, mRow: Int, dRow: Int) {
handleRefreshMonthsOf(year: year)
handleRefreshDaysOf(year: year, month: month)
var mRow = months_available.index(of: month) ?? 0
if let monthLast = months_available.last, let monthFirst = months_available.first {
if month < monthFirst {
mRow = 0
} else if month > monthLast {
mRow = months_available.count - 1
}
}
var dRow = days_available.index(of: day) ?? 0
if let dayLast = days_available.last, let dayFirst = days_available.first {
if day < dayFirst {
dRow = 0
} else if day > dayLast {
dRow = days_available.count - 1
}
}
let yRow = year - minDate.year
return (yRow, mRow, dRow)
}
/// 处理`月`范围
fileprivate func handleRefreshMonthsOf(year: Int) {
if (maxDate.year == minDate.year) {
months_available = months_.filter({ $0 >= minDate.month && $0 <= maxDate.month })
} else {
if year == minDate.year {
months_available = months_.filter({ $0 >= minDate.month })
} else if year == maxDate.year {
months_available = months_.filter({ $0 <= maxDate.month })
} else {
months_available = months_
}
}
}
/// 处理`日`范围
fileprivate func handleRefreshDaysOf(year: Int, month: Int) {
let fullDays = Date.fullDaysOf(year: year, month: month)
if (maxDate.year == minDate.year) {
if (maxDate.month == minDate.month){
days_available = days_.filter({ $0 >= minDate.day && $0 <= maxDate.day })
} else {
if (month == minDate.month) {
days_available = days_.filter({ $0 >= minDate.day && $0 <= fullDays })
} else if (month == maxDate.month) {
days_available = days_.filter({ $0 <= maxDate.day })
} else {
days_available = days_.filter({ $0 <= fullDays })
}
}
} else {
if year == minDate.year {
if month == minDate.month {
days_available = days_.filter({ $0 >= minDate.day && $0 <= fullDays })
} else {
days_available = days_.filter({ $0 <= fullDays })
}
} else if year == maxDate.year {
if month == maxDate.month {
days_available = days_.filter({ $0 <= maxDate.day })
} else {
days_available = days_.filter({ $0 <= fullDays })
}
} else {
days_available = days_.filter({ $0 <= fullDays })
}
}
}
}
extension CCDateManager {
fileprivate func numberOfRowsInComponent(_ component: Int) -> Int {
switch component {
case 0:
// return months_available.count
return 120_000 // for loop loading
case 1:
// return days_available.count
return 120_000 // for loop loading
case 2:
return (maxDate.year - minDate.year) + 1
default: return 0
}
}
fileprivate func intValueForRow(row: Int, forComponent component: Int) -> Int{
switch component {
case 0:
return months_available[row % months_available.count]
case 1:
return days_available[row % days_available.count ]
case 2:
return minDate.year + row
default: return 1
}
}
}
extension CCDateManager: CCDatePickerDataSource {
func datepicker(_ picker: CCDatePicker, numberOfRowsInComponent component: Int) -> Int {
return self.numberOfRowsInComponent(component)
}
func datepicker(_ picker: CCDatePicker, intValueForRow row: Int, forComponent component: Int) -> Int {
return self.intValueForRow(row: row, forComponent: component)
}
}
extension Date {
static var cc_defaultFormatter: DateFormatter {
return self.dateFormatterWith("MM-dd-yyyy")
}
/// 自定义时间格式的格式化器
fileprivate static func dateFormatterWith(_ formatString: String) -> DateFormatter {
let threadDic = Thread.current.threadDictionary
if let fmt = threadDic.object(forKey: formatString) as? DateFormatter {
return fmt
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = formatString
threadDic.setObject(dateFormatter, forKey: formatString as NSCopying)
return dateFormatter
}
/// 指定年月的天数
fileprivate static func fullDaysOf(year: Int, month: Int) -> Int {
if [1, 3, 5, 7, 8, 10, 12].contains(month) { return 31 }
if [4, 6, 9, 11].contains(month) { return 30 }
let isLeapYear = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
return isLeapYear ? 29 : 28 // 二月
}
fileprivate var year: Int {
return NSCalendar.current.component(.year, from: self)
}
fileprivate var month: Int {
return NSCalendar.current.component(.month, from: self)
}
fileprivate var day: Int {
return NSCalendar.current.component(.day, from: self)
}
}
最佳答案
您希望您的自定义控件在用户在内部选择器 View 中选择一行时发出 valueChanged
事件。
添加行:
sendActions(for: .valueChanged)
您希望您的自定义控件在哪里告诉所有已注册的目标值已更改。至少这将在您的 pickerView:didSelectRow
委托(delegate)方法的实现中。
一些注意事项:
addTarget
。CCDatePickerDelegate
委托(delegate)协议(protocol),因此您想使用 addTarget
在值更改时收到通知。关于ios - 如何将监听器添加到 UIControl 的子类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56894479/
我正在制作一个自定义 iOS 键盘,并有一个 UIControl 子类来表示我的按钮。我试图获得与普通 iOS 键盘相同的行为: 用户开始触摸一个按钮 用户拖动其他按钮(需要检测到这一点,以便他们可以
这是设置:我在表格单元格中有 UIControls,允许从右向左滑动以实现删除功能(如清除) 表格单元格位于 UITableView 中。 TableView 位于另一个 UIControl 中,该
我做了一些关于自旋转的实验。情况:我有一个 TabBar 4(选项卡),三个应该只是纵向。最后一个是 UINavigationController,它本身不应自动旋转任何堆叠的 Controller
我有一个名为 CheckboxView 的 UIControl 自定义子类,当用户点击矩形时,它只是在矩形内绘制一个复选标记。这是作为一个单元格添加到 tableView 中的,它位于主容器 Attr
我注意到,当控件是其他控件的子控件并且部分位于父控件边界之外时,仍然可以看到控件在父控件边界之外的部分。 例如,我将一个图像放置在一个网格内,部分放置在网格边界之外,但该图像仍然完整显示。 如何将要显
我尝试以编程方式将 UITableView 添加到 ScrollView 中。我已经设置了 anchor 和数据源,委托(delegate)。你可以在代码中看到。但我不能。我放了一个断点,一切都运行了
Documentation of UIControl Class : UIControlEventTouchDragInside、UIControlEventTouchDragEnter、UICont
我有两个自定义 UIControl。这些是滚轮。 它们的框架重叠。在屏幕截图中,左上角的那个在右边那个后面。当在重叠区域内启动触摸时,如何在左侧开始跟踪? 我的 beginTrackingWithTo
我创建了第 3 方键盘,并尝试为我的按键添加背景。我使用 UIControl 作为我的键,没有 Storyboard。 我有几种类型的按钮,并尝试为我的角色添加背景 UIView。 let img =
我正在创建 GUI,我希望在那里有输入和结果。 我有 text 字段作为标签,edit 或 popup 用于输入。当我有 uicontrol('style','text','string','foo'
我有一个 UIControl 子类。我使用以下代码更改它的背景颜色: - (void)drawRect:(CGRect)rect { [self.backgroundColor setFill
我有一个 UIControl,它实现了 touches begind 方法,如下所示: - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent
我不想要 UIButton 或类似的东西。我想直接继承 UIControl 并制作我自己的非常特殊的控件。 但由于某种原因,我覆盖的任何方法都没有被调用。目标 Action 的东西有效,目标接收适当的
我的自定义子类扩展 UIControl (EditableImageView)基本上它包含 3 个 subview : UIButton(UIButtonTypeCustom) UIImageView
我目前有一个 UIControl,它有许多 subview (图像、标签)。 不幸的是,当我使用 addTarget 等时,它不会检测到 subview 上的触摸。 [myCustomView a
基本上我需要一个图像按钮,特别是一个自定义对象: 1) 点击时调用 Controller 的操作 2)封装自定义数据 3)由包装 View 自动移动(不相关) 好吧,我通过 UIControl 的子类
我可以在短时间内禁用按钮吗?我有一个注册 View ,在 3 次错误尝试时,它会将用户带到注册 View 之外,并且会在一段时间内禁用注册按钮,比如 5 分钟。是否可以? 最佳答案 - (void)
我已经构建了一个 UIControl 子类来显示 1 个月的日历 View 一部 iPhone。大多数月份需要 5 周才能显示日期,但 有些月需要 6,所以我建立了动态调整大小的控件 自己根据需要
我创建了一个名为“TestButton”的 UIControl 子类,带有标签和 imageview subview 。该对象是使用框架创建的,作为初始化过程的一部分,我创建了 subview 元素。
堆栈溢出, 我正在努力扩展 UIControl 以为其自身分配单个值。实现这一点是为了它可以在启动时分配一个值以实现持久性。我的目标是尽可能普遍地做到这一点,以便它可以轻松扩展。我的方法的主要组成部分
我是一名优秀的程序员,十分优秀!