iOS项目笔记

2017-01-11  本文已影响140人  字节码

1.根据服务端返回的json 字符串中的 \n 让label的文字换行

UIFont *ltextFont = [UIFont systemFontOfSize:14];
    NSString *str = @"活动时间:2016年12月23日--2017年1月6日\n作品要求:设计稿,形式体裁不限,作品尺寸为A4纸。将作品以图文的形式参与话题,文字注明城市与名字,自建话题无效。\n评选方式:平台用户以点赞方式投票\n活动奖励:前三名的作品除平台金币奖励外,还会获得超牛逼涂鸦书籍。";
    CGSize size = [str sizeWithFont: ltextFont constrainedToSize:CGSizeMake(CGRectGetWidth([UIScreen mainScreen].bounds), MAXFLOAT)lineBreakMode:NSLineBreakByWordWrapping];
    
    UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(0,200,size.width,size.height)];
    label.numberOfLines = 0; // 最关键的一句
    label.text = str;
    label.font = ltextFont;
    [self.view addSubview:label];

2.显示时间(几分钟前,几小时前,几天前)

+ (NSString *)compareCurrentTime:(NSString *)str {
    NSString *timeStr = @"2016-02-22 19:27:38";
    // 将时间字符串转换为NSDate
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyy-MM-dd HH:mm:ss"];
    NSDate *timeDate = [dateFormatter dateFromString:timeStr];
    
    // 获取当前时间
    NSDate *currentDate = [NSDate date];
    // 获取当前时间距离timeStr的时间差
    NSTimeInterval timeInterVal = [currentDate timeIntervalSinceDate:timeDate];
    
    long temp = 0;
    NSString *resultStr;
    
    if (timeInterVal / 60 < 1) {
        resultStr = @"刚刚";
        
    } else if ((temp = timeInterVal / 60) < 60) {
        resultStr = [NSString stringWithFormat:@"%ld分钟前", temp];
    } else if ((temp = temp / 60) < 24) {
        resultStr = [NSString stringWithFormat:@"%ld小时前", temp];
    } else if ((temp = temp / 24) < 30) {
        resultStr = [NSString stringWithFormat:@"%ld天前", temp];
    } else if ((temp = temp / 30) < 12) {
        resultStr = [NSString stringWithFormat:@"%ld月前", temp];
        
    } else {
        temp = temp / 12;
        resultStr = [NSString stringWithFormat:@"%ld年前", temp];
    }
    
    return resultStr;
}

3.中文斜体

经查阅资料-- iOS中不支持中文字体倾斜,只有设置倾斜角度

// 1.设置反射。倾斜15度
CGAffineTransform matrix =  CGAffineTransformMake(1, 0, tanf(15 * (CGFloat)M_PI / 180), 1, 0, 0);
// 2.取得系统字符并设置反射
UIFontDescriptor *desc = [ UIFontDescriptor fontDescriptorWithName:[UIFont systemFontOfSize :18].fontName matrix:matrix];
// 3.获取字体
UIFont *font = [UIFont fontWithDescriptor:desc size:18];
    self.rankingBtn.titleLabel.font = font;

 // 4.设置文字的字体,可以画出来
[string. text drawInRect :dr withFont :font lineBreakMode : NSLineBreakByTruncatingTail ];
// 也可以直接设置,比如
// [self.rankingBtn setTitle:viewModel.item.ranking forState:UIControlStateNormal];
   
    通过以上方法--绘制出斜体字。

4.iOS四种切除圆角的方法

// 创建一个view
  UIView *showView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
  [self.view addSubview:showView];
  showView.backgroundColor = [UIColor whiteColor];
  showView.alpha = 0.5;

   // 贝塞尔曲线(创建一个圆)
    UIBezierPath *path = [UIBezierPath     bezierPathWithArcCenter:CGPointMake(100 / 2.f, 100 / 2.f)
                                                        radius:100 / 2.f
                                                       startAngle:0 
                                                       endAngle:M_PI * 2
                                                      clockwise:YES];

      CAShapeLayer *layer = [CAShapeLayer layer];
      layer.frame = showView.bounds;
      layer.path = path.CGPath;
      [showView.layer addSublayer:layer];
