IQTextView.m 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. //
  2. // IQTextView.m
  3. // https://github.com/hackiftekhar/IQKeyboardManager
  4. // Copyright (c) 2013-24 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/UIKit.h>
  24. #import "IQTextView.h"
  25. NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
  26. @interface IQTextView ()
  27. @property(nullable, nonatomic, strong) UILabel *placeholderLabel;
  28. @end
  29. NS_EXTENSION_UNAVAILABLE_IOS("Unavailable in extension")
  30. @implementation IQTextView
  31. @synthesize placeholder = _placeholder;
  32. @synthesize placeholderLabel = _placeholderLabel;
  33. @synthesize placeholderTextColor = _placeholderTextColor;
  34. -(void)initialize
  35. {
  36. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshPlaceholder) name:UITextViewTextDidChangeNotification object:self];
  37. }
  38. -(void)dealloc
  39. {
  40. [_placeholderLabel removeFromSuperview];
  41. _placeholderLabel = nil;
  42. [[NSNotificationCenter defaultCenter] removeObserver:self];
  43. }
  44. - (instancetype)init
  45. {
  46. self = [super init];
  47. if (self) {
  48. [self initialize];
  49. }
  50. return self;
  51. }
  52. -(void)awakeFromNib
  53. {
  54. [super awakeFromNib];
  55. [self initialize];
  56. }
  57. -(void)refreshPlaceholder
  58. {
  59. if([[self text] length] || [[self attributedText] length])
  60. {
  61. if (self.placeholderLabel.alpha != 0)
  62. {
  63. [self.placeholderLabel setAlpha:0];
  64. [self setNeedsLayout];
  65. [self layoutIfNeeded];
  66. }
  67. }
  68. else if(self.placeholderLabel.alpha != 1)
  69. {
  70. [self.placeholderLabel setAlpha:1];
  71. [self setNeedsLayout];
  72. [self layoutIfNeeded];
  73. }
  74. }
  75. - (void)setText:(NSString *)text
  76. {
  77. [super setText:text];
  78. [self refreshPlaceholder];
  79. }
  80. -(void)setAttributedText:(NSAttributedString *)attributedText
  81. {
  82. [super setAttributedText:attributedText];
  83. [self refreshPlaceholder];
  84. }
  85. -(void)setFont:(UIFont *)font
  86. {
  87. [super setFont:font];
  88. self.placeholderLabel.font = self.font;
  89. [self setNeedsLayout];
  90. [self layoutIfNeeded];
  91. }
  92. -(void)setTextAlignment:(NSTextAlignment)textAlignment
  93. {
  94. [super setTextAlignment:textAlignment];
  95. self.placeholderLabel.textAlignment = textAlignment;
  96. [self setNeedsLayout];
  97. [self layoutIfNeeded];
  98. }
  99. -(void)layoutSubviews
  100. {
  101. [super layoutSubviews];
  102. self.placeholderLabel.frame = [self placeholderExpectedFrame];
  103. }
  104. -(void)setPlaceholder:(NSString *)placeholder
  105. {
  106. _placeholder = placeholder;
  107. self.placeholderLabel.text = placeholder;
  108. [self refreshPlaceholder];
  109. }
  110. -(void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder
  111. {
  112. _attributedPlaceholder = attributedPlaceholder;
  113. self.placeholderLabel.attributedText = attributedPlaceholder;
  114. [self refreshPlaceholder];
  115. }
  116. -(void)setPlaceholderTextColor:(UIColor*)placeholderTextColor
  117. {
  118. _placeholderTextColor = placeholderTextColor;
  119. self.placeholderLabel.textColor = placeholderTextColor;
  120. }
  121. -(UIEdgeInsets)placeholderInsets
  122. {
  123. return UIEdgeInsetsMake(self.textContainerInset.top, self.textContainerInset.left + self.textContainer.lineFragmentPadding, self.textContainerInset.bottom, self.textContainerInset.right + self.textContainer.lineFragmentPadding);
  124. }
  125. -(CGRect)placeholderExpectedFrame
  126. {
  127. UIEdgeInsets placeholderInsets = [self placeholderInsets];
  128. CGFloat maxWidth = CGRectGetWidth(self.frame)-placeholderInsets.left-placeholderInsets.right;
  129. CGSize expectedSize = [self.placeholderLabel sizeThatFits:CGSizeMake(maxWidth, CGRectGetHeight(self.frame)-placeholderInsets.top-placeholderInsets.bottom)];
  130. return CGRectMake(placeholderInsets.left, placeholderInsets.top, maxWidth, expectedSize.height);
  131. }
  132. -(UILabel*)placeholderLabel
  133. {
  134. if (_placeholderLabel == nil)
  135. {
  136. _placeholderLabel = [[UILabel alloc] init];
  137. _placeholderLabel.autoresizingMask = (UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight);
  138. _placeholderLabel.lineBreakMode = NSLineBreakByWordWrapping;
  139. _placeholderLabel.numberOfLines = 0;
  140. _placeholderLabel.font = self.font;
  141. _placeholderLabel.textAlignment = self.textAlignment;
  142. _placeholderLabel.backgroundColor = [UIColor clearColor];
  143. _placeholderLabel.isAccessibilityElement = NO;
  144. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  145. if (@available(iOS 13.0, *))
  146. {
  147. _placeholderLabel.textColor = [UIColor placeholderTextColor];
  148. }
  149. else
  150. #endif
  151. {
  152. _placeholderLabel.textColor = [UIColor lightTextColor];
  153. }
  154. _placeholderLabel.alpha = 0;
  155. [self addSubview:_placeholderLabel];
  156. }
  157. return _placeholderLabel;
  158. }
  159. //When any text changes on textField, the delegate getter is called. At this time we refresh the textView's placeholder
  160. -(id<UITextViewDelegate>)delegate
  161. {
  162. [self refreshPlaceholder];
  163. return [super delegate];
  164. }
  165. -(CGSize)intrinsicContentSize
  166. {
  167. if (self.hasText)
  168. {
  169. return [super intrinsicContentSize];
  170. }
  171. UIEdgeInsets placeholderInsets = [self placeholderInsets];
  172. CGSize newSize = [super intrinsicContentSize];
  173. newSize.height = [self placeholderExpectedFrame].size.height + placeholderInsets.top + placeholderInsets.bottom;
  174. return newSize;
  175. }
  176. - (CGRect)caretRectForPosition:(UITextPosition *)position {
  177. CGRect originalRect = [super caretRectForPosition:position];
  178. // When placeholder is visible and text alignment is centered
  179. if (_placeholderLabel.alpha == 1 && self.textAlignment == NSTextAlignmentCenter) {
  180. // Calculate the width of the placeholder text
  181. CGSize textSize = [_placeholderLabel.text sizeWithAttributes:@{NSFontAttributeName:_placeholderLabel.font}];
  182. // Calculate the starting x position of the centered placeholder text
  183. CGFloat centeredTextX = (self.bounds.size.width - textSize.width) / 2;
  184. // Update the caret position to match the starting x position of the centered text
  185. originalRect.origin.x = centeredTextX;
  186. }
  187. return originalRect;
  188. }
  189. @end