iOS开发小技巧和常用工具类(平时收集和整理)
前言
作为一个开发者应该学会去整理收集开发常用的工具类,这些复用的工具可以在项目开发中给你很大程度提高你的工作效率。难道你不想早点完成工作,然后出去撩妹、陪女朋友或者回家陪老婆孩子吗?反正我想早点回家😄。
更新
2017-12-12 PM 重新编辑
- 使用Pod管理公共类(常用的工具类和分类)传送门-->
- 为后期的组件化学习和项目重构做准备
想学习的小伙伴欢迎一起哈
由于工作原因(其实是本人比较懒) 文章更新会很慢或者忘记更新,关于更新的说明: 文章中只提供方法名说明在俺的github上可以找到具体实现
小方法小技巧大用处(我的github没有哦)
建好Base类
BaseViewController
所有的控制器继承于这个base,在此基类你可以对所有控制器共同的属性和方法进行控制。比如设置控制器View的背景色,下面是我的base,只供参考
BaseViewController.h
@interface BaseViewController : UIViewController
@property (nonatomic, assign)VCType vcType;
@property (nonatomic, strong)id vcParam;
- (void)push:(NSString *)vcName;
- (void)push:(NSString *)vcName param:(id)param;
- (void)openWeb:(NSString *)urlStr title:(NSString *)title;
@end
BaseViewController.m
- (void)push:(NSString *)vcName{
Class classVC = NSClassFromString(vcName);
UIViewController *vc = [classVC new];
[self.navigationController pushViewController:vc animated:YES];
}
- (void)push:(NSString *)vcName param:(id)param{
Class classVC = NSClassFromString(vcName);
BaseViewController *vc = [classVC new];
vc.vcParam = param;
[self.navigationController pushViewController:vc animated:YES];
}
- (void)openWeb:(NSString *)urlStr title:(NSString *)title{
WebViewController *vc = [[WebViewController alloc]init];
vc.strUrl = urlStr;
vc.title = title;
[self.navigationController pushViewController:vc animated:YES];
}
BaseLabel、BaseTextField、BaseTableViewCell和BaseButton等等我就不一一列举了
在这些基类中可以设置常用的初始化设置,比如我常用的label字体是15号黑色等
分享个亲身经历:有一次项目经理要求把所有输入框的光标颜色改为主题色,当时我用的是扩展替换了初始化方法,如果当时我有这个基类就可以一句代码搞定了ಥ_ಥ
runtime轻松搞定初始化UITabBarController
- (void)initTabBarWithTitles:(NSArray *)titles
vcNames:(NSArray *)classes
images:(NSArray *)images
selectedImages:(NSArray *)selectedImages{
NSDictionary *nomalTextDic = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:12.0],
NSForegroundColorAttributeName:UIColorFromRGB(MAIN_GRY_COLOR)};
NSDictionary *higjlightTextDic = @{NSFontAttributeName:[UIFont boldSystemFontOfSize:12.0],
NSForegroundColorAttributeName:UIColorFromRGB(THEME_COLOR)};
NSMutableArray *vcArray = [NSMutableArray array];
for (int i = 0;i < titles.count ; i ++) {
UITabBarItem *first_item = [[UITabBarItem alloc]initWithTitle:titles[i] image:[UIImage imageWithFileName:images[i]] selectedImage:[[UIImage imageWithFileName:selectedImages[i]]imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]];
first_item.imageInsets = UIEdgeInsetsMake(0, 0, 0, 0);
[first_item setTitleTextAttributes:nomalTextDic forState:UIControlStateNormal];
[first_item setTitleTextAttributes:higjlightTextDic forState:UIControlStateSelected];
first_item.tag = VCType_First + i;
Class classVC = NSClassFromString(classes[i]);
BaseViewController *vc = [classVC new];
vc.tabBarItem = first_item;
[vcArray addObject:vc];
}
self.viewControllers = vcArray;
}
推荐ReactiveCocoa
准备工作
- ReactiveCocoa的简单介绍和使用(这是一个传送门)
- pod导入(OC版本建议导入2.5版本)
pod 'ReactiveCocoa', '~> 2.5'
在这里我只介绍一个我经常用到的小方法:由于现在做的项目很多页面需要打开系统相册和相机所以简单封装了一个打开系统相机和相册的方法
VDImagePicker.h
@interface VDImagePicker : UIImagePickerController
+ (void)pickImageWithController:(id)vc sourceType:(UIImagePickerControllerSourceType)sourceType callback:(void(^)(UIImage *image))callback;
@end
VDImagePicker.m
#import "VDImagePicker.h"
#import <ReactiveCocoa/ReactiveCocoa.h>
@interface VDImagePicker ()
@property (nonatomic, copy)void (^callback)(UIImage *image);
@end
@implementation VDImagePicker
+ (void)pickImageWithController:(id)vc sourceType:(UIImagePickerControllerSourceType)sourceType callback:(void (^)(UIImage *))callback{
if (sourceType == UIImagePickerControllerSourceTypeCamera&&![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
VDLog(@"该设备不支持相机调用哦!ಥ_ಥ");
return;
}
VDImagePicker *imagePicker = [[VDImagePicker alloc]init];
imagePicker.callback = callback;
[[vc rac_signalForSelector:@selector(imagePickerController:didFinishPickingImage:editingInfo:) fromProtocol:@protocol(UIImagePickerControllerDelegate)] subscribeNext:^(id x) {
if (imagePicker.callback) {
imagePicker.callback([x objectAtIndex:1]);
}
[imagePicker dismissViewControllerAnimated:YES completion:^{
imagePicker.callback = nil;
[imagePicker removeFromParentViewController];
}];
}];
imagePicker.allowsEditing = YES;
imagePicker.delegate = vc;
imagePicker.sourceType = sourceType;
[vc presentViewController:imagePicker animated:YES completion:nil];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
一个方法搞定访问相机和相册是不是很爽O(∩_∩)O~,不过一般是配合acitonSheet使用,自己写个兼容iOS8.0的UIAlertController的扩展方法配合使用吧!
↑↑↑↑↑↑ 上面的内容我的github没有哦
↓↓↓↓↓↓ 下面的内容都在我的github
- UINavigationBar扩展改变导航栏背景色
- FMDB二次封装
- 文件与文件夹操作
- 下面的内容我就不列举了
一、常用的宏定义
善于利用宏在开发中过程中会减少很多工作量比如定义开发过程中的常用尺寸,这样在后续开发中不用为了改一个相同尺寸而满世界的去找这个尺寸在哪用到了。宏定义用的很广泛,例如屏幕的宽高,网络请求的baseUrl等等
下面是自己整理的一些示例:
#if TARGET_IPHONE_SIMULATOR//模拟器
#define PHONE_MARK 0
#elif TARGET_OS_IPHONE//真机
#define PHONE_MARK 1
#endif
//16进制颜色转换
#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
//屏幕的宽高
#define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width
#define SCREEN_HEIGHT [[UIScreen mainScreen] bounds].size.height
//转化为weak对象(block循环引用使用时)
#define WeakObj(o) __weak typeof(o) obj##Weak = o;
自定义log并以一个宏区分是否为debug模式
#define DEBUGGER 1 //上线版本屏蔽此宏
#ifdef DEBUGGER
/* 自定义log 可以输出所在的类名,方法名,位置(行数)*/
#define VDLog(format, ...) NSLog((@"%s [Line %d] " format), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define VDLog(...)
#endif
示例在viewDidLoad中
VDLog(@"很好用的");
输出结果为
-[ViewController viewDidLoad] [Line 35] 很好用的
一、UIView的扩展类(category)
1.在开发中会经常获取或者改变控件的位置大小,所以这样的类别就大有用处了,你可以方便的改变任意位置大小属性(这样在做一些控件的位置大小动画也很方便),废话不多说直接上代码。
UIView+tool.h
#import <UIKit/UIKit.h>
@interface UIView (tool)
#pragma mark [frame]
/**
* view的x(横)坐标
*/
@property (nonatomic, assign)CGFloat v_x;
/**
* view的y(纵)坐标
*/
@property (nonatomic, assign)CGFloat v_y;
/**
* view的宽度
*/
@property (nonatomic, assign)CGFloat v_w;
/**
* view的高度
*/
@property (nonatomic, assign)CGFloat v_h;
#pragma mark [layer]
/**
* view的圆角半径
*/
@property (nonatomic, assign)CGFloat v_cornerRadius;
@end
2.此扩展的功能为给一些空的页面添加默认图,比如网页加载失败时或者一些tableView的列表数据为空时给出一个提示页并且增加重新加载机制。这个在大部分项目中必不可少的一部分,所以这个东西就诞生了!
UIView+EmptyShow.h
#import <UIKit/UIKit.h>
typedef void (^ReloadDataBlock)();
typedef NS_ENUM(NSInteger, ViewDataType)
{
ViewDataTypeMyOrder = 0,//我的订单
ViewDataTypeLoadFail//web页加载失败
};
@interface CustomerWarnView : UIView
//提示图
@property (nonatomic, strong) UIImageView *imageView;
//提示文字
@property (nonatomic, strong) UILabel *tipLabel;
//刷新按钮
@property (nonatomic, strong) UIButton *loadBtn;
//用于回调
@property (nonatomic, copy) ReloadDataBlock reloadBlock;
//初始化
+ (CustomerWarnView *)initWithFrame:(CGRect)frame andType:(ViewDataType)type;
@end
@interface UIView (EmptyShow)
@property (strong, nonatomic) CustomerWarnView *warningView;
/**
* 空页面显示提醒图与文字并添加重新刷新
*
* @param emptyType 页面的展示的数据类别(例如:我的订单或者web页)
* @param haveData 是否有数据
* @param block 重新加载页面(不需要时赋空)
*/
- (void)emptyDataCheckWithType:(ViewDataType)emptyType
andHaveData:(BOOL)haveData
andReloadBlock:(ReloadDataBlock)block;
@end
源码结构分析:源码中使用runtime给类别添加属性并使用纯代码实现一个自定义View,其中布局采用了第三库Masonry(需要的请自行导入),刷新的回调使用的block。
用法:只需要在获取数据后或者网页加载失败的回调中使用tableView或者webView等调用此方法
- (void)emptyDataCheckWithType:(ViewDataType)emptyType
andHaveData:(BOOL)haveData
andReloadBlock:(ReloadDataBlock)block;
示例图
二、NSString的扩展类
在做项目中对字符串的处理是必不可少的,或者要对一些数据进行字符串的转换等等。下面就介绍几个自己在项目中经常使用的方法。下面的category包括:时间戳转化为字符串的各类形式,获取当前设备deviceId和字符串向富文本的转换(哥现在做的项目大部分页面都用到了这东西)。
NSString+tool.h
#import <Foundation/Foundation.h>
static NSString *const XCColorKey = @"color";
static NSString *const XCFontKey = @"font";
static NSString *const XCRangeKey = @"range";
/**
range的校验结果
*/
typedef enum
{
RangeCorrect = 0,
RangeError = 1,
RangeOut = 2,
}RangeFormatType;
@interface NSString (tool)
#pragma mark - 常用工具
/**
* 获取当前Vindor标示符
*
* @return deviceId
*/
+ (NSString *) getDeviceIdentifierForVendor;
/**
* 转换为XXXX年XX月XX日
*
* @param time 时间戳
*
* @return 年月日
*/
+ (NSString*) format:(NSTimeInterval) time;
/**
* 转化为XX时XX分XX秒
*
* @param time 时间戳
*
* @return 时:分:秒
*/
+ (NSString*) formatTime:(NSTimeInterval) time;
/**
* 转化为XXXX年XX月XX日XX时XX分XX秒
*
* @param time 时间戳
*
* @return 年月日 时:分:秒
*/
+ (NSString *) formatDateAndTime:(NSTimeInterval)time;
#pragma mark - 校验NSRange
/**
* 校验范围(NSRange)
*
* @param range Range
*
* @return 校验结果:RangeFormatType
*/
- (RangeFormatType)checkRange:(NSRange)range;
#pragma mark - 改变单个范围字体的大小和颜色
/**
* 改变字体的颜色
*
* @param color 颜色(UIColor)
* @param range 范围(NSRange)
*
* @return 转换后的富文本(NSMutableAttributedString)
*/
- (NSMutableAttributedString *)changeColor:(UIColor *)color
andRange:(NSRange)range;
/**
* 改变字体大小
*
* @param font 字体大小(UIFont)
* @param range 范围(NSRange)
*
* @return 转换后的富文本(NSMutableAttributedString)
*/
- (NSMutableAttributedString *)changeFont:(UIFont *)font
andRange:(NSRange)range;
/**
* 改变字体的颜色和大小
*
* @param colors 字符串的颜色
* @param colorRanges 需要改变颜色的字符串范围
* @param fonts 字体大小
* @param fontRanges 需要改变字体大小的字符串范围
*
* @return 转换后的富文本(NSMutableAttributedString)
*/
- (NSMutableAttributedString *)changeColor:(UIColor *)color
andColorRang:(NSRange)colorRange
andFont:(UIFont *)font
andFontRange:(NSRange)fontRange;
#pragma mark - 改变多个范围内的字体和颜色
/**
* 改变多段字符串为一种颜色
*
* @param color 字符串的颜色
* @param ranges 范围数组:[NSValue valueWithRange:NSRange]
*
* @return 转换后的富文本(NSMutableAttributedString)
*/
- (NSMutableAttributedString *)changeColor:(UIColor *)color andRanges:(NSArray<NSValue *> *)ranges;
/**
* 改变多段字符串为同一大小
*
* @param font 字体大小
* @param ranges 范围数组:[NSValue valueWithRange:NSRange]
*
* @return 转换后的富文本(NSMutableAttributedString)
*/
- (NSMutableAttributedString *)changeFont:(UIFont *)font andRanges:(NSArray<NSValue *> *)ranges;
@end
下面是转化为富文本的示例代码
UILabel *lable = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, V_SCREEN_WIDTH, 100)];
lable.center = self.view.center;
lable.numberOfLines = 0;
lable.textAlignment = NSTextAlignmentCenter;
lable.textColor = [UIColor blackColor];
lable.font = [UIFont systemFontOfSize:15.0];
[self.view addSubview:lable];
NSString *str = @"这个是绿色字号是20";
//富文本
lable.attributedText = [str changeColor:[UIColor greenColor] andColorRang:NSMakeRange(3, 2) andFont:[UIFont systemFontOfSize:20] andFontRange:NSMakeRange(8, 2)];
效果如图所示
三、UIImage扩展类
下面分享的是对image进行处理的扩展类,主要包含:图片的缩放,剪切和压缩
UIImage+tool.h
#import <UIKit/UIKit.h>
@interface UIImage (tool)
/**
* 等比缩放
*
* @param size 设置尺寸
*
* @return image
*/
-(UIImage *)scaleImageToSize:(CGSize)size;
/**
* 自定长宽
*
* @param reSize 设置尺寸
*
* @return image
*/
-(UIImage *)imageReSize:(CGSize)reSize;
/**
* 剪切
*
* @param cutRect 选取截取部分
*
* @return image
*/
-(UIImage *)cutImageWithRect:(CGRect)cutRect;
/**
* 压缩
*
* @param image 待压缩的图片
*
* @return image
*/
+ (UIImage *)smallTheImage:(UIImage *)image;
/**
* 压缩(上传)
*
* @param image 待压缩图片
*
* @return 图片的二进制文件
*/
+ (NSData *)smallTheImageBackData:(UIImage *)image;
/**
* view转位图(一般用于截图)
*
* @param view 需要转化的view
*
* @return image
*/
+ (UIImage *)imageFromView:(UIView*)view;
@end
四、UIControl的扩展类
相信大家都遇到过按钮反复点击问题的处理,下面介绍的UIControl+clickRepeatedly扩展类,只需一句代码搞定此问题。不用太感谢我哦!(温馨提示:建议此类别别大范围使用)。
UIControl+clickRepeatedly.h
#import <UIKit/UIKit.h>
@interface UIControl (clickRepeatedly)
/**
* 设置点击的间隔(防止反复点击)
*/
@property (nonatomic, assign)NSTimeInterval clickInterval;
@property (nonatomic, assign)BOOL ignoreClick;
@end
UIControl+clickRepeatedly.m
#import "UIControl+clickRepeatedly.h"
#import <objc/runtime.h>
static const char *ClickIntervalKey;
static const char *IgnoreClick;
@implementation UIControl (clickRepeatedly)
- (void)setClickInterval:(NSTimeInterval)clickInterval{
objc_setAssociatedObject(self, &ClickIntervalKey, @(clickInterval), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSTimeInterval)clickInterval{
return [objc_getAssociatedObject(self, &ClickIntervalKey) doubleValue];
}
- (void)setIgnoreClick:(BOOL)ignoreClick{
objc_setAssociatedObject(self, &IgnoreClick, @(ignoreClick), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)ignoreClick{
return [objc_getAssociatedObject(self, &IgnoreClick) boolValue];
}
+ (void)load
{
//替换点击事件
Method a = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
Method b = class_getInstanceMethod(self, @selector(rc_sendAction:to:forEvent:));
method_exchangeImplementations(a, b);
}
- (void)rc_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
if (self.ignoreClick) {
return;
}
else{
[self rc_sendAction:action to:target forEvent:event];
}
if (self.clickInterval > 0)
{
self.ignoreClick = YES;
[self performSelector:@selector(setIgnoreClick:) withObject:@(NO) afterDelay:self.clickInterval];
}
}
@end
看上面的代码是不是很简单,其实使用起来更加简单,只需要在你初始化好的btn
设置如下代码:
btn.clickInterval = 3;//点击完三秒后才能点哦
五、用户信息(单例模式)
UserInfoModel:实现一些轻量级的用户信息存储和类的归档存入
六、AFNetWorking的二次封装
1.简单的HTTP(POST)请求:其中添加了多种请求错误判断和debug模式下打印响应成功的数据,最后采用block进行响应结果的回调
/**
* 发送请求
*
* @param requestAPI 请求的API
* @param vc 发送请求的视图控制器
* @param params 请求参数
* @param className 数据模型
* @param response 请求的返回结果回调
*/
- (void)sendRequestWithAPI:(NSString *)requestAPI
withVC:(UIViewController *)vc
withParams:(NSDictionary *)params
withClass:(Class)className
responseBlock:(RequestResponse)response;
2.创建下载任务并对下载进度,任务对象和存储路径等进行回调(支持多任务下载),创建的任务会自动加到下载队列中,下载进度的回调自动回到主线程(便于相关UI的操作)
/**
* 创建下载任务
*
* @param url 下载地址
* @param fileName 文件名
* @param downloadTask 任务
* @param progress 进度
* @param result 结果
*/
- (void)createDdownloadTaskWithURL:(NSString *)url
withFileName:(NSString *)fileName
Task:(DownloadTask)downloadTask
Progress:(TaskProgress)progress
Result:(TaskResult)result;
3.创建上传任务与下载方法回调基本一致
/**
* 创建上传任务
*
* @param url 上传地址
* @param mark 任务标识
* @param data 序列化文件
* @param uploadTask 任务
* @param progress 进度
* @param result 结果
*/
- (void)createUploadTaskWithUrl:(NSString *)url
WithMark:(NSString *)mark
withData:(NSData *)data
Task:(UploadTask)uploadTask
Progress:(TaskProgress)progress
Result:(TaskResult)result;
七、校验工具类
1.手机号校验
+(BOOL) isValidateMobile:(NSString *)mobile
2.邮箱校验
+(BOOL)isValidateEmail:(NSString *)email
3.车牌号校验
+(BOOL) isvalidateCarNo:(NSString*)carNo
4.身份证号验证
+(BOOL) isValidateIDCardNo:(NSString *)idCardNo
待续
本笔记会持续更新。
附上github上源码,工具类全部在tool文件目录下
别着急离开,留点足迹给个喜欢!您的鼓励是我最大的支持↖(ω)↗