- (UIImageView  *)avatarImage { 

     if (!_avatarImage) { 

        _avatarImage = [[UIImageView alloc] initWithFrame:CGRectMake(20,10, avatarDiameter, avatarDiameter)];
        _avatarImage.backgroundColor = [UIColor grayColor];
        _avatarImage.contentMode = UIViewContentModeScaleAspectFit;
        _avatarImage.layer.cornerRadius = avatarDiameter/2.0;
        _avatarImage.layer.masksToBounds = YES;
        [_avatarImage setImage:[UIImage imageNamed:@"test.jpg"]];
      }
    return _avatarImage;
}
- (instancetype)initWithFrame:(CGRect)frame {

    if (self = [super initWithFrame:frame]) {

    }
    return self;
}
// 自定义一个类继承自UIView,使用drawRect画圆
- (void)drawRect:(CGRect)rect { 
     // Drawing code 
    CGRect bounds = self.bounds;
    [[UIColor whiteColor] set];
    UIRectFill(bounds);

    [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:CGRectGetWidth(bounds)/2.0] addClip];
    [self.image drawInRect:bounds];
}
    // 头像控件
    self.headerView = [UIButton buttonWithType:UIButtonTypeCustom];
    self.headerView.backgroundColor = kColorGlobalCell;
    self.headerView.hidden = NO;
    self.headerView.tag = NSIntegerMax;
    self.headerView.clipsToBounds = YES;
    [self.contentView addSubview:self.headerView];
    
    // 镂空的圆形图片盖在头像上面,目的是让头像显示为圆形, 
    self.cornerImageView = [[UIImageView alloc] init];
    self.cornerImageView.center = self.headerView.center;
    self.cornerImageView.image = [UIImage imageNamed:@"corner_circle"];
    self.cornerImageView.tag = NSIntegerMax;
    [self.contentView addSubview:self.cornerImageView];
self.headerView.frame = self.viewModel.headerFrame;
    self.cornerImageView.frame = CGRectMake(0, 0, CGRectGetWidth(self.viewModel.headerFrame)+5, CGRectGetHeight(self.viewModel.headerFrame)+5);
    self.cornerImageView.center = self.headerView.center;

利用透明圆形的图片来进行遮盖,会引起blending,但性能仍然很高的

Blending:
在iOS的图形处理中,blending主要指的是混合像素颜色的计算。最直观的例子就是,我们把两个图层叠加在一起,如果第一个图层的透明的,则最终像素的颜色计算需要将第二个图层也考虑进来。这一过程即为Blending。
会导致blending的原因:
layer(UIView)的Alpha < 1
UIImgaeView的image含有Alpha channel(即使UIImageView的alpha是1,但只要image含透明通道,则仍会导致Blending)

为什么Blending会导致性能的损失?
原因是很直观的,如果一个图层是不透明的,则系统直接显示该图层的颜色即可。而如果图层是透明的,则会引入更多的计算,因为需要把下面的图层也包括进来,进行混合后颜色的计算。

下面是一个镂空的圆形图片


corner_circle@2x.png

5 将NSInteger类型的秒,转换为00:00:00格式

- (NSString *)coverTime:(NSInteger)secondCount {
    
    // 时
    NSString *tmphh = [NSString stringWithFormat:@"%ld",secondCount / 3600];
    NSLog(@"tmphh--%@", tmphh);
    if ([tmphh length] == 1){
        tmphh = [NSString stringWithFormat:@"0%@",tmphh];
    }
    
    // 分
    NSString *tmpmm = [NSString stringWithFormat:@"%ld",(secondCount / 60) % 60];
    if ([tmpmm length] == 1) {
        tmpmm = [NSString stringWithFormat:@"0%@",tmpmm];
    }
    
    // 秒
    NSString *tmpss = [NSString stringWithFormat:@"%ld",secondCount % 60];
    if ([tmpss length] == 1)
    {
        tmpss = [NSString stringWithFormat:@"0%@",tmpss];
    }
    
    //    NSLog(@"%@:%@", tmphh, tmpmm);
    return [NSString stringWithFormat:@"%@:%@:%@",tmphh,tmpmm,tmpss];
}

6 当需要在tableView中展示九宫格布局的情况:
比如

屏幕快照 2017-01-12 下午6.44.17.png

由于,此界面是由一个tableView构成的,现在要在原有的基础上添加添加一个子标题,点击后就是这种九宫格布局,为了在原因基础上搭建界面,最好在tableViewCell中嵌套一个collectionView,用collectionViewCell展示每一个九宫格数据,而且这样tableView直接把数据源给collectionView,方便其展示数据:

7 让创建出来的UILabel 上下左右有间隙

