PlaceholderTextView.m 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. //
  2. // PlaceholderTextView.m
  3. // StocksBar
  4. //
  5. // Created by 翟玉磊 on 2017/3/2.
  6. // Copyright © 2017年 翟玉磊. All rights reserved.
  7. //
  8. #import "PlaceholderTextView.h"
  9. #define HAS_TEXT_CONTAINER [self respondsToSelector:@selector(textContainer)]
  10. #define HAS_TEXT_CONTAINER_INSETS(x) [(x) respondsToSelector:@selector(textContainerInset)]
  11. @interface PlaceholderTextView ()
  12. @property (strong, nonatomic) UITextView *_placeholderTextView;
  13. @end
  14. static NSString * const kAttributedPlaceholderKey = @"attributedPlaceholder";
  15. static NSString * const kPlaceholderKey = @"placeholder";
  16. static NSString * const kFontKey = @"font";
  17. static NSString * const kAttributedTextKey = @"attributedText";
  18. static NSString * const kTextKey = @"text";
  19. static NSString * const kExclusionPathsKey = @"exclusionPaths";
  20. static NSString * const kLineFragmentPaddingKey = @"lineFragmentPadding";
  21. static NSString * const kTextContainerInsetKey = @"textContainerInset";
  22. static NSString * const kTextAlignmentKey = @"textAlignment";
  23. @implementation PlaceholderTextView
  24. - (instancetype)initWithCoder:(NSCoder *)coder
  25. {
  26. self = [super initWithCoder:coder];
  27. if (self) {
  28. [self preparePlaceholder];
  29. }
  30. return self;
  31. }
  32. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 70000
  33. - (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer
  34. {
  35. self = [super initWithFrame:frame textContainer:textContainer];
  36. if (self) {
  37. [self preparePlaceholder];
  38. }
  39. return self;
  40. }
  41. #else
  42. - (id)initWithFrame:(CGRect)frame
  43. {
  44. self = [super initWithFrame:frame];
  45. if (self) {
  46. [self preparePlaceholder];
  47. }
  48. return self;
  49. }
  50. #endif
  51. - (void)preparePlaceholder
  52. {
  53. NSAssert(!self._placeholderTextView, @"placeholder has been prepared already: %@", self._placeholderTextView);
  54. // the label which displays the placeholder
  55. // needs to inherit some properties from its parent text view
  56. // account for standard UITextViewPadding
  57. CGRect frame = self.bounds;
  58. self._placeholderTextView = [[UITextView alloc] initWithFrame:frame];
  59. self._placeholderTextView.opaque = NO;
  60. self._placeholderTextView.backgroundColor = [UIColor clearColor];
  61. self._placeholderTextView.textColor = [UIColor colorWithWhite:0.7f alpha:0.7f];
  62. self._placeholderTextView.textAlignment = self.textAlignment;
  63. self._placeholderTextView.editable = NO;
  64. self._placeholderTextView.scrollEnabled = NO;
  65. self._placeholderTextView.userInteractionEnabled = NO;
  66. self._placeholderTextView.font = self.font;
  67. self._placeholderTextView.isAccessibilityElement = NO;
  68. self._placeholderTextView.contentOffset = self.contentOffset;
  69. self._placeholderTextView.contentInset = self.contentInset;
  70. if ([self._placeholderTextView respondsToSelector:@selector(setSelectable:)]) {
  71. self._placeholderTextView.selectable = NO;
  72. }
  73. if (HAS_TEXT_CONTAINER) {
  74. self._placeholderTextView.textContainer.exclusionPaths = self.textContainer.exclusionPaths;
  75. self._placeholderTextView.textContainer.lineFragmentPadding = self.textContainer.lineFragmentPadding;
  76. }
  77. if (HAS_TEXT_CONTAINER_INSETS(self)) {
  78. self._placeholderTextView.textContainerInset = self.textContainerInset;
  79. }
  80. if (_attributedPlaceholder) {
  81. self._placeholderTextView.attributedText = _attributedPlaceholder;
  82. } else if (_placeholder) {
  83. self._placeholderTextView.text = _placeholder;
  84. }
  85. [self setPlaceholderVisibleForText:self.text];
  86. self.clipsToBounds = YES;
  87. // some observations
  88. NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  89. [defaultCenter addObserver:self selector:@selector(textDidChange:)
  90. name:UITextViewTextDidChangeNotification object:self];
  91. [self addObserver:self forKeyPath:kAttributedPlaceholderKey
  92. options:NSKeyValueObservingOptionNew context:nil];
  93. [self addObserver:self forKeyPath:kPlaceholderKey
  94. options:NSKeyValueObservingOptionNew context:nil];
  95. [self addObserver:self forKeyPath:kFontKey
  96. options:NSKeyValueObservingOptionNew context:nil];
  97. [self addObserver:self forKeyPath:kAttributedTextKey
  98. options:NSKeyValueObservingOptionNew context:nil];
  99. [self addObserver:self forKeyPath:kTextKey
  100. options:NSKeyValueObservingOptionNew context:nil];
  101. [self addObserver:self forKeyPath:kTextAlignmentKey
  102. options:NSKeyValueObservingOptionNew context:nil];
  103. if (HAS_TEXT_CONTAINER) {
  104. [self.textContainer addObserver:self forKeyPath:kExclusionPathsKey
  105. options:NSKeyValueObservingOptionNew context:nil];
  106. [self.textContainer addObserver:self forKeyPath:kLineFragmentPaddingKey
  107. options:NSKeyValueObservingOptionNew context:nil];
  108. }
  109. if (HAS_TEXT_CONTAINER_INSETS(self)) {
  110. [self addObserver:self forKeyPath:kTextContainerInsetKey
  111. options:NSKeyValueObservingOptionNew context:nil];
  112. }
  113. }
  114. - (void)setPlaceholder:(NSString *)placeholderText
  115. {
  116. _placeholder = [placeholderText copy];
  117. _attributedPlaceholder = [[NSAttributedString alloc] initWithString:placeholderText];
  118. [self resizePlaceholderFrame];
  119. }
  120. - (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholderText
  121. {
  122. _placeholder = attributedPlaceholderText.string;
  123. _attributedPlaceholder = [attributedPlaceholderText copy];
  124. [self resizePlaceholderFrame];
  125. }
  126. - (void)layoutSubviews
  127. {
  128. [super layoutSubviews];
  129. [self resizePlaceholderFrame];
  130. }
  131. - (void)resizePlaceholderFrame
  132. {
  133. CGRect frame = self._placeholderTextView.frame;
  134. frame.size = self.bounds.size;
  135. self._placeholderTextView.frame = frame;
  136. }
  137. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
  138. change:(NSDictionary *)change context:(void *)context
  139. {
  140. if ([keyPath isEqualToString:kAttributedPlaceholderKey]) {
  141. self._placeholderTextView.attributedText = [change valueForKey:NSKeyValueChangeNewKey];
  142. }
  143. else if ([keyPath isEqualToString:kPlaceholderKey]) {
  144. self._placeholderTextView.text = [change valueForKey:NSKeyValueChangeNewKey];
  145. }
  146. else if ([keyPath isEqualToString:kFontKey]) {
  147. self._placeholderTextView.font = [change valueForKey:NSKeyValueChangeNewKey];
  148. }
  149. else if ([keyPath isEqualToString:kAttributedTextKey]) {
  150. NSAttributedString *newAttributedText = [change valueForKey:NSKeyValueChangeNewKey];
  151. [self setPlaceholderVisibleForText:newAttributedText.string];
  152. }
  153. else if ([keyPath isEqualToString:kTextKey]) {
  154. NSString *newText = [change valueForKey:NSKeyValueChangeNewKey];
  155. [self setPlaceholderVisibleForText:newText];
  156. }
  157. else if ([keyPath isEqualToString:kExclusionPathsKey]) {
  158. self._placeholderTextView.textContainer.exclusionPaths = [change objectForKey:NSKeyValueChangeNewKey];
  159. [self resizePlaceholderFrame];
  160. }
  161. else if ([keyPath isEqualToString:kLineFragmentPaddingKey]) {
  162. self._placeholderTextView.textContainer.lineFragmentPadding = [[change objectForKey:NSKeyValueChangeNewKey] floatValue];
  163. [self resizePlaceholderFrame];
  164. }
  165. else if ([keyPath isEqualToString:kTextContainerInsetKey]) {
  166. NSValue *value = [change objectForKey:NSKeyValueChangeNewKey];
  167. self._placeholderTextView.textContainerInset = value.UIEdgeInsetsValue;
  168. }
  169. else if ([keyPath isEqualToString:kTextAlignmentKey]) {
  170. NSNumber *alignment = [change objectForKey:NSKeyValueChangeNewKey];
  171. self._placeholderTextView.textAlignment = alignment.intValue;
  172. }
  173. else {
  174. [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
  175. }
  176. }
  177. - (void)setPlaceholderTextColor:(UIColor *)placeholderTextColor
  178. {
  179. self._placeholderTextView.textColor = placeholderTextColor;
  180. }
  181. - (UIColor *)placeholderTextColor
  182. {
  183. return self._placeholderTextView.textColor;
  184. }
  185. - (void)textDidChange:(NSNotification *)aNotification
  186. {
  187. [self setPlaceholderVisibleForText:self.text];
  188. }
  189. - (BOOL)becomeFirstResponder
  190. {
  191. [self setPlaceholderVisibleForText:self.text];
  192. return [super becomeFirstResponder];
  193. }
  194. - (void)setPlaceholderVisibleForText:(NSString *)text
  195. {
  196. if (text.length < 1) {
  197. if (self.fadeTime > 0.0) {
  198. if (![self._placeholderTextView isDescendantOfView:self]) {
  199. self._placeholderTextView.alpha = 0;
  200. [self addSubview:self._placeholderTextView];
  201. [self sendSubviewToBack:self._placeholderTextView];
  202. }
  203. [UIView animateWithDuration:_fadeTime animations:^{
  204. self._placeholderTextView.alpha = 1;
  205. }];
  206. }
  207. else {
  208. [self addSubview:self._placeholderTextView];
  209. [self sendSubviewToBack:self._placeholderTextView];
  210. self._placeholderTextView.alpha = 1;
  211. }
  212. }
  213. else {
  214. if (self.fadeTime > 0.0) {
  215. [UIView animateWithDuration:_fadeTime animations:^{
  216. self._placeholderTextView.alpha = 0;
  217. }];
  218. }
  219. else {
  220. [self._placeholderTextView removeFromSuperview];
  221. }
  222. }
  223. }
  224. - (void)dealloc
  225. {
  226. [[NSNotificationCenter defaultCenter] removeObserver:self];
  227. [self removeObserver:self forKeyPath:kAttributedPlaceholderKey];
  228. [self removeObserver:self forKeyPath:kPlaceholderKey];
  229. [self removeObserver:self forKeyPath:kFontKey];
  230. [self removeObserver:self forKeyPath:kAttributedTextKey];
  231. [self removeObserver:self forKeyPath:kTextKey];
  232. [self removeObserver:self forKeyPath:kTextAlignmentKey];
  233. if (HAS_TEXT_CONTAINER) {
  234. [self.textContainer removeObserver:self forKeyPath:kExclusionPathsKey];
  235. [self.textContainer removeObserver:self forKeyPath:kLineFragmentPaddingKey];
  236. }
  237. if (HAS_TEXT_CONTAINER_INSETS(self)) {
  238. [self removeObserver:self forKeyPath:kTextContainerInsetKey];
  239. }
  240. }
  241. @end