1.NSDate转成时间戳出现一年的误差
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"YYYY-MM-dd"];
//从服务器拿到的时间值1451347200000,服务器上显示的时间是:29-12月-15
NSDate *confromTimesp = [NSDate dateWithTimeIntervalSince1970:[date longLongValue]/1000];
NSString *timeString = [formatter stringFromDate:confromTimesp];
打断点输出后显示的是:2016-12-29
第二种方法的时间戳格式是:yyyy-MM-dd
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd"];
//从服务器拿到的时间值1451347200000,服务器上显示的时间是:29-12月-15
NSDate *confromTimesp = [NSDate dateWithTimeIntervalSince1970:[date longLongValue]/1000];
NSString *timeString = [formatter stringFromDate:confromTimesp];
打断点输出后显示的是:2015-12-29
可以看到同一天打印的日期差距一年,这个bug太恶心,而且还是日期在年末的情况下才出现的。
查了下YYYY-MM-dd和yyyy-MM-dd的区别,以下仅供参考:
“YYYY format” 是 “ISO week numbering system”,“yyyy format” 是 “Gregorian Calendar”。
以后处理时间戳可要注意了要使用小写的yyyy就行了.
2视图切换(push与modal混用)
在实际开发过程中往往视图切换是混用的,比如在项目中LoginViewController,往往是modal出来的,但是在登陆页面,往往有跳转到注册或者是忘记密码页面,此时又需要push过去,往往会push失败(原因:当登录界面的作为模态视图的话. 当我们离开当前界用presentViewController弹出登录界面的话..就会导致在登录界面这个模态视图中视图间的跳转会失效. 这是由于模态视图其实是不同于导航控制器的新的视图, 并且只有将这个视图处理完成后才能回到原来的视图. 模态视图就相当于死胡同 进入就必须原路返回, 也就是不可以在模态视图中执行页面跳转.)
如何让模态中的self.navigationController不空呢, 也就很简单了, 只需要将登录这个视图控制器封装成navigationController 弹出来, 而这个模态只作为这个navigationController的rootViewController即可
LoginViewCOntroller * loginVC = [LoginViewCOntroller alloc] init];
UINavigationController* navi = [[UINavigationController alloc] initWithRootViewController:loginVC];
[self.navigationController presentViewController:navi animated:YES completion:nil];
然后从LoginViewController push切换RegisterViewController就没有问题了。
3. 在自定义View内部跳转控制器
3.1拿到主窗口的根控制器UITabBarController,用UITabBarController 选中的控制器(导航控制器)进行push
UITabBarController *tabBarVc = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController;
UINavigationController *nav = (UINavigationController *)tabBarVc.selectedViewController;
[nav pushViewController:<#(nonnull UIViewController *)#> animated:YES];
3.2 主流框架下,自定义view里利用到UITabBarController控制器 modal 出来的导航控制器push
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
UINavigationController *nav = (UINavigationController *)root.presentedViewController;
[nav pushViewController:<#(nonnull UIViewController *)#> animated:YES];
4.服务器返回的数据类型动态改变
responseObject:{
"code":3;
"imgArr":["imgurl","imgurl2"]
}
举例,服务器返回的json中,imgArr字段标准的格式,应该是对应数组,所有无论数组中含有几条图片链接数据,返回的都应该是一个数组,但是服务人员往往会在数组中元素为空的时候返回一个null,这个时候如果不做处理的话APP就会崩溃,所以最好的方式就是做一下兼容。判断返回的数据是否是数组,如果是数组的话 并且元素个数大于0的话,那么开始做解析
id imgArr = response[@"imgArr"];
if ([imgArr isKindOfClass:[NSArray class]]) {
NSArray *dataArr =imgArr;
if (dataArr.count > 0) {
[dataArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
}
}
5.多线程上传图片
优化for循环上传多张图片。
将异步任务放在全局队列中添加到线程组中,有几张图片就创建几个异步任务。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
for (NSInteger i = 0 ; i < self.imageDataArr.count; i ++) {
dispatch_group_async(group, queue, ^{
//上传图片方法
[self upLoadImageWithIdx:i];
});
}
6.正则表达式校验输入规则
- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string {
if (textField == self.capacitySupply && textField.isFirstResponder) {
return [self validateNumber:string];
}
return YES;
}
- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string {
if (textField == self.capacitySupply && textField.isFirstResponder) {
return [self validateNumber:string];
}
return YES;
}
7 自定义label文字顶部对齐
#import <UIKit/UIKit.h>
typedef enum
{
VerticalAlignmentTop = 0, // default
VerticalAlignmentMiddle,
VerticalAlignmentBottom,
} VerticalAlignment;
@interface myUILabel : UILabel
{
@private
VerticalAlignment _verticalAlignment;
}
@property (nonatomic) VerticalAlignment verticalAlignment;
@end
#import "myUILabel.h"
@implementation myUILabel
@synthesize verticalAlignment = verticalAlignment_;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.verticalAlignment = VerticalAlignmentMiddle;
}
return self;
}
- (void)setVerticalAlignment:(VerticalAlignment)verticalAlignment {
verticalAlignment_ = verticalAlignment;
[self setNeedsDisplay];
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {
CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
switch (self.verticalAlignment) {
case VerticalAlignmentTop:
textRect.origin.y = bounds.origin.y;
break;
case VerticalAlignmentBottom:
textRect.origin.y = bounds.origin.y + bounds.size.height - textRect.size.height;
break;
case VerticalAlignmentMiddle:
// Fall through.
default:
textRect.origin.y = bounds.origin.y + (bounds.size.height - textRect.size.height) / 2.0;
}
return textRect;
}
-(void)drawTextInRect:(CGRect)requestedRect {
CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
[super drawTextInRect:actualRect];
}
@end
8当需要将字典转为模型的时候,如果遇到字典的键与模型类中属性的名称或是个数不一致的情况,可以尝试重写setValue:forUndefinedKey:方法。
在实际开发及应用过程中,经常会遇到通过外部数据构造的字典的键与自定义数据模型类中属性的名称或是个数不一致的情况。当使用setValuesForKeysWithDictionary:方法时,对于数据模型中缺少的、不能与任何键配对的属性的时候,系统会自动调用setValue:forUndefinedKey:这个方法,该方法默认的实现会引发一个NSUndefinedKeyExceptiony异常。
如果想要程序在运行过程中不引发任何异常信息且正常工作,可以让数据模型类重写setValue:forUndefinedKey:方法以覆盖默认实现,而且可以通过这个方法的两个参数获得无法配对键值。
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
9.对上传的图片进行压缩
//
// CompressPicturesTool.h
// MOffice
//
// Created by 方冬冬 on 2018/4/13.
// Copyright © 2018年 ChinaSoft. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, ImageQuality) {
ImageQualityNormal,
ImageQualityStandard,
ImageQualityHigh,
};
@interface CompressPicturesTool : NSObject
//压缩图片。保证清晰度的同时 压缩文件到指定的大小
+ (NSData *)zipImageWithImage:(UIImage *)image imageType:(ImageQuality)imageType;
@property (nonatomic,assign) ImageQuality imageType;
@end
//
// CompressPicturesTool.m
// MOffice
//
// Created by 方冬冬 on 2018/4/13.
// Copyright © 2018年 ChinaSoft. All rights reserved.
//
#import "CompressPicturesTool.h"
@implementation CompressPicturesTool
/**
压图片质量
@param image image
@return Data
*/
+ (NSData *)zipImageWithImage:(UIImage *)image imageType:(ImageQuality)imageType
{
if (!image) {
return nil;
}//图片最大200KB
CGFloat maxFileSize = 0;
if (imageType == ImageQualityNormal) {
maxFileSize = 100 * 1024;
}
if (imageType == ImageQualityStandard) {
maxFileSize = 150 * 1024;
}
if (imageType == ImageQualityHigh) {
maxFileSize = 200 * 1024;
}
CGFloat compression = 0.9f;
NSData *compressedData = UIImageJPEGRepresentation(image, compression);
while ([compressedData length] > maxFileSize) {
compression *= 0.9;
compressedData = UIImageJPEGRepresentation([[self class] compressImage:image newWidth:image.size.width*compression], compression);
}
return compressedData;
}
/**
* 等比缩放本图片大小
*
* @param newImageWidth 缩放后图片宽度,像素为单位
*
* @return self-->(image)
*/
+ (UIImage *)compressImage:(UIImage *)image newWidth:(CGFloat)newImageWidth
{
if (!image) return nil;
float imageWidth = image.size.width;
float imageHeight = image.size.height;
float width = newImageWidth;
float height = image.size.height/(image.size.width/width);
float widthScale = imageWidth /width;
float heightScale = imageHeight /height;
// 创建一个bitmap的context
// 并把它设置成为当前正在使用的context
UIGraphicsBeginImageContext(CGSizeMake(width, height));
if (widthScale > heightScale) {
[image drawInRect:CGRectMake(0, 0, imageWidth /heightScale , height)];
}
else {
[image drawInRect:CGRectMake(0, 0, width , imageHeight /widthScale)];
}
// 从当前context中创建一个改变大小后的图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 使当前的context出堆栈
UIGraphicsEndImageContext();
return newImage;
}
/**
*将图片缩放到指定的CGSize大小
* UIImage image 原始的图片
* CGSize size 要缩放到的大小
*/
+(UIImage*)image:(UIImage *)image scaleToSize:(CGSize)size{
// 得到图片上下文,指定绘制范围
UIGraphicsBeginImageContext(size);
// 将图片按照指定大小绘制
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
// 从当前图片上下文中导出图片
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
// 当前图片上下文出栈
UIGraphicsEndImageContext();
// 返回新的改变大小后的图片
return scaledImage;
}
+(NSData *)imageData:(UIImage *)myimage
{
//普通--100 标准- 150 高清---200
NSData *data=UIImageJPEGRepresentation(myimage, 1.0);
if (data.length>1024 *1024) {
if (data.length>10240*1024) {//10M以及以上
data=UIImageJPEGRepresentation(myimage, 0.1);//压缩之后1M~
}else if (data.length>5120*1024){//5M~10M
data=UIImageJPEGRepresentation(myimage, 0.2);//压缩之后1M~2M
}else if (data.length>2048*1024){//2M~5M
data=UIImageJPEGRepresentation(myimage, 0.5);//压缩之后1M~2.5M
}
//1M~2M不压缩
}
return data;
}
- (UIImage *)scaleToSize:(UIImage *)img size:(CGSize)size{
// 创建一个bitmap的context
// 并把它设置成为当前正在使用的context
UIGraphicsBeginImageContext(size);
// 绘制改变大小的图片
[img drawInRect:CGRectMake(0,0, size.width, size.height)];
// 从当前context中创建一个改变大小后的图片
UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();
// 使当前的context出堆栈
UIGraphicsEndImageContext();
//返回新的改变大小后的图片
return scaledImage;
}
@end
10 得到时间字符串
+(NSString *)getNowTimeTimestamp
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
[formatter setDateStyle:NSDateFormatterMediumStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss:sss"]; //
----------设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制
//设置时区,这个对于时间的处理有时很重要
NSTimeZone* timeZone = [NSTimeZone systemTimeZone];
[formatter setTimeZone:timeZone];
NSDate *datenow = [NSDate date];//现在时间,你可以输出来看下是什么格式
NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]*1000];
return timeSp;
}
11 动态修改网络框架的端口和IP
//在pch中定义宏
#define SAVEDEFAULTS(value,key) [[NSUserDefaults standardUserDefaults] setObject:value forKey:key];\
[[NSUserDefaults standardUserDefaults] synchronize];
#define GETDEFAULTS(key) [[[NSUserDefaults standardUserDefaults] objectForKey:key] stringByReplacingOccurrencesOfString:@" " withString:@""];
#define BaseI_P [[[NSUserDefaults standardUserDefaults] objectForKey:@"BaseI_P"] stringByReplacingOccurrencesOfString:@" " withString:@""]
#define Base_port [[[NSUserDefaults standardUserDefaults] objectForKey:@"Base_port"] stringByReplacingOccurrencesOfString:@" " withString:@""]
#define BaseIPNUM @"192.168.80.11"
#define BasePortNUm @"28100"
//在Appdelegate中使用
[[NSUserDefaults standardUserDefaults] setObject:BaseIPNUM forKey:@"BaseI_P"];
[[NSUserDefaults standardUserDefaults] setObject:BasePortNUm forKey:@"Base_port"];
[WSOAPIClient sharedClient].baseUrl = BASE_URL;
12使用 SDWebImageDownloader下载图片并且展示
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:[NSURL URLWithString:imageUrl] options:SDWebImageDownloaderAllowInvalidSSLCertificates progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
dispatch_async(dispatch_get_main_queue(), ^{
signImageView.image = image;
});
}];
13 对数组中模型对象进行排序(根据模型的某一个属性)
//排序
- (NSArray *)soartofflineArrWithArr:(NSMutableArray *)itemArr{
NSArray *resultArr = [itemArr sortedArrayUsingComparator:^NSComparisonResult(ReadListModel * obj1, ReadListModel *obj2)
{
return [obj1.READCODE compare:obj2.READCODE];
}];
return resultArr;
}
14并行队列的异步执行,任务组的使用,以及同步任务锁的添加
#pragma mark - Prevate Method
- (void)addTestData {
//并行队列创建
dispatch_queue_t concurrentQueue = dispatch_queue_create("zeluli.concurrent", DISPATCH_QUEUE_CONCURRENT);
//线程组创建
dispatch_group_t group = dispatch_group_create();
//同步锁
dispatch_semaphore_t lock = dispatch_semaphore_create(1);//其中参数1表示该新生成信号的总的信号量为1个。
for (int i = 0; i < 50; i ++) {
dispatch_group_async(group, concurrentQueue, ^{
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);//信号等待,它像一个安保,比如小区规定最多只能进入3辆车,而进入一辆车后名额就会减少一个,当剩下的名额为0的时候,再有汽车说要进去时,就只能在外面等待了,直到有名额闲置出来了,才能开进小区。
[self createTestModel];
dispatch_semaphore_signal(lock);//信号释放,当有一辆车从小区出来时,就腾出来了一个名额。
});
}
//线程组里面的队列任务执行完毕后,就可以 通知主线称更新数据。UI
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self updateDataSource];
});
}
关于信号量,一般可以用停车来比喻。
停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。
信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal
就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),
调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;
当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主
没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在
15根据文字内容计算文本高度
-(CGFloat)countTextHeight:(NSString *) text {
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:text];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineSpacing = 0;
UIFont *font = [UIFont systemFontOfSize:14];
[attributeString addAttribute:NSParagraphStyleAttributeName value:style range:NSMakeRange(0, text.length)];
[attributeString addAttribute:NSFontAttributeName value:font range:NSMakeRange(0, text.length)];
NSStringDrawingOptions options = NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading;
CGRect rect = [attributeString boundingRectWithSize:CGSizeMake(SCREEN_WIDTH - 30, CGFLOAT_MAX) options:options context:nil];
return rect.size.height + 40;
}