某些情况下,需要让UIlabel的上下左右有一定的间隙,有时候设置textAlignment并不能解决问题,比如文本已经设置了右对其了,但需求还是要让文本向左偏移一些,以下方法即可解决

解决方法:创建UILabel的子类使其能够让文字有上下左右的空隙,通过drawTextInRect方法实现

@implementation XYUserHomePageViewContentLabel {
    UIEdgeInsets _insets;
}

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        
        self.textColor = kColorNameTextBlack;
        self.font = kFontWithSize(15);
        self.textAlignment = NSTextAlignmentRight;
        self.backgroundColor = [UIColor whiteColor];
        //  根据情况设置UIEdgeInsets _insets属性
        //  让文本向左偏移15的距离
        _insets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 10.0f);

    }
    return self;
}

- (void)drawTextInRect:(CGRect)rect {
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, _insets)];
}

8 自定义导航条后,侧滑手势失效问题:
8.1 第一种情况:恢复系统默认的侧滑手势
第一步:自定义一个导航栏,并重写它的 viewDidLoad方法

系统默认的手势为interactivePopGestureRecognizer 当导航控制器响应的默认手势时,我们就重新设置手势的代理不再是系统的 UINavagtionController,而是我们自定义的这个导航控制器 , 再设置导航控制器的代理为它自己,因为一会还要监听它自己的代理方法

- (void)viewDidLoad {
    [super viewDidLoad];
    __weak typeof(self) weakSelf = self;
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.delegate = weakSelf;
        self.delegate = weakSelf;
    }
}

第二步 :重写导航控制器的pushViewController方法

重写 push 方法最主要的目的是让这个 pop 手势在 push 的时候变为不可用,因为可能在 push 过程中用户很快的进行了左滑动,那么就会造成 pop 栈中的混乱,导致画面卡顿,所以为了避免这个情况,我们必须要设置在 Push 过程中禁止这个手势的响应

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    
    if (self.childViewControllers.count > 0) {
        [viewController setHidesBottomBarWhenPushed:YES];
        
        // 自定义导航条左侧返回按钮
        UIButton *backBarButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [backBarButton setImage:[UIImage imageNamed:@"Login_backSel"].xy_originalMode forState:UIControlStateNormal];
        backBarButton.contentEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0);
        [backBarButton sizeToFit];
        viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backBarButton];
        
        // 如果自定义返回按钮后,
        if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
            self.interactivePopGestureRecognizer.enabled = NO;
        }
        
    }
    
    [super pushViewController:viewController animated:animated];
}

第三步 :监听导航控制器 是否显示完毕 的 代理方法

这个方法主要是监听导航栏是否显示完毕,当显示完毕的时候我们要进行一个判断,因为当控制器ViewController 数量为1的时候,用户如果进行了右滑,但问题是当前控制器数量只有一个,那个这个方法 POP 什么呢?根本就没法 POP 嘛,也会造成了 pop 栈中的混乱,导致画面一直卡顿,所以我们也要进行一个判断,判断控制器数量只有一个的时候,我们就禁止了这个手势,反之则开启

#pragma mark - UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    
    if (navigationController.childViewControllers.count == 1) {
        self.interactivePopGestureRecognizer.enabled = NO;
    } else {
        self.interactivePopGestureRecognizer.enabled = YES;
    }
}

当然,我们在自定义的导航条的左侧返回按钮时,监听返回的方法中,最好让返回按钮判断当前是 push 还是 modal显示的,根据当前情况进行 pop 和 disMiss

- (void)backBarButton {
    
    // 判断两种情况: push 和 present
    if ([self isPresent]) {
        [self dismissViewControllerAnimated:YES completion:^{
        }];
    }else {
        [[self navigationController] popViewControllerAnimated:YES];
    }
    
}

- (BOOL)isPresent {
    
    BOOL isPresent;
    
    NSArray *viewcontrollers = self.viewControllers;
    
    if (viewcontrollers.count > 1) {
        if ([viewcontrollers objectAtIndex:viewcontrollers.count - 1] == self) {
            
            isPresent = NO; //push方式
        }
    }
    else{
        isPresent = YES;  // modal方式
    }
    
    return isPresent;
}

8.2 第二种情况,全屏滑动返回

自定义导航控制器

