IQTextView.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. //
  2. // IQTextView.swift
  3. // https://github.com/hackiftekhar/IQKeyboardManager
  4. // Copyright (c) 2013-16 Iftekhar Qurashi.
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. import UIKit
  24. /** @abstract UITextView with placeholder support */
  25. open class IQTextView: UITextView {
  26. @objc required public init?(coder aDecoder: NSCoder) {
  27. super.init(coder: aDecoder)
  28. #if swift(>=4.2)
  29. let UITextViewTextDidChange = UITextView.textDidChangeNotification
  30. #else
  31. let UITextViewTextDidChange = Notification.Name.UITextViewTextDidChange
  32. #endif
  33. NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextViewTextDidChange, object: self)
  34. }
  35. @objc override public init(frame: CGRect, textContainer: NSTextContainer?) {
  36. super.init(frame: frame, textContainer: textContainer)
  37. #if swift(>=4.2)
  38. let notificationName = UITextView.textDidChangeNotification
  39. #else
  40. let notificationName = Notification.Name.UITextViewTextDidChange
  41. #endif
  42. NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: notificationName, object: self)
  43. }
  44. @objc override open func awakeFromNib() {
  45. super.awakeFromNib()
  46. #if swift(>=4.2)
  47. let UITextViewTextDidChange = UITextView.textDidChangeNotification
  48. #else
  49. let UITextViewTextDidChange = Notification.Name.UITextViewTextDidChange
  50. #endif
  51. NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextViewTextDidChange, object: self)
  52. }
  53. deinit {
  54. placeholderLabel.removeFromSuperview()
  55. NotificationCenter.default.removeObserver(self)
  56. }
  57. private var placeholderInsets: UIEdgeInsets {
  58. return UIEdgeInsets(top: self.textContainerInset.top, left: self.textContainerInset.left + self.textContainer.lineFragmentPadding, bottom: self.textContainerInset.bottom, right: self.textContainerInset.right + self.textContainer.lineFragmentPadding)
  59. }
  60. private var placeholderExpectedFrame: CGRect {
  61. let placeholderInsets = self.placeholderInsets
  62. let maxWidth = self.frame.width-placeholderInsets.left-placeholderInsets.right
  63. let expectedSize = placeholderLabel.sizeThatFits(CGSize(width: maxWidth, height: self.frame.height-placeholderInsets.top-placeholderInsets.bottom))
  64. return CGRect(x: placeholderInsets.left, y: placeholderInsets.top, width: maxWidth, height: expectedSize.height)
  65. }
  66. lazy var placeholderLabel: UILabel = {
  67. let label = UILabel()
  68. label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  69. label.lineBreakMode = .byWordWrapping
  70. label.numberOfLines = 0
  71. label.font = self.font
  72. label.textAlignment = self.textAlignment
  73. label.backgroundColor = UIColor.clear
  74. label.textColor = UIColor(white: 0.7, alpha: 1.0)
  75. label.alpha = 0
  76. self.addSubview(label)
  77. return label
  78. }()
  79. /** @abstract To set textView's placeholder text color. */
  80. @IBInspectable open var placeholderTextColor: UIColor? {
  81. get {
  82. return placeholderLabel.textColor
  83. }
  84. set {
  85. placeholderLabel.textColor = newValue
  86. }
  87. }
  88. /** @abstract To set textView's placeholder text. Default is nil. */
  89. @IBInspectable open var placeholder: String? {
  90. get {
  91. return placeholderLabel.text
  92. }
  93. set {
  94. placeholderLabel.text = newValue
  95. refreshPlaceholder()
  96. }
  97. }
  98. /** @abstract To set textView's placeholder attributed text. Default is nil. */
  99. open var attributedPlaceholder: NSAttributedString? {
  100. get {
  101. return placeholderLabel.attributedText
  102. }
  103. set {
  104. placeholderLabel.attributedText = newValue
  105. refreshPlaceholder()
  106. }
  107. }
  108. @objc override open func layoutSubviews() {
  109. super.layoutSubviews()
  110. placeholderLabel.frame = placeholderExpectedFrame
  111. }
  112. @objc internal func refreshPlaceholder() {
  113. if !text.isEmpty || !attributedText.string.isEmpty {
  114. placeholderLabel.alpha = 0
  115. } else {
  116. placeholderLabel.alpha = 1
  117. }
  118. }
  119. @objc override open var text: String! {
  120. didSet {
  121. refreshPlaceholder()
  122. }
  123. }
  124. open override var attributedText: NSAttributedString! {
  125. didSet {
  126. refreshPlaceholder()
  127. }
  128. }
  129. @objc override open var font: UIFont? {
  130. didSet {
  131. if let unwrappedFont = font {
  132. placeholderLabel.font = unwrappedFont
  133. } else {
  134. placeholderLabel.font = UIFont.systemFont(ofSize: 12)
  135. }
  136. }
  137. }
  138. @objc override open var textAlignment: NSTextAlignment {
  139. didSet {
  140. placeholderLabel.textAlignment = textAlignment
  141. }
  142. }
  143. @objc override open var delegate: UITextViewDelegate? {
  144. get {
  145. refreshPlaceholder()
  146. return super.delegate
  147. }
  148. set {
  149. super.delegate = newValue
  150. }
  151. }
  152. @objc override open var intrinsicContentSize: CGSize {
  153. guard !hasText else {
  154. return super.intrinsicContentSize
  155. }
  156. var newSize = super.intrinsicContentSize
  157. let placeholderInsets = self.placeholderInsets
  158. newSize.height = placeholderExpectedFrame.height + placeholderInsets.top + placeholderInsets.bottom
  159. return newSize
  160. }
  161. }