LWDatePickerDialog.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. //
  2. // LWDatePickerDialog.swift
  3. // DatePickerDialogSwift
  4. //
  5. // Created by 刘振兴 on 2018/1/17.
  6. //
  7. import UIKit
  8. private extension Selector {
  9. //按钮点击
  10. static let buttonTapped = #selector(LWDatePickerDialog.buttonTapped)
  11. //设备方向转换
  12. static let deviceOrientationDidChange = #selector(LWDatePickerDialog.deviceOrientationDidChange)
  13. }
  14. struct LWDialogStyle {
  15. //title
  16. static let titleColor = UIColor(hex: "#FFFFFF")
  17. static let titleTextFont = UIFont(name: "PingFangSC-Regular", size: 18)
  18. static let titleViewBackColor = UIColor(hex: "#FB4747")
  19. //DatePicker unSelected TextColor Font
  20. static let dpUnSelTextColor = UIColor(hex: "#999999")
  21. static let dpUnSelTextFont = UIFont(name: "PingFangSC-Regular", size: 18)
  22. static let dpSelTextColor = UIColor(hex: "#FB4747")
  23. static let dpSelTextFont = UIFont(name: "PingFangSC-Regular", size: 23)
  24. //button
  25. static let okButtonTextColor = UIColor(hex: "#FFFFFF")
  26. static let okButtonFont = UIFont(name: "PingFangSC-Regular", size: 16)
  27. static let okButtonBackColor = UIColor(hex: "#FB4747")
  28. static let cancelButtonTextColor = UIColor(hex: "#FFFFFF")
  29. static let cancelButtonFont = UIFont(name: "PingFangSC-Regular", size: 16)
  30. static let cancelButtonBackColor = UIColor(hex: "#CCCCCC")
  31. // MARK: - Constants
  32. static let defaultWidth:CGFloat = 300
  33. static let defaultTitleContainerHeight:CGFloat = 50
  34. static let defaultTitleHeight:CGFloat = 35
  35. static let defaultDatePickerHeight:CGFloat = 230
  36. static let defaultButtonContainerHeight:CGFloat = 50
  37. static let defaultButtonHeight: CGFloat = 35
  38. static let defaultButtonSpacerHeight: CGFloat = 1
  39. static let cornerRadius: CGFloat = 15
  40. static let doneButtonTag: Int = 1
  41. }
  42. open class LWDatePickerDialog: UIView {
  43. //回调类型定义
  44. public typealias DatePickerCallback = ( Date? ) -> Void
  45. // MARK: - Views
  46. private var dialogView: UIView!
  47. //title view
  48. private var titleContainerView:UIView!
  49. private var titleLabel: UILabel!
  50. //picker view
  51. open var datePicker: UIDatePicker!
  52. //button view
  53. private var buttonContainerView:UIView!
  54. private var cancelButton: UIButton!
  55. private var doneButton: UIButton!
  56. // MARK: - Variables
  57. private var defaultDate: Date?
  58. private var datePickerMode: UIDatePicker.Mode?
  59. private var callback: DatePickerCallback?
  60. var showCancelButton: Bool = false
  61. var locale: Locale?
  62. private var textColor: UIColor!
  63. private var buttonColor: UIColor!
  64. private var font: UIFont!
  65. // MARK: - Dialog initialization
  66. public init(textColor: UIColor = UIColor.black,
  67. buttonColor: UIColor = UIColor.blue,
  68. font: UIFont = .boldSystemFont(ofSize: 15),
  69. locale: Locale? = nil,
  70. showCancelButton: Bool = true) {
  71. let size = UIScreen.main.bounds.size
  72. super.init(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
  73. self.textColor = textColor
  74. self.buttonColor = buttonColor
  75. self.font = font
  76. self.showCancelButton = showCancelButton
  77. self.locale = locale
  78. setupView()
  79. }
  80. required public init?(coder aDecoder: NSCoder) {
  81. super.init(coder: aDecoder)
  82. }
  83. func setupView() {
  84. self.dialogView = createContainerView()
  85. self.dialogView!.layer.shouldRasterize = true
  86. self.dialogView!.layer.rasterizationScale = UIScreen.main.scale
  87. self.layer.shouldRasterize = true
  88. self.layer.rasterizationScale = UIScreen.main.scale
  89. self.dialogView!.layer.opacity = 0.5
  90. self.dialogView!.layer.transform = CATransform3DMakeScale(1.3, 1.3, 1)
  91. self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
  92. self.addSubview(self.dialogView!)
  93. }
  94. /// Handle device orientation changes
  95. @objc func deviceOrientationDidChange(_ notification: Notification) {
  96. self.frame = UIScreen.main.bounds
  97. let dialogSize = CGSize(width: LWDialogStyle.defaultWidth,height: LWDialogStyle.defaultTitleContainerHeight + LWDialogStyle.defaultDatePickerHeight + LWDialogStyle.defaultButtonContainerHeight)
  98. dialogView.frame = CGRect(x: (UIScreen.main.bounds.size.width - dialogSize.width) / 2,
  99. y: (UIScreen.main.bounds.size.height - dialogSize.height) / 2,
  100. width: dialogSize.width,
  101. height: dialogSize.height)
  102. }
  103. /// Create the dialog view, and animate opening the dialog
  104. open func show(_ title: String,
  105. doneButtonTitle: String = "Done",
  106. cancelButtonTitle: String = "Cancel",
  107. defaultDate: Date = Date(),
  108. minimumDate: Date? = nil, maximumDate: Date? = nil,
  109. datePickerMode: UIDatePicker.Mode = .dateAndTime,
  110. callback: @escaping DatePickerCallback) {
  111. self.titleLabel.text = title
  112. self.doneButton.setTitle(doneButtonTitle, for: .normal)
  113. if showCancelButton {
  114. self.cancelButton.setTitle(cancelButtonTitle, for: .normal)
  115. }
  116. self.datePickerMode = datePickerMode
  117. self.callback = callback
  118. self.defaultDate = defaultDate
  119. self.datePicker.datePickerMode = self.datePickerMode ?? UIDatePicker.Mode.date
  120. self.datePicker.date = self.defaultDate ?? Date()
  121. self.datePicker.maximumDate = maximumDate
  122. self.datePicker.minimumDate = minimumDate
  123. if let locale = self.locale {
  124. self.datePicker.locale = locale
  125. }
  126. /* Add dialog to main window */
  127. guard let appDelegate = UIApplication.shared.delegate else { fatalError() }
  128. guard let window = appDelegate.window else { fatalError() }
  129. window?.addSubview(self)
  130. window?.bringSubviewToFront(self)
  131. window?.endEditing(true)
  132. NotificationCenter.default.addObserver(self,
  133. selector: .deviceOrientationDidChange,
  134. name: UIDevice.orientationDidChangeNotification,
  135. object: nil)
  136. /* Anim */
  137. UIView.animate(
  138. withDuration: 0.2,
  139. delay: 0,
  140. options: .curveEaseInOut,
  141. animations: {
  142. self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
  143. self.dialogView!.layer.opacity = 1
  144. self.dialogView!.layer.transform = CATransform3DMakeScale(1, 1, 1)
  145. }
  146. )
  147. }
  148. /// Dialog close animation then cleaning and removing the view from the parent
  149. private func close() {
  150. let currentTransform = self.dialogView.layer.transform
  151. let startRotation = (self.value(forKeyPath: "layer.transform.rotation.z") as? NSNumber) as? Double ?? 0.0
  152. let rotation = CATransform3DMakeRotation((CGFloat)(-startRotation + .pi * 270 / 180), 0, 0, 0)
  153. self.dialogView.layer.transform = CATransform3DConcat(rotation, CATransform3DMakeScale(1, 1, 1))
  154. self.dialogView.layer.opacity = 1
  155. UIView.animate(
  156. withDuration: 0.2,
  157. delay: 0,
  158. options: [],
  159. animations: {
  160. self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
  161. let transform = CATransform3DConcat(currentTransform, CATransform3DMakeScale(0.6, 0.6, 1))
  162. self.dialogView.layer.transform = transform
  163. self.dialogView.layer.opacity = 0
  164. }) { (_) in
  165. for v in self.subviews {
  166. v.removeFromSuperview()
  167. }
  168. self.removeFromSuperview()
  169. self.setupView()
  170. }
  171. }
  172. /// Creates the container view here: create the dialog, then add the custom content and buttons
  173. private func createContainerView() -> UIView {
  174. let screenSize = UIScreen.main.bounds.size
  175. //title + datePicker + button height
  176. let dialogSize = CGSize(width: LWDialogStyle.defaultWidth, height: LWDialogStyle.defaultTitleContainerHeight + LWDialogStyle.defaultDatePickerHeight + LWDialogStyle.defaultButtonContainerHeight)
  177. // For the black background
  178. self.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height)
  179. // This is the dialog's container; we attach the custom content and the buttons to this one
  180. let container = UIView(frame: CGRect(x: (screenSize.width - dialogSize.width) / 2,
  181. y: (screenSize.height - dialogSize.height) / 2,
  182. width: dialogSize.width,
  183. height: dialogSize.height))
  184. // First, we style the dialog to match the iOS8 UIAlertView >>>
  185. let gradient: CAGradientLayer = CAGradientLayer(layer: self.layer)
  186. gradient.frame = container.bounds
  187. gradient.colors = [UIColor(red: 218/255, green: 218/255, blue: 218/255, alpha: 1).cgColor,
  188. UIColor(red: 233/255, green: 233/255, blue: 233/255, alpha: 1).cgColor,
  189. UIColor(red: 218/255, green: 218/255, blue: 218/255, alpha: 1).cgColor]
  190. let cornerRadius = LWDialogStyle.cornerRadius
  191. gradient.cornerRadius = cornerRadius
  192. container.layer.insertSublayer(gradient, at: 0)
  193. container.layer.cornerRadius = cornerRadius
  194. container.layer.masksToBounds = true
  195. container.layer.borderColor = UIColor(red: 198/255, green: 198/255, blue: 198/255, alpha: 1).cgColor
  196. container.layer.borderWidth = 1
  197. container.layer.shadowRadius = cornerRadius + 5
  198. container.layer.shadowOpacity = 0.1
  199. container.layer.shadowOffset = CGSize(width: 0 - (cornerRadius + 5) / 2, height: 0 - (cornerRadius + 5) / 2)
  200. container.layer.shadowColor = UIColor.black.cgColor
  201. container.layer.shadowPath = UIBezierPath(roundedRect: container.bounds,
  202. cornerRadius: container.layer.cornerRadius).cgPath
  203. // There is a line above the button
  204. // let yPosition = container.bounds.size.height - kDefaultButtonHeight - kDefaultButtonSpacerHeight
  205. // let lineView = UIView(frame: CGRect(x: 0,
  206. // y: yPosition,
  207. // width: container.bounds.size.width,
  208. // height: kDefaultButtonSpacerHeight))
  209. // lineView.backgroundColor = UIColor(red: 198/255, green: 198/255, blue: 198/255, alpha: 1)
  210. // container.addSubview(lineView)
  211. //Title
  212. self.titleContainerView = UIView(frame: CGRect(x: 0, y: 0, width: LWDialogStyle.defaultWidth, height: LWDialogStyle.defaultTitleContainerHeight))
  213. self.titleContainerView.backgroundColor = LWDialogStyle.titleViewBackColor
  214. self.titleLabel = UILabel(frame: CGRect(x: 20, y: (LWDialogStyle.defaultTitleContainerHeight-LWDialogStyle.defaultTitleHeight)/2, width: LWDialogStyle.defaultWidth - 50, height: LWDialogStyle.defaultTitleHeight))
  215. self.titleLabel.textAlignment = .left
  216. self.titleLabel.textColor = LWDialogStyle.titleColor
  217. self.titleLabel.font = LWDialogStyle.titleTextFont
  218. self.titleContainerView.addSubview(self.titleLabel)
  219. container.addSubview(self.titleContainerView)
  220. //DatePicker
  221. self.datePicker = configuredDatePicker()
  222. container.addSubview(self.datePicker)
  223. // Add the buttons
  224. self.buttonContainerView = UIView(frame: CGRect(x: 0, y: LWDialogStyle.defaultTitleContainerHeight + LWDialogStyle.defaultDatePickerHeight, width: LWDialogStyle.defaultWidth, height: LWDialogStyle.defaultButtonContainerHeight))
  225. self.backgroundColor = UIColor.white
  226. addButtonsToView(container: buttonContainerView)
  227. container.addSubview(self.buttonContainerView)
  228. return container
  229. }
  230. fileprivate func configuredDatePicker() -> UIDatePicker {
  231. let datePicker = UIDatePicker(frame: CGRect(x: 0, y: LWDialogStyle.defaultTitleContainerHeight, width: 0, height: 0))
  232. datePicker.setValue(LWDialogStyle.dpSelTextColor, forKeyPath: "textColor")
  233. datePicker.autoresizingMask = .flexibleRightMargin
  234. datePicker.frame.size.width = LWDialogStyle.defaultWidth
  235. datePicker.frame.size.height = LWDialogStyle.defaultDatePickerHeight
  236. return datePicker
  237. }
  238. /// Add buttons to container
  239. private func addButtonsToView(container: UIView) {
  240. var buttonWidth = (container.bounds.size.width - 20*2) / 2
  241. var leftButtonFrame = CGRect(
  242. x: 10,
  243. y: (container.bounds.size.height - LWDialogStyle.defaultButtonHeight)/2,
  244. width: buttonWidth,
  245. height: LWDialogStyle.defaultButtonHeight
  246. )
  247. var rightButtonFrame = CGRect(
  248. x: 10 + buttonWidth + 10 * 2,
  249. y: (container.bounds.size.height - LWDialogStyle.defaultButtonHeight)/2,
  250. width: buttonWidth,
  251. height: LWDialogStyle.defaultButtonHeight
  252. )
  253. if showCancelButton == false {
  254. buttonWidth = container.bounds.size.width
  255. leftButtonFrame = CGRect()
  256. rightButtonFrame = CGRect(
  257. x: (LWDialogStyle.defaultWidth - buttonWidth) / 2,
  258. y: (container.bounds.size.height - LWDialogStyle.defaultButtonHeight)/2,
  259. width: buttonWidth,
  260. height: LWDialogStyle.defaultButtonHeight
  261. )
  262. }
  263. let interfaceLayoutDirection = UIApplication.shared.userInterfaceLayoutDirection
  264. let isLeftToRightDirection = interfaceLayoutDirection == .leftToRight
  265. if showCancelButton {
  266. self.cancelButton = UIButton(type: .custom) as UIButton
  267. self.cancelButton.frame = isLeftToRightDirection ? leftButtonFrame : rightButtonFrame
  268. self.cancelButton.setTitleColor(LWDialogStyle.cancelButtonTextColor, for: .normal)
  269. self.cancelButton.setTitleColor(LWDialogStyle.cancelButtonTextColor, for: .highlighted)
  270. self.cancelButton.backgroundColor = LWDialogStyle.cancelButtonBackColor
  271. self.cancelButton.titleLabel!.font = LWDialogStyle.cancelButtonFont
  272. self.cancelButton.layer.cornerRadius = LWDialogStyle.cornerRadius
  273. self.cancelButton.addTarget(self, action: .buttonTapped, for: .touchUpInside)
  274. container.addSubview(self.cancelButton)
  275. }
  276. self.doneButton = UIButton(type: .custom) as UIButton
  277. self.doneButton.frame = isLeftToRightDirection ? rightButtonFrame : leftButtonFrame
  278. self.doneButton.tag = LWDialogStyle.doneButtonTag
  279. self.doneButton.backgroundColor = LWDialogStyle.okButtonBackColor
  280. self.doneButton.setTitleColor(LWDialogStyle.okButtonTextColor, for: .normal)
  281. self.doneButton.setTitleColor(LWDialogStyle.okButtonTextColor, for: .highlighted)
  282. self.doneButton.titleLabel!.font = LWDialogStyle.okButtonFont
  283. self.doneButton.layer.cornerRadius = LWDialogStyle.cornerRadius
  284. self.doneButton.addTarget(self, action: .buttonTapped, for: .touchUpInside)
  285. container.addSubview(self.doneButton)
  286. }
  287. @objc func buttonTapped(sender: UIButton!) {
  288. if sender.tag == LWDialogStyle.doneButtonTag {
  289. self.callback?(self.datePicker.date)
  290. } else {
  291. self.callback?(nil)
  292. }
  293. close()
  294. }
  295. deinit {
  296. NotificationCenter.default.removeObserver(self)
  297. }
  298. }