- (void)viewDidLoad {
    [super viewDidLoad];
 
    id target = self.interactivePopGestureRecognizer.delegate;
    self.interactivePopGestureRecognizer.enabled = NO;
    // 全屏滑动返回手势
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
    
    [self.view addGestureRecognizer:pan];
    
    pan.delegate = self;
   
    self.interactivePopGestureRecognizer.enabled = NO;
}
- (void)pushViewController:(XYProfileBaseController *)viewController animated:(BOOL)animated {

    if ([viewController isKindOfClass:[XYProfileBaseController class]]) {
        if (viewController.isHiddenLeftButton) {
            
            viewController.hiddenLeftButton = self.childViewControllers.count < 1;
        }
        if (self.childViewControllers.count) {
            viewController.hidesBottomBarWhenPushed = YES;
            viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:viewController action:@selector(leftBtnClick:)];
        }
    }
    [super pushViewController:viewController animated:animated];
}

#pragma mark - <UIGestureRecognizerDelegate>
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {

    return self.viewControllers.count > 1;
}

9 项目中app账号与环信账号的管理 - 登录

第一种方法:只需要iOS与环信交互:用户在我们的app注册会员的时候,注册成功获得返回值,正确的返回值内去注册环信账号,而环信账号就是用户注册app的账号的uid,密码是写死的,例如wiox8YvKZd23bbIYw,任何用户注册时,传给环信的密码都是相同的,这样方便登录。如果环信返回给你是注册成功,此时需要在成功里创建NSUserDeafults存储你的环信账号,密码就不用存了,并且调用环信登录方法。每次登录时公司在环信的账号会返回access_token、user信息及expires_in。登录时,公司服务器也会返回一个登录信息的json,登录成功时其中就有imUser即时通讯账号,使用imUser登录环信用户账号密码即可。
如果不在AppDelegate里写登录,你的APP如果退出了,下次打开就没用了,所以必须时时刻刻登录。所以你需要在AppDelegate登录,账号就是你本地存储的账号。密码还是死的。如果正常流程来说就是:

1.注册自己APP账号-成功-注册环信账号,密码为死的-成功-登录环信账号。

2.登录自己APP账号-成功-登录环信

3.AppDelegate存储账号。

第二种方法:让iOS,服务器,环信交互:通过上面的那种方式,跟服务器唯一交互的就是密码,你需要在用户注册你自己产品的时候,密码也注册环信。注册成功,将密码post给服务器,登录的时候成功,服务器不光返回error,msg,还要加一个环信的password,这样你拿到password 可以再进行登录。当然注册的时候Phone和Password需都需要存储,方便在AppDelegate入口类登录。

1.注册自己APP账号-成功-注册环信账号,密码为注册APP的密码-成功-Post密码给服务器-成功-登录环信账号。
2.登录自己APP账号-成功-拿到服务器返回的Password,登录环信账号-成功。
3.AppDelegate存储账号和密码

// 在app启动后调用登录IM

- (void)loginImUserAccount {
    
    // 读取用户登录后存储的登录信息
    NSDictionary *loginInfo  = [NSDictionary dictionaryWithContentsOfFile:kLoginInfoPath];
    XYLoginInfoItem *item = [XYLoginInfoItem loginInfoItemWithDict:loginInfo];
    
    // EMOptions设置配置信息 AppKey:注册环信的AppKey。
    EMOptions *options = [EMOptions optionsWithAppkey:@"wuwo#ziwo"];
    //apnsCertName:推送证书名(不需要加后缀)
    options.apnsCertName = @"MeSelf_APNS_DistriBution";
    // 初始化SDK
    EMError *error = [[EMClient sharedClient] initializeSDKWithOptions:options];
    if (!error) {
        NSLog(@"初始化成功");
    }
    
    // 使用用户登录成功后,使用公司服务端返回imUser账号登录环信
    error = [[EMClient sharedClient] loginWithUsername:item.iyidamUser.username password:item.imUser.password];
    if (!error) {
        NSLog(@"登录成功");
    } else {
        NSLog(@"登录失败-%@", error.errorDescription);
    }

}

10 集成环信 3.2.3 相关问题
10.1 使用EaseUI 报错问题解决:

当把EaseUI拖入到项目后进行编译,后报很多错误: 比如连UIKit及NSString都找不到, 这是因为EaseUI中加入了.c文件,属于混编报错,等于把PCH中的代码都拷贝一份到.c文件中了,需要给导入到OC的文件加OBJC

解决方法:

  1. 在pch文件中在所有代码的头部加入#ifdef __OBJC__尾部加入 #endif
    如下:
#ifdef __OBJC__

// 中间放 之前所有的 代码

#endif```

