在iOS 14中如何通过程序打开UIDatePicker?

67

我有一个普通的 UIDatePicker(抱歉,它不是英文,请忽略):

我的日期选择器

但问题是只有在我按下日期选择器时它才会出现:

图片

我想知道如何像第一张图中那样显示日期选择器(覆盖整个屏幕并带有模糊效果),仅通过代码,这样用户就不需要按下日期选择器来显示日期选择器。谢谢。


1
你是如何创建那个用户界面的?用storyboard还是代码实现的?你有outlet吗? - zaitsman
1
你可以使用 yourPicker.becomeFirstResponder() 强制使一个对象成为第一响应者。这应该更或多或少地模拟了对日期字段的轻触。 - Sanzio Angeli
1
@SanzioAngeli 请将此作为答案提交并提供更多细节。 - אורי orihpt
1
经过进一步测试尝试得出答案,你是正确的,这种方法不起作用。这仅适用于“ .wheels”样式(这就是我所使用的样式)。对于造成的混乱,非常抱歉。 - Sanzio Angeli
1
@אוריorihpt 在这里检查代码:https://gist.github.com/vvit/22b33b779ba85db85e02bc5bcc5979a0 - SoftDesigner
显示剩余8条评论
16个回答

15
您可以使用 .compact 样式的 datePicker,并在其上方放置一个 UILabel,并为其设置背景颜色以隐藏 datePicker,然后确保标签的用户交互已禁用,然后您就可以在 datePicker 上轻触它,它将显示模态视图,然后更改 datePicker 的值,您可以使用自己的日期格式化程序相应地设置标签的文本,您还可以将 datePicker 和标签都放在一个 UIView 中,并将其裁剪到边界内。

这是一个聪明的解决方法。它对我起作用了。我有两个并排的日期选择器。只需要确保它们正确对齐,以便来自(较小)标签的点击可以穿过即可。 - Dylan
我曾经使用这个解决方法,在日期选择器被点击时隐藏标签,但是iOS 15导致我无法再捕获来自日期选择器的点击。https://dev59.com/7WoMtIcB2Jgan1znkItD - teradyl

9

很遗憾,目前无法编程显示日期选择器弹出窗口,至少不能使用可能导致您的应用被拒绝的黑客技巧:

  • UIDatePicker控件不公开任何可通过sendActions(for:)触发的事件-allControlEvents返回一个空选项集

  • 在iOS上正式不可能模拟用户点击-没有公共API可以实现这一点,所有解决方法都意味着诉诸私有API

  • 如果以上还不够,那么UIDatePicker的UI结构由私有类组成:

    enter image description here enter image description here

结论是,在其当前状态下,只能通过用户交互来控制UIDatePicker UI。也许将来会改变,例如UIButton公开操作以触发与用户点击按钮时相同的行为,因此也许UIDatePicker将来会公开这样的操作。


1
除非应用程序崩溃,否则没有人会拒绝它们。 - Dmitry
1
@Dmitry,使用私有(iOS SDK)API肯定会导致您的二进制文件在提交到AppStore后被自动拒绝。 - Cristik
1
从来没有遇到过这种情况。 - Dmitry

9

我也有同样的问题。我使用以下 hack:

