一行代码实现UITextView自适应高度

Github:Demo链接

使用方法

  1. 将Demo的两个文件拖入项目中,导入头文件


    一行代码实现UITextView自适应高度_第1张图片
    Screen Shot 2018-12-05 at 3.25.32 PM.png

2.使用需要改变高度的textView调用以下方法并设置最小和最大高度, 0为不限制, 调用了之后,即实现高度自适应了

    [textView autoChangeWithMinHeight:0 maxHeight:0 changeBlock:^(CGFloat height){
        NSLog(@"高度变化了=%f",height);
    }];

实现思路

1.UITextView 继承UIScrollView ,文本变化时,contentSize会更改
2.根据contentSize的变化,去设置UITextView的高度
3.使用KVO去监听contentSize的变化,以改变view高度

具体实现过程

  1. 当textView实例调用autoChangeHeightWithBlock 该方法时,通过KVO,让textView自身观察自身的属性contentSize的变化。因类别不能直接声明属性,所以将回调通过context传递给kvo的通知方法。
- (void)autoChangeWithMinHeight:(CGFloat)minHeight maxHeight:(CGFloat)maxHeight changeBlock:(XWChangeHeightBlock)changeHeightBlock{

    NSMutableArray *objects = [NSMutableArray arrayWithObjects:@(maxHeight),@(minHeight), nil];
    if( changeHeightBlock ){
        [objects addObject:changeHeightBlock];
    }
    //__bridge_retained 持有objects 避免传过去的Objects 已经被释放
    [self registerForKVOWithContext:(__bridge_retained void*)objects];
}

- (void)registerForKVOWithContext:(void*)context {
    
    //若已经添加过KVO,则不再添加
    if( [[NSUserDefaults standardUserDefaults] boolForKey:self.markKey] ){
        NSLog(@"已添加过观察者,本次添加会被忽略");
        return;
    }
    
    [self addObserver:self forKeyPath:self.keyPath options:(NSKeyValueObservingOptionNew) context:context];
    
    //添加一个标记,用来标记已经添加过观察者,为了移除观察者
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:self.markKey];
}

2.在KVO 的通知方法中,重新设置textView的高度,并调用回调

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    
    //取得新的contentSize的值
    NSValue *sizeValue =
    [self getNewValueWithObject:object change:change];
    if( !sizeValue ) return;
    
    //取得传递过来的minHeight, maxHeight, 回调block
    CGFloat maxHeight = 0, minHeight = 0;
    XWChangeHeightBlock changeBlock =
    [self getChangeBlockWithMaxHeight:&maxHeight minHeight:&minHeight context:context];
    
    //计算textview的新高度
    CGFloat newHeight =
    [self getNewHeightWithValue:sizeValue minHeight:minHeight maxHeight:maxHeight];
    
    //设置textview的高度,设置成功则回调,未成功则不回调
    if( [self changeTextViewFrameWithNewHeight:newHeight] && changeBlock ){
        
        changeBlock(newHeight);
    }
}

3.在dealloc 中移除观察者

- (void)dealloc{
    //移除观察者
    [self unregisterFromKVO];
}

- (void)unregisterFromKVO {
    if( [[NSUserDefaults standardUserDefaults] boolForKey:self.markKey] ){
        [self removeObserver:self forKeyPath:self.keyPath];
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:self.markKey];
    }
}

4.为了保证markKey的唯一性,使用了textView的内存地址作为key值

- (NSString*)keyPath{
    return @"contentSize";
}

- (NSString*)markKey{
    NSString *key = [NSString stringWithFormat:@"%p",(self)];
    return key;
}

总结

当移除观察者的时候,如果本身未添加,则会崩溃,但我又没有找到判断是否已添加某观察者办法,所以使用了NSUserDefaults 去缓存是否已添加过观察者。OC类型转C类型时,最好使用__bridge_retained 进行持有 ,不然可能存在在使用过程中被释放的情况。

你可能感兴趣的:(一行代码实现UITextView自适应高度)