把自己以往做的项目中的一些小功能抠出来写在这里,供大家使用,会一直持续更新
UITextView自适应高度
1.KVO
//静态变量的地址可以保证context的独一无二
static void * abc = &abc;
- (void)viewDidLoad {
[super viewDidLoad];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(20, 20, 200, 35)];
[self.view addSubview:textView];
textView.layer.borderColor = [UIColor lightGrayColor].CGColor;
textView.layer.borderWidth = 1;
textView.layer.cornerRadius = 5;
textView.font = [UIFont systemFontOfSize:20];
//为textView添加一个观察者,来观察他的contentSize属性的变化
//context:上下文. 对某一个属性或者方法之类的 跨方法使用时的唯一标识
[textView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:abc];
}
//当前对象 如果观察到了 某些变化时就会触发下方的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
//使用context作为判断依据 尽量不要使用keyPath
if (context == abc) {
NSLog(@"%@", change);
CGSize size = [change[@"new"] CGSizeValue];
UITextView *textView = (UITextView *)object;
CGRect frame = textView.frame;
//限制高度 根据打印,5行时 高度136 高度未变化也不更新布局
if (size.height > 140 || size.height == frame.size.height) {
return;
}
frame.size.height = size.height;
textView.frame = frame;
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
2.sizeThatFits:
//文本更改时触发高度自适应
- (void)textViewDidChange:(UITextView *)textView {
//根据文本调整textView的高度
CGSize sizeToFit = [textView sizeThatFits:CGSizeMake(textView.frame.size.width-16, MAXFLOAT)];
//判断高度是否变化,未变化不重新布局
if (sizeToFit.height != textView.frame.size.height) {
textView.frame = CGRectMake(textView.frame.origin.x, textView.frame.origin.y, textView.frame.size.width, sizeToFit.height);
}
}
实现二维码扫描的空心遮罩
cropRect为扫描二维码框的Rect
1.CoreGraphics -> CGPath
- (void)setCropRect:(CGRect)cropRect{
CAShapeLayer *cropLayer = [[CAShapeLayer alloc] init];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, nil, cropRect);
CGPathAddRect(path, nil, self.view.bounds);
[cropLayer setFillRule:kCAFillRuleEvenOdd];//生成空心遮罩
[cropLayer setPath:path];
CFRelease(path);//CF对象记得手动释放
[cropLayer setFillColor:[UIColor blackColor].CGColor];
[cropLayer setOpacity:0.3];
[cropLayer setNeedsDisplay];
[self.view.layer addSublayer:cropLayer];
}
2.UIKit -> UIBezierPath
- (void)setCropRect:(CGRect)cropRect{
CAShapeLayer *cropLayer = [[CAShapeLayer alloc] init];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.view.bounds cornerRadius:0];
UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:cropRect];
[path appendPath:rectPath];
[cropLayer setFillRule:kCAFillRuleEvenOdd];//生成空心遮罩
[cropLayer setPath:path.CGPath];
[cropLayer setFillColor:[UIColor blackColor].CGColor];
[cropLayer setOpacity:0.3];
[cropLayer setNeedsDisplay];
[self.view.layer addSublayer:cropLayer];
}
UIImageView的手势缩放
通过UIScrollViewDelegate中的代理方法来对UIImageView实现简单的缩放功能
附加手势实现:
缩放比例为1(未进行缩放)时,放大倍数
缩放比例大于1(进行过缩放)时,还原倍数
_scrollView.minimumZoomScale = 1.0; // 最小缩放比例
_scrollView.maximumZoomScale = 6.0; // 最大缩放比例
_imageView.userInteractionEnabled = YES; // 记得打开UIImageView的用户交互,否则手势无法触发
#pragma mark - UIScrollView Delegate
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return _imageView; // 指定需要缩放的view,否则无法实现缩放功能(可为多个view)
}
#pragma mark - GestureRecogizer Method
- (void)addGestureRecognizer {
// 双击的 Recognizer
UITapGestureRecognizer* doubleRecognizer;
doubleRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)];
doubleRecognizer.numberOfTapsRequired = 2; // 双击
// _imageView添加双击手势
[_imageView addGestureRecognizer:doubleRecognizer];
}
-(void)doubleTap:(UITapGestureRecognizer*)recognizer
{
if (_scrollView.zoomScale > 1.0) {//放大过 --> 还原大小
[UIView animateWithDuration:.2 animations:^{
_scrollView.zoomScale = 1.0;
}];
} else {//未放大 --> 放大2倍
[UIView animateWithDuration:.2 animations:^{
_scrollView.zoomScale = 2.0;
}];
}
}
JSON与JSON字符串相互转换
1.JSON转JSON字符串
思路:NSDictionary/NSArray -> NSData -> NSString
方法:
+ (NSString *)parseJSONToJSONString:(id)JSON {
NSError *error;
NSData *JSONData = [NSJSONSerialization dataWithJSONObject:JSON options:NSJSONWritingPrettyPrinted error:&error];
NSString *JSONString = [[NSString alloc] initWithData:JSONData encoding:NSUTF8StringEncoding];
return JSONString;
}
2.JSON字符串转JSON
思路:NSString -> NSData -> NSDictionary/NSArray
方法:
+ (id)parseJSONStringToJSON:(NSString *)JSONString {
NSData *JSONData = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
id JSON = [NSJSONSerialization JSONObjectWithData:JSONData options:NSJSONReadingMutableLeaves error:nil];
return JSON;
}
备注:
1.NSJSONReadingOptions
typedef NS_OPTIONS(NSUInteger, NSJSONReadingOptions) {
NSJSONReadingMutableContainers = (1UL << 0),
NSJSONReadingMutableLeaves = (1UL << 1),
NSJSONReadingAllowFragments = (1UL << 2)
} API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
NSJSONReadingMutableContainers // 返回可变容器,NSMutableDictionary或NSMutableArray
NSJSONReadingMutableLeaves // 不仅返回的最外层是可变的, 内部的子数值或字典也是可变对象
NSJSONReadingAllowFragments // 返回允许JSON字符串最外层既不是NSArray也不是NSDictionary,但必须是有效的JSON Fragment.
2.NSJSONWritingOptions
typedef NS_OPTIONS(NSUInteger, NSJSONWritingOptions) {
NSJSONWritingPrettyPrinted = (1UL << 0),
NSJSONWritingSortedKeys API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0)) = (1UL << 1)
} API_AVAILABLE(macos(10.7), ios(5.0), watchos(2.0), tvos(9.0));
NSJSONWritingPrettyPrinted // 是将生成的json数据格式化输出,这样可读性高,不设置则输出的json字符串就是一整行
NSJSONWritingSortedKeys // 输出的json字符串就是一整行 仅支持iOS11以上
利用UIBezierPath绘制三角形
- (void)drawRect:(CGRect)rect {
//绘制路径
UIBezierPath *path = [UIBezierPath bezierPath];
//设置颜色
[self.triangleColor set];
//-------------绘制三角形------------
//
// B
// /\
// / \
// / \
// /__ __\
// A C
//
//设置起点 A
[path moveToPoint:CGPointMake(0, rect.size.height)];
//添加一根线到某个点 B
[path addLineToPoint:CGPointMake(rect.size.width * 0.5, 0)];
//添加一根线到某个点 C
[path addLineToPoint:CGPointMake(rect.size.width, rect.size.height)];
//关闭路径
[path closePath];
//填充(会把颜色填充进去)
[path fill];
}
验证码倒计时
- (void)theCountdown
{
__block int timeout = 60;
__weak __typeof(self)weakSelf = self;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, <#intervalInSeconds#> * NSEC_PER_SEC, <#leewayInSeconds#> * NSEC_PER_SEC); intervalInSeconds(倒计时间隔),leeway(期望的延迟时间)
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t _Nullable handler); dispatch_block_t 为block
dispatch_source_set_event_handler(timer, ^{
//因为^{}为block,有循环引用问题,所以需要弱引用倒计时按钮
if(timeout<=0){
//倒计时结束,关闭
dispatch_source_cancel(_timer);
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.codeBtn setTitle:@"发送验证码" forState:0];
[weakSelf.codeBtn setTitleColor:[UIColor whiteColor] forState:0];
[weakSelf.codeBtn setUserInteractionEnabled:YES];
});
}else{
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.codeBtn setTitle:[NSString stringWithFormat:@"%ds",timeout] forState:0];
[weakSelf.codeBtn setTitleColor:[UIColor whiteColor] forState:0];
[weakSelf.codeBtn setUserInteractionEnabled:NO];
});
timeout--;
}
});
dispatch_resume(timer);
}
下拉放大
#define headerHeight 200
@property (nonatomic, strong) UIImageView *iconIV;
- (void)viewDidLoad {
[super viewDidLoad];
self.iconIV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"scrollViewImage"]];
self.iconIV.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, headerHeight);
//放大时不改变宽高比例
self.iconIV.contentMode = UIViewContentModeScaleAspectFill;
self.iconIV.clipsToBounds = YES;
/*
头部视图 x y width 都是不能设置的 而图片要随着下拉 调整y轴 所以图片不能直接作为头部视图
可以把图片放到一个view里 view作为头部 而且view不能剪切子视图
*/
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, headerHeight)];
headerView.clipsToBounds = NO;//不剪切子视图超出部分!!!
[headerView addSubview:self.iconIV];
self.tableView.tableHeaderView = headerView;
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
cell.textLabel.text = @"这是个下拉放大的Demo!";
return cell;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGPoint offset = scrollView.contentOffset;
NSLog(@"%f", offset.y);
//根据打印值:向上移动 正数 向下移动 负数
CGFloat height = headerHeight - offset.y;
self.iconIV.frame = CGRectMake(0, offset.y, self.view.frame.size.width, height);
}