let datePicker = UIDatePicker(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
datePicker.preferredDatePickerStyle = .compact

// Removes Labels
datePicker.subviews.forEach({ $0.subviews.forEach({ $0.removeFromSuperview() }) })

然后我只需在视图顶部添加此日期选择器,它将打开此弹出窗口。


8
感谢您的建议,但我不会将其标记为已接受,因为它并没有回答我所有的问题。用户仍然需要按日期选择器才能打开它。我想要的解决方案是只用代码而不需要用户按键来完成这个操作。 - אורי orihpt
我认为这是最接近实现这个功能的方法。 - Shunan
我看到标签消失了,但是日历没有出现。 - clearlight

8

无法使用UIKit14中的代码打开UIDatePicker

  • UIDatePicker没有相应的API
  • UIKit不允许我们创建一个对UIKit产生任何影响的UIEventsUITouch对象。

1
我不这么认为。 - אורי orihpt
3
我已经发布了一个悬赏任务,并且很高兴看到有人能够证明我是错误的。 - Blazej SLEBODA
我有一个问题,也许可以帮助你解决这个问题,是什么会触发日期选择器的出现?我知道你说你想要通过编程实现,但必须有一个动作来触发它。用户会点击任何东西吗?应用程序会在特定点或行为时打开它吗? - J Arango
@JArango 在我的特定代码中,触发器是按下UIButton。但我一般性地询问如何使用代码完成此操作,无论触发器是什么。 - אורי orihpt
我可以告诉你某件事情是做不到的,但你永远不能说它是不可能的。在这个世界上,一切皆有可能。 - Dmitry
显示剩余2条评论

6
我不知道这是否仍然有效,但是您可以利用新的UIDatePicker的.inline样式。然后使用自己的视图和动画来模仿.compact样式的DatePicker的外观。
let secondDatePicker = UIDatePicker()
secondDatePicker.preferredDatePickerStyle = .inline
self.view.addSubview(secondDatePicker)

secondDatePicker.translatesAutoresizingMaskIntoConstraints = false
secondDatePicker.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
secondDatePicker.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

如果您能很好地调整弹出窗口的大小,那么这非常聪明。 - spentag

5

更新:在iOS15上,这个方法不再有效。

@Amr Mohamed的答案很聪明,他给日期选择器视图添加了一个标签,但是当点击日期选择器视图时,这也会导致一个故障,当日期选择器视图动画退出时,你也可以看到日期选择器。

所以我们需要“隐藏”日期选择器视图,但又让它保持可交互状态。(我们不能只使用isHidden = true或者alpha = 0,否则就无法点击了)

这是我在我的项目中所做的:

extension UIView {
    func loopDescendantViews(_ closure: (_ subView: UIView) -> Void) {
        for v in subviews {
            closure(v)
            v.loopDescendantViews(closure)
        }
    }
}

let datePicker = UIDatePicker()
datePicker.preferredDatePickerStyle = .compact
datePicker.loopDescendantViews {
    $0.alpha = $0 is UIControl ? 1 : 0
}

接下来,您可以将这个不可见的日期选择器放在标签/其他视图之上。一旦您点击标签(实际上是点击了日期选择器),日期选择器视图控制器将会动画弹出。


太棒了。谢谢! - nil
这段代码无法工作,因为Alpha值被设置为0的视图将变得不可点击。 - mykolaj

2

为了确保我们没有漏掉任何显而易见的东西,.inline 样式可以在不需要用户单击日期或时间标签的情况下显示完整的日历。有了这个功能,可以安排任何与显示日历和选定/默认日期等相关的行为...


我们知道的。 - אורי orihpt
1
只是以编程方式触发它的变通方法和黑客技巧如此严苛,以至于人们需要知道有一种可靠的方法来实现它,但我从快速阅读文档中没有了解到,因为默认模式是紧凑模式。所以我认为你必须点击标签才能看到日历。因此,即使您知道并且真正关注您的问题和所有细节的人可能会假设所有这些,但仍然有可能有些人会受益于有人指出这一点。事实上,在我找到.inline可以满足我的需求之前,我非常接近使用第三方日期选择器。 - clearlight

2
您可以从storyboard中将日期选择器的首选样式设置为"inline"。这样做,您将直接获得全屏幕进行日期选择。
以下是首选样式为"compact"(您当前使用的样式)的日期选择器的外观: date picker with preferred style as compact 与首选样式为"inline"(您所寻找的样式)的日期选择器相比,它的外观如下: date picker with preferred style as inline

1

我找到了一种方法,可以让它在iOS 14和iOS 15上都能正常工作,但由于它读取层次结构,所以可能会在任何即将推出的iOS版本中停止工作:

// The callback from your tap gesture or whatever you use:
func handleTapOnView() {
    let datePickerGesture: UIGestureRecognizer? = {
        guard let compactView = datePicker.subviews.first else { return nil }
        // iOS 15
        if let dateLabel = compactView.subviews
                .first(where: { String(describing: type(of: $0)).hasSuffix("DateLabel") }),
           let gesture = dateLabel.gestureRecognizers?.first {
            return gesture
        }
        // iOS 14
        if let gesture = compactView.gestureRecognizers?.last {
            return gesture
        }
        return nil
    }()
    datePickerGesture?.state = .ended
}

在代码的“iOS 15”部分,如果您想要聚焦日期选择部分,请使用.hasSuffix("DateLabel"),如果想要聚焦时间选择部分,请使用.hasSuffix("TimeLabel")

0

我正在使用这个扩展来设置UIDatePicker的每个子视图的clear颜色,然后像其他人建议的那样使用一个UILabel


extension UIDatePicker {
  
  private func traverse(view: UIView) {
    for subview in view.subviews {
      self.traverse(view: subview)

      // Setting alpha to 0 disables userInteraction.
      subview.alpha = 0.02
    }
  }
  
  func paintClear() {
    self.traverse(view: self)
  }
  
}



网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接