2.环信内部集成的MBProgressHUD   SDWebImage MJRefresh 与我们工程中集成的这几个第三方库发生冲突!删掉工程中自己集成的这些第三方库,或者删除环信EaseUI 里面的这些第三方库! 
需要注意的是:如果删除的是环信集成的第三方库!由于环信在集成的第三方库中加了EM前缀! 记得删掉EaseUI 中使用方法的前缀,不然会报错!
[官方集成视频地址](http://www.imgeek.org/video/23)

10.2 聊天会话页面更换用户头像和昵称

![WechatIMG2.jpeg](https://img.haomeiwen.com/i2135374/9f4e0d13aa7145d0.jpeg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
经查看环信官网文档:
解决方法:在EaseMessageViewController或其子类方法中设置其数据源EaseMessageViewControllerDataSource,在以下数据源方法中 将EMMessage类型转换为符合<IMessageModel>协议的类型,设置用户信息,消息显示用户昵称和头像。

pragma mark - EaseMessageViewControllerDataSource

/*!
@method
@brief 将EMMessage类型转换为符合<IMessageModel>协议的类型
@discussion 将EMMessage类型转换为符合<IMessageModel>协议的类型,设置用户信息,消息显示用户昵称和头像
@param viewController 当前消息视图
@param EMMessage 聊天消息对象类型
@result 返回<IMessageModel>协议的类型
*/


10 集成友盟第三方登录
当第三方授权成功后,使用第三方平台获取的相关字段,再给服务器发送登录(一般需传入deviceToken、head、name、openPlatform、openPlatformId、os、osVersion、phoneModel、versionCode ),获取用户的loginInfo信息,服务器返回的code码为success为,就可以切换跟控制器了,再将loginInfo写入到本地保存,每次进入app后取出loginInfo进行判断用户是否登录,然后切换正确的跟控制器。

11 点赞、关注等接口的实现
当用户点赞时给服务器发送请求,传入相应的字段,当服务器响应成功时,根据code码判断是否点赞成功;当服务器返回的code为0时,点赞成功,此时应该修改对应的模型数据(比如把模型的isPraise修改为YES,点赞数据+1), 再调用reloadData刷新表格,在cell的模型的set方法中进行设置按钮的选中状态和按钮是否可以响应事件.

11 当界面需要按照服务端返回的前十个模型进行暂时排行榜单时,并且服务端并未返回排名
解决方法:首先给模型添加一个扩展属性ranking,用于作为榜单的id,在请求服务器数据完成时,开启子线程对数据进行处理,给前10个数据在模型中给ranking赋值,这里需要对数据源进行遍历,所以最好开启子线程处理,当处理到第10个数据时,停止遍历,并回到主线程中,刷新数据
代码如下:

/// 对模型进行处理 -- 为榜单前10个模型扩展排名属性


12 OC项目中怎么创建Swift文件及OC与Swift代码之间互相调用
首先在OC项目中创建一个类,Language选择为Swift,创建后会弹出一个桥接文件,点击创建即可,此时会产生一个```项目名-Bridging-Header.h```,此文件是为了swift代码中使用oc代码准备的桥接文件,如果swift想要使用oc的某个类,直接在这个桥接文件中导入oc头文件即可。
在OC需要使用到Swift类的类中导入```项目名-Swift.h```,然后就可以互相调用了,

13 Swift中获取String 字符串的长度
String结构体中没有Length的方法,但是可以通过Sting的扩展属性成员characters的count属性来获取

let title : String = "title"
let count = title.characters.count //获取String字符串的长度
print(count)


13 当导航控制器下的tableView的偏移量出现了问题时:
我设置了tableView的contentInset但是并未触发scrollViewDidScroll事件,虽然contentInset没有问题,但是导致初次进入界面时tableView的偏移量不对

self.automaticallyAdjustsScrollViewInsets = false
tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0)

所以手动设置tableView.contentOffset

tableView.setContentOffset(CGPoint.init(x: 0, y: -64), animated: true)


14 Swift中使用Masonry布局的代码格式:

headerLine.mas_makeConstraints { (make) in
make?.left.right().equalTo()(self)?.setOffset(0)
make?.bottom.equalTo()(self)?.setOffset(-margin)
make?.height.equalTo()(44)
}
figureSpecialView.mas_makeConstraints { (make) in
make?.top.left().right().equalTo()(self)?.setOffset(0)
make?.bottom.equalTo()(self.headerLine.mas_top)?.setOffset(-margin)

    }
上一篇下一篇

猜你喜欢

热点阅读