一:首先查看一下關於UITextView的定義
NS_CLASS_AVAILABLE_IOS(2_0) @interface UITextView : UIScrollView <UITextInput> @property(nullable,nonatomic,weak) id<UITextViewDelegate> delegate; @property(null_resettable,nonatomic,copy) NSString *text; @property(nullable,nonatomic,strong) UIFont *font; @property(nullable,nonatomic,strong) UIColor *textColor; @property(nonatomic) NSTextAlignment textAlignment; @property(nonatomic) NSRange selectedRange; //選中范圍 @property(nonatomic,getter=isEditable) BOOL editable; // 是否可以編輯 @property(nonatomic,getter=isSelectable) BOOL selectable NS_AVAILABLE_IOS(7_0); // 是否可以選中 @property(nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0); //屬性可以設定使電話號碼、網址、電子郵件和符合格式的日期等文字變為鏈接文字 @property(nonatomic) BOOL allowsEditingTextAttributes NS_AVAILABLE_IOS(6_0); // 默認值為NO 是否允許更改字符屬性字典 @property(null_resettable,copy) NSAttributedString *attributedText NS_AVAILABLE_IOS(6_0); //富文本 @property(nonatomic,copy) NSDictionary<NSString *, id> *typingAttributes NS_AVAILABLE_IOS(6_0); //滾動到文本的某個段落 - (void)scrollRangeToVisible:(NSRange)range; @property (nullable, readwrite, strong) UIView *inputView; @property (nullable, readwrite, strong) UIView *inputAccessoryView; @property(nonatomic) BOOL clearsOnInsertion NS_AVAILABLE_IOS(6_0); // 設置是否允許再次編輯時在內容中間插入內容 - (instancetype)initWithFrame:(CGRect)frame textContainer:(nullable NSTextContainer *)textContainer NS_AVAILABLE_IOS(7_0) NS_DESIGNATED_INITIALIZER; - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder NS_DESIGNATED_INITIALIZER; //則定義了一個矩形區域用於存放已經進行了排版並設置好屬性的文字 @property(nonatomic,readonly) NSTextContainer *textContainer NS_AVAILABLE_IOS(7_0); @property(nonatomic, assign) UIEdgeInsets textContainerInset NS_AVAILABLE_IOS(7_0); //用於管理NSTextStorage其中的文字內容的排版布局 @property(nonatomic,readonly) NSLayoutManager *layoutManager NS_AVAILABLE_IOS(7_0); //NSTextStorage保存並管理UITextView要展示的文字內容,該類是NSMutableAttributedString的子類,由於可以靈活地往文字添加或修改屬性 @property(nonatomic,readonly,strong) NSTextStorage *textStorage NS_AVAILABLE_IOS(7_0); //鏈接文本的樣式設置 @property(null_resettable, nonatomic, copy) NSDictionary<NSString *, id> *linkTextAttributes NS_AVAILABLE_IOS(7_0); @end
UITextView是繼承於UIScrollView,並且遵循UITextInput的協議;而UIScrollView是繼承於UIView,並且遵循NSCoding協議;上面有些屬性跟UITextField是一樣的,就沒有標注出來;關於UITextInput的協議也可以見關於UITextField裡面的內容;包含一些鍵盤響應及相應的通知處理知識點;
知識點1:關於UIDataDetectorTypes此屬性可以設定使電話號碼、網址、電子郵件和符合格式的日期等文字變為鏈接文字
typedef NS_OPTIONS(NSUInteger, UIDataDetectorTypes) { UIDataDetectorTypePhoneNumber = 1 << 0, // Phone number detection UIDataDetectorTypeLink = 1 << 1, // URL detection UIDataDetectorTypeAddress NS_ENUM_AVAILABLE_IOS(4_0) = 1 << 2, // Street address detection UIDataDetectorTypeCalendarEvent NS_ENUM_AVAILABLE_IOS(4_0) = 1 << 3, // Event detection UIDataDetectorTypeNone = 0, // No detection at all UIDataDetectorTypeAll = NSUIntegerMax // All types };
實例: textView.dataDetectorTypes = UIDataDetectorTypeAll; UIDataDetectorTypeAll可以檢測檢測電話、網址和郵箱。符合條件的文本中的內容就會高亮;
知識點2:設置鏈接樣式的運用實例
UITextView * textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 100, 300, 50)]; NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"這是一個鏈接:www.123456.com"]; [attributedString addAttribute:NSLinkAttributeName value:@"url1://www.baidu.com" range:NSMakeRange(7, 14)]; NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor greenColor], NSUnderlineColorAttributeName: [UIColor lightGrayColor], NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)}; textView.linkTextAttributes = linkAttributes; textView.attributedText = attributedString; textView.delegate = self; textView.editable = NO; // 可編輯狀態不能點擊鏈接 [self.view addSubview:textView];
// 要實現代理 - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange { if ([[URL scheme] isEqualToString:@"url1"]) { NSString * url = [URL host]; NSLog(@"%@",url); // 在這裡利用url做點什麼事情...... return NO; } return YES; }
知識點3:UITextView: 響應鍵盤的 return 事件
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{ if ([text isEqualToString:@"\n"]){ //判斷輸入的字是否是回車,即按下return //在這裡做你響應return鍵的代碼 [textView resignFirstResponder]; return NO; //這裡返回NO,就代表return鍵值失效,即頁面上按下return,不會出現換行,如果為yes,則輸入頁面會換行 } return YES; }
知識點4:如何優雅的讓UITextView根據輸入文字實時改變高度
// 初始化控件 self.contentTextView = [[UITextView alloc]initWithFrame:CGRectMake((kMainBoundsWidth-250)/2, kMainBoundsHeight/2-50, 250, 39)]; self.contentTextView .layer.cornerRadius = 4; self.contentTextView .layer.masksToBounds = YES; self.contentTextView .delegate = self; self.contentTextView .layer.borderWidth = 1; self.contentTextView .font = [UIFont systemFontOfSize:14]; self.contentTextView .layer.borderColor = [[[UIColor lightGrayColor] colorWithAlphaComponent:0.4] CGColor]; //加下面一句話的目的是,是為了調整光標的位置,讓光標出現在UITextView的正中間 self.contentTextView.textContainerInset = UIEdgeInsetsMake(10,0, 0, 0); [self.view addSubview:self.contentTextView ];
//計算輸入文字高度的方法,之所以返回的高度值加22是因為UITextView有一個初始的高度值40,但是輸入第一行文字的時候文字高度只有18,所以UITextView的高度會發生變化,效果不太好 - (float) heightForTextView: (UITextView *)textView WithText: (NSString *) strText{ CGSize constraint = CGSizeMake(textView.contentSize.width , CGFLOAT_MAX); CGRect size = [strText boundingRectWithSize:constraint options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading) attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:14]} context:nil]; float textHeight = size.size.height + 22.0; return textHeight; }
//每次輸入文字後調用該方法,此時輸入的文字並不在textView.text中,而在另一個參數text中,走完該方法後每次輸入的文字才加入到textView.text中。解決方案是將textView.text和text文字拼接起來 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { CGRect frame = textView.frame; float height; if ([text isEqual:@""]) { if (![textView.text isEqualToString:@""]) { height = [ self heightForTextView:textView WithText:[textView.text substringToIndex:[textView.text length] - 1]]; }else{ height = [ self heightForTextView:textView WithText:textView.text]; } }else{ height = [self heightForTextView:textView WithText:[NSString stringWithFormat:@"%@%@",textView.text,text]]; } frame.size.height = height; [UIView animateWithDuration:0.5 animations:^{ textView.frame = frame; } completion:nil]; return YES; }
知識點5:textView其實是沒有PlaceHolder,如何設置PlaceHolder如下:
#import <UIKit/UIKit.h> @interface UIPlaceHolderTextView : UITextView @property (nonatomic, strong) NSString *placeholder; @property (nonatomic, strong) UIColor *placeholderColor; -(void)textChanged:(NSNotification*)notification; @end
#import "UIPlaceHolderTextView.h" @interface UIPlaceHolderTextView() @property (nonatomic, strong) UILabel *placeHolderLabel; @end @implementation UIPlaceHolderTextView CGFloat const UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION = 0.25; - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; #if __has_feature(objc_arc) #else [_placeHolderLabel release]; _placeHolderLabel = nil; [_placeholderColor release]; _placeholderColor = nil; [_placeholder release]; _placeholder = nil; [super dealloc]; #endif } - (void)awakeFromNib { [super awakeFromNib]; if (!self.placeholder) { _placeholder = @""; } if (!self.placeholderColor) { [self setPlaceholderColor:[UIColor lightGrayColor]]; } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil]; } - (id)initWithFrame:(CGRect)frame { if( (self = [super initWithFrame:frame]) ) { _placeholder = @""; [self setPlaceholderColor:[UIColor lightGrayColor]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged:) name:UITextViewTextDidChangeNotification object:nil]; } return self; } - (void)textChanged:(NSNotification *)notification { if([[self placeholder] length] == 0) { return; } [UIView animateWithDuration:UI_PLACEHOLDER_TEXT_CHANGED_ANIMATION_DURATION animations:^{ if([[self text] length] == 0) { [[self viewWithTag:999] setAlpha:1]; } else { [[self viewWithTag:999] setAlpha:0]; } }]; } - (void)setText:(NSString *)text { [super setText:text]; [self textChanged:nil]; } - (void)drawRect:(CGRect)rect { if( [[self placeholder] length] > 0 ) { UIEdgeInsets insets = self.textContainerInset; if (_placeHolderLabel == nil ) { _placeHolderLabel = [[UILabel alloc] initWithFrame:CGRectMake(insets.left+5,insets.top,self.bounds.size.width - (insets.left +insets.right+10),1.0)]; _placeHolderLabel.lineBreakMode = NSLineBreakByWordWrapping; _placeHolderLabel.font = self.font; _placeHolderLabel.backgroundColor = [UIColor clearColor]; _placeHolderLabel.textColor = self.placeholderColor; _placeHolderLabel.alpha = 0; _placeHolderLabel.tag = 999; [self addSubview:_placeHolderLabel]; } _placeHolderLabel.text = self.placeholder; [_placeHolderLabel sizeToFit]; [_placeHolderLabel setFrame:CGRectMake(insets.left+5,insets.top,self.bounds.size.width - (insets.left +insets.right+10),CGRectGetHeight(_placeHolderLabel.frame))]; [self sendSubviewToBack:_placeHolderLabel]; } if( [[self text] length] == 0 && [[self placeholder] length] > 0 ) { [[self viewWithTag:999] setAlpha:1]; } [super drawRect:rect]; } - (void)setPlaceholder:(NSString *)placeholder{ if (_placeholder != placeholder) { _placeholder = placeholder; [self setNeedsDisplay]; } } @end
二:代理方法的內容
@protocol UITextViewDelegate <NSObject, UIScrollViewDelegate> @optional // 將要開始編輯 - (BOOL)textViewShouldBeginEditing:(UITextView *)textView; // 將要結束編輯 - (BOOL)textViewShouldEndEditing:(UITextView *)textView; // 開始編輯 - (void)textViewDidBeginEditing:(UITextView *)textView; // 結束編輯 - (void)textViewDidEndEditing:(UITextView *)textView; // 文本將要改變 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text; // 文本發生改變 - (void)textViewDidChange:(UITextView *)textView; // 焦點發生改變 - (void)textViewDidChangeSelection:(UITextView *)textView; // 是否允許對文本中的URL進行操作 - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange NS_AVAILABLE_IOS(7_0); // 是否允許對文本中的富文本進行操作 - (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange NS_AVAILABLE_IOS(7_0);