JXTextView.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. //
  2. // JXTextView.m
  3. // MessageDisplayExample
  4. //
  5. // Created by qtone-1 on 14-4-24.
  6. // Copyright (c) 2014年 曾宪华 开发团队(http://iyilunba.com ) 本人QQ:543413507 本人QQ群(142557668). All rights reserved.
  7. //
  8. #import "JXTextView.h"
  9. @interface JXTextView ()<UITextViewDelegate>
  10. @property (nonatomic, copy) NSString *placeHolderStr;
  11. @end
  12. @implementation JXTextView
  13. @synthesize placeHolder;
  14. @synthesize previousTextViewContentHeight,target,didTouch;
  15. @synthesize isEditing;
  16. #pragma mark - Setters
  17. - (id)initWithFrame:(CGRect)frame {
  18. self = [super initWithFrame:frame];
  19. if (self) {
  20. // Initialization code
  21. [self setup];
  22. if(!self.disableAutoSize){
  23. [self addObserver:self forKeyPath:@"contentSize"
  24. options:NSKeyValueObservingOptionNew
  25. context:nil];
  26. [g_notify addObserver:self selector:@selector(changeKeyBoard:) name:UIKeyboardWillShowNotification object:nil];
  27. }
  28. }
  29. return self;
  30. }
  31. - (id)init
  32. {
  33. self = [super init];
  34. if (self) {
  35. }
  36. return self;
  37. }
  38. -(void)dealloc{
  39. // NSLog(@"JXTextView.dealloc");
  40. if(!self.disableAutoSize){
  41. [self removeObserver:self forKeyPath:@"contentSize"];
  42. [g_notify removeObserver:self name:UIKeyboardWillShowNotification object:nil];
  43. }
  44. self.placeHolder = nil;
  45. _placeHolderTextColor = nil;
  46. // [g_notify removeObserver:self name:UITextViewTextDidChangeNotification object:self];
  47. // [super dealloc];
  48. }
  49. - (void)setPlaceHolder:(NSString *)value {
  50. if([placeHolder isEqualToString:value]) {
  51. return;
  52. }
  53. placeHolder = [value copy];
  54. if (placeHolder) {
  55. _placeHolderStr = placeHolder;
  56. }
  57. NSUInteger maxChars = [JXTextView maxCharactersPerLine];
  58. if([placeHolder length] > maxChars) {
  59. placeHolder = [placeHolder substringToIndex:maxChars - 8];
  60. placeHolder = [[placeHolder stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] stringByAppendingFormat:@"..."];
  61. }
  62. [self setNeedsDisplay];
  63. }
  64. - (void)setPlaceHolderTextColor:(UIColor *)placeHolderTextColor {
  65. if([placeHolderTextColor isEqual:_placeHolderTextColor]) {
  66. return;
  67. }
  68. _placeHolderTextColor = placeHolderTextColor;
  69. [self setNeedsDisplay];
  70. }
  71. #pragma mark - Message text view
  72. - (NSUInteger)numberOfLinesOfText {
  73. return [JXTextView numberOfLinesForMessage:self.text];
  74. }
  75. + (NSUInteger)maxCharactersPerLine {
  76. return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) ? 33 : 109;
  77. }
  78. + (NSUInteger)numberOfLinesForMessage:(NSString *)text {
  79. return (text.length / [JXTextView maxCharactersPerLine]) + 1;
  80. }
  81. #pragma mark - Text view overrides
  82. - (void)setText:(NSString *)text {
  83. [super setText:text];
  84. [self setNeedsDisplay];
  85. }
  86. - (void)setAttributedText:(NSAttributedString *)attributedText {
  87. [super setAttributedText:attributedText];
  88. [self setNeedsDisplay];
  89. }
  90. - (void)setContentInset:(UIEdgeInsets)contentInset {
  91. [super setContentInset:contentInset];
  92. [self setNeedsDisplay];
  93. }
  94. - (void)setFont:(UIFont *)font {
  95. [super setFont:font];
  96. [self setNeedsDisplay];
  97. }
  98. - (void)setTextAlignment:(NSTextAlignment)textAlignment {
  99. [super setTextAlignment:textAlignment];
  100. [self setNeedsDisplay];
  101. }
  102. #pragma mark - Notifications
  103. - (void)didReceiveTextDidChangeNotification:(NSNotification *)notification {
  104. // [self setNeedsDisplay];
  105. }
  106. #pragma mark - Life cycle
  107. - (void)setup {
  108. // [g_notify addObserver:self
  109. // selector:@selector(didReceiveTextDidChangeNotification:)
  110. // name:UITextViewTextDidChangeNotification
  111. // object:self];
  112. _placeHolderTextColor = [UIColor lightGrayColor];
  113. self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
  114. self.scrollIndicatorInsets = UIEdgeInsetsMake(10.0f, 0.0f, 10.0f, 8.0f);
  115. self.contentInset = UIEdgeInsetsZero;
  116. self.scrollEnabled = YES;
  117. self.scrollsToTop = NO;
  118. self.userInteractionEnabled = YES;
  119. self.font = [UIFont systemFontOfSize:16.0f];
  120. self.textColor = [UIColor blackColor];
  121. self.backgroundColor = [UIColor whiteColor];
  122. self.keyboardAppearance = UIKeyboardAppearanceDefault;
  123. self.keyboardType = UIKeyboardTypeDefault;
  124. self.textAlignment = NSTextAlignmentLeft;
  125. self.delegate = self;
  126. //重要,扁平
  127. self.backgroundColor = [UIColor clearColor];
  128. // self.layer.borderColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor;
  129. self.layer.borderColor = [UIColor colorWithWhite:0.8f alpha:1.0f].CGColor;
  130. self.layer.borderWidth = 0.65f;
  131. self.layer.cornerRadius = 6.0f;
  132. self.returnKeyType = UIReturnKeySend;
  133. isEditing = NO;
  134. }
  135. #pragma mark - Drawing
  136. - (void)drawRect:(CGRect)rect
  137. {
  138. [super drawRect:rect];
  139. if([self.text length] == 0 && self.placeHolder) {
  140. CGRect placeHolderRect = CGRectMake(10.0f,
  141. (self.frame.size.height-self.font.pointSize)/2,
  142. rect.size.width,
  143. rect.size.height);
  144. [self.placeHolderTextColor set];
  145. if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_0) {
  146. NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
  147. paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
  148. paragraphStyle.alignment = self.textAlignment;
  149. [self.placeHolder drawInRect:placeHolderRect
  150. withAttributes:@{ NSFontAttributeName : self.font,
  151. NSForegroundColorAttributeName : self.placeHolderTextColor,
  152. NSParagraphStyleAttributeName : paragraphStyle }];
  153. }
  154. else {
  155. // [self.placeHolder drawInRect:placeHolderRect
  156. // withFont:self.font
  157. // lineBreakMode:NSLineBreakByTruncatingTail
  158. // alignment:self.textAlignment];
  159. [self.placeHolder drawInRect:placeHolderRect withAttributes:@{NSFontAttributeName:self.font}];
  160. }
  161. }
  162. }
  163. - (void)observeValueForKeyPath:(NSString *)keyPath
  164. ofObject:(id)object
  165. change:(NSDictionary *)change
  166. context:(void *)context {
  167. // if (object == self && [keyPath isEqualToString:@"contentSize"]) {
  168. if ([keyPath isEqualToString:@"contentSize"]) {
  169. [self layoutAndAnimateMessageInputTextView:object];
  170. }
  171. }
  172. - (CGFloat)getTextViewContentH{
  173. // return textView.contentSize.height;
  174. if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
  175. return ceilf([self sizeThatFits:self.frame.size].height);
  176. } else {
  177. return self.contentSize.height;
  178. }
  179. }
  180. - (void)layoutAndAnimateMessageInputTextView:(UITextView *)textView {
  181. CGFloat maxHeight = 80;
  182. CGFloat contentH = [self getTextViewContentH];
  183. BOOL isShrinking = contentH < self.previousTextViewContentHeight;
  184. CGFloat changeInHeight = contentH - self.previousTextViewContentHeight;
  185. if (!isShrinking && (self.previousTextViewContentHeight == maxHeight || textView.text.length == 0)) {
  186. changeInHeight = 0;
  187. }
  188. else {
  189. changeInHeight = MIN(changeInHeight, maxHeight - self.previousTextViewContentHeight);
  190. }
  191. if (changeInHeight != 0.0f) {
  192. [UIView animateWithDuration:0.25f
  193. animations:^{
  194. if (isShrinking) {
  195. if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0) {
  196. self.previousTextViewContentHeight = MIN(contentH, maxHeight);
  197. }
  198. // if shrinking the view, animate text view frame BEFORE input view frame
  199. }
  200. self.frame = CGRectMake(self.frame.origin.x,
  201. self.frame.origin.y,
  202. self.frame.size.width,
  203. self.frame.size.height + changeInHeight);
  204. self.superview.frame = CGRectMake(self.superview.frame.origin.x,
  205. self.superview.frame.origin.y - changeInHeight,
  206. self.superview.frame.size.width,
  207. self.superview.frame.size.height + changeInHeight);
  208. if (!isShrinking) {
  209. if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0) {
  210. self.previousTextViewContentHeight = MIN(contentH, maxHeight);
  211. }
  212. // growing the view, animate the text view frame AFTER input view frame
  213. }
  214. }
  215. completion:^(BOOL finished) {
  216. }];
  217. self.previousTextViewContentHeight = MIN(contentH, maxHeight);
  218. }
  219. // Once we reached the max height, we have to consider the bottom offset for the text view.
  220. // To make visible the last line, again we have to set the content offset.
  221. if (self.previousTextViewContentHeight == maxHeight) {
  222. double delayInSeconds = 0.01;
  223. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
  224. dispatch_after(popTime,
  225. dispatch_get_main_queue(),
  226. ^(void) {
  227. CGPoint bottomOffset = CGPointMake(0.0f, contentH - textView.bounds.size.height);
  228. [textView setContentOffset:bottomOffset animated:YES];
  229. });
  230. }
  231. }
  232. -(void)changeKeyBoard:(NSNotification *)aNotifacation
  233. {
  234. return;
  235. // //获取到键盘frame 变化之前的frame
  236. // NSValue *keyboardBeginBounds=[[aNotifacation userInfo]objectForKey:UIKeyboardFrameBeginUserInfoKey];
  237. // CGRect beginRect=[keyboardBeginBounds CGRectValue];
  238. //
  239. // //获取到键盘frame变化之后的frame
  240. // NSValue *keyboardEndBounds=[[aNotifacation userInfo]objectForKey:UIKeyboardFrameEndUserInfoKey];
  241. //
  242. // CGRect endRect=[keyboardEndBounds CGRectValue];
  243. //
  244. // CGFloat deltaY=endRect.origin.y-beginRect.origin.y;
  245. // //拿frame变化之后的origin.y-变化之前的origin.y,其差值(带正负号)就是我们self.view的y方向上的增量
  246. // deltaY=-endRect.size.height;
  247. //
  248. //// NSLog(@"deltaY:%f",deltaY);
  249. //
  250. // [self.superview setFrame:CGRectMake(0, JX_SCREEN_HEIGHT+deltaY-self.superview.frame.size.height, self.superview.frame.size.width, self.superview.frame.size.height)];
  251. }
  252. - (BOOL)textViewShouldBeginEditing:(UITextView *)textView{
  253. isEditing = YES;
  254. return YES;
  255. }
  256. - (void)textViewDidBeginEditing:(UITextView *)textView{
  257. self.previousTextViewContentHeight = [self getTextViewContentH];
  258. }
  259. - (void)textViewDidEndEditing:(UITextView *)textView{
  260. isEditing = NO;
  261. }
  262. - (void)textViewDidChange:(UITextView *)textView{
  263. NSString* s = textView.text;
  264. unichar c = [s characterAtIndex:s.length-1];
  265. if (c == '\n'){
  266. [self sendToTarget];
  267. // textView.hidden = YES;
  268. }
  269. if (s.length <= 0) {
  270. self.placeHolder = _placeHolderStr;
  271. }else {
  272. self.placeHolder = nil;
  273. }
  274. }
  275. -(void)sendToTarget{
  276. if(self.target != nil && [self.target respondsToSelector:self.didTouch]){
  277. NSString* s = self.text;
  278. unichar c = [s characterAtIndex:s.length-1];
  279. if (c == '\n')
  280. s = [s substringToIndex:s.length-1];
  281. [self.target performSelectorOnMainThread:self.didTouch withObject:s waitUntilDone:YES];
  282. }
  283. }
  284. @end