IQTextView.swift 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. //
  2. // IQTextView.swift
  3. // https://github.com/hackiftekhar/IQKeyboardManager
  4. // Copyright (c) 2013-20 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. @available(iOSApplicationExtension, unavailable)
  26. @objc open class IQTextView: UITextView {
  27. @objc required public init?(coder aDecoder: NSCoder) {
  28. super.init(coder: aDecoder)
  29. NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextView.textDidChangeNotification, object: self)
  30. }
  31. @objc override public init(frame: CGRect, textContainer: NSTextContainer?) {
  32. super.init(frame: frame, textContainer: textContainer)
  33. NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextView.textDidChangeNotification, object: self)
  34. }
  35. @objc override open func awakeFromNib() {
  36. super.awakeFromNib()
  37. NotificationCenter.default.addObserver(self, selector: #selector(self.refreshPlaceholder), name: UITextView.textDidChangeNotification, object: self)
  38. }
  39. deinit {
  40. IQ_PlaceholderLabel.removeFromSuperview()
  41. }
  42. private var placeholderInsets: UIEdgeInsets {
  43. return UIEdgeInsets(top: self.textContainerInset.top, left: self.textContainerInset.left + self.textContainer.lineFragmentPadding, bottom: self.textContainerInset.bottom, right: self.textContainerInset.right + self.textContainer.lineFragmentPadding)
  44. }
  45. private var placeholderExpectedFrame: CGRect {
  46. let placeholderInsets = self.placeholderInsets
  47. let maxWidth = self.frame.width-placeholderInsets.left-placeholderInsets.right
  48. let expectedSize = IQ_PlaceholderLabel.sizeThatFits(CGSize(width: maxWidth, height: self.frame.height-placeholderInsets.top-placeholderInsets.bottom))
  49. return CGRect(x: placeholderInsets.left, y: placeholderInsets.top, width: maxWidth, height: expectedSize.height)
  50. }
  51. lazy var IQ_PlaceholderLabel: UILabel = {
  52. let label = UILabel()
  53. label.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  54. label.lineBreakMode = .byWordWrapping
  55. label.numberOfLines = 0
  56. label.font = self.font
  57. label.textAlignment = self.textAlignment
  58. label.backgroundColor = UIColor.clear
  59. label.isAccessibilityElement = false
  60. #if swift(>=5.1)
  61. label.textColor = UIColor.systemGray
  62. #else
  63. label.textColor = UIColor.lightText
  64. #endif
  65. label.alpha = 0
  66. self.addSubview(label)
  67. return label
  68. }()
  69. /** @abstract To set textView's placeholder text color. */
  70. @IBInspectable open var placeholderTextColor: UIColor? {
  71. get {
  72. return IQ_PlaceholderLabel.textColor
  73. }
  74. set {
  75. IQ_PlaceholderLabel.textColor = newValue
  76. }
  77. }
  78. /** @abstract To set textView's placeholder text. Default is nil. */
  79. @IBInspectable open var placeholder: String? {
  80. get {
  81. return IQ_PlaceholderLabel.text
  82. }
  83. set {
  84. IQ_PlaceholderLabel.text = newValue
  85. refreshPlaceholder()
  86. }
  87. }
  88. /** @abstract To set textView's placeholder attributed text. Default is nil. */
  89. open var attributedPlaceholder: NSAttributedString? {
  90. get {
  91. return IQ_PlaceholderLabel.attributedText
  92. }
  93. set {
  94. IQ_PlaceholderLabel.attributedText = newValue
  95. refreshPlaceholder()
  96. }
  97. }
  98. @objc override open func layoutSubviews() {
  99. super.layoutSubviews()
  100. IQ_PlaceholderLabel.frame = placeholderExpectedFrame
  101. }
  102. @objc internal func refreshPlaceholder() {
  103. if !text.isEmpty || !attributedText.string.isEmpty {
  104. IQ_PlaceholderLabel.alpha = 0
  105. } else {
  106. IQ_PlaceholderLabel.alpha = 1
  107. }
  108. }
  109. @objc override open var text: String! {
  110. didSet {
  111. refreshPlaceholder()
  112. }
  113. }
  114. open override var attributedText: NSAttributedString! {
  115. didSet {
  116. refreshPlaceholder()
  117. }
  118. }
  119. @objc override open var font: UIFont? {
  120. didSet {
  121. if let unwrappedFont = font {
  122. IQ_PlaceholderLabel.font = unwrappedFont
  123. } else {
  124. IQ_PlaceholderLabel.font = UIFont.systemFont(ofSize: 12)
  125. }
  126. }
  127. }
  128. @objc override open var textAlignment: NSTextAlignment {
  129. didSet {
  130. IQ_PlaceholderLabel.textAlignment = textAlignment
  131. }
  132. }
  133. @objc override weak open var delegate: UITextViewDelegate? {
  134. get {
  135. refreshPlaceholder()
  136. return super.delegate
  137. }
  138. set {
  139. super.delegate = newValue
  140. }
  141. }
  142. @objc override open var intrinsicContentSize: CGSize {
  143. guard !hasText else {
  144. return super.intrinsicContentSize
  145. }
  146. var newSize = super.intrinsicContentSize
  147. let placeholderInsets = self.placeholderInsets
  148. newSize.height = placeholderExpectedFrame.height + placeholderInsets.top + placeholderInsets.bottom
  149. return newSize
  150. }
  151. }