美文网首页
iOS小技巧

iOS小技巧

作者: 天蚕 | 来源:发表于2016-02-25 17:00 被阅读409次

    1、修改光标颜色

    UIView的属性tintColor用于修改光标颜色

    2、图片拉伸

    代码拉伸
    //旧版本
    @interface UIImage(UIImageDeprecated)
    // use resizableImageWithCapInsets: and capInsets.
    - (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight __TVOS_PROHIBITED;
    @property(nonatomic,readonly) NSInteger leftCapWidth __TVOS_PROHIBITED;   // default is 0. if non-zero, horiz. stretchable. right cap is calculated as width - leftCapWidth - 1
    @property(nonatomic,readonly) NSInteger topCapHeight __TVOS_PROHIBITED;   // default is 0. if non-zero, vert. stretchable. bottom cap is calculated as height - topCapWidth - 1
    
    @end
    

    leftCapWidth代表左侧需要保护的部位(自动计算左侧需要保护部分为width - leftCapWidth - 1)
    topCapHeight代表顶部需要保护的部位(自动计算左侧需要保护部分为height - topCapHeight - 1)
    保留中间的1个点进行拉伸

    //新版本
    - (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets NS_AVAILABLE_IOS(5_0); // create a resizable version of this image. the interior is tiled when drawn.
    - (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets resizingMode:(UIImageResizingMode)resizingMode NS_AVAILABLE_IOS(6_0); // the interior is resized according to the resizingMode
    

    capInsets代表上左下右四个方向需要保护的区域
    拉伸区域可以根据需求定制

    新特性(Assets)自动拉伸

    (利用新特性拉伸图片,编译器会先自动确定一个拉伸区域和保护区域;如果图片是对称的,那么系统默认拉伸图片中心的一个点;如果不对称,系统默认会自动保护不对称区域,在此过程中还会避让圆角不被拉伸,总之,相当智能)
    //1、在Assets.xcassets中选择要拉伸的图片 //2、选择窗口右上角图片属性 //3、在slicing->slices选择Horizontal and Vertical选项,表示水平和垂直方向都拉伸即可 //4、新特性会自动计算拉伸区域和需要保护的区域,也可以手动编辑

    新特性拉伸操作方法1
    ![Uploading Snip20160225_6_823671.png . . .]
    新特性拉伸操作方法2
    Snip20160225_6.png

    3、修改背景alph值而不影响子控件的alph,子控件不继承父控件的alph值

    有时候我们需要修改控件的背景透明度,但是子控件默认
    回继承父控件的透明度属性,此时可以这样干
    self.backgroundColor = [[UIColor clearColor] colorWithAlphaComponent:0.1];
    

    4、解决导航栏的标题不居中

    //当导航栏的标题不居中,在上一页消失的时候将标题设为nil即可
    

    5、坐标系转换

    convertRectconvertPoint作为UIView转换坐标系的方法,是将view所在坐标系内(即以view的左上角为坐标原点(0,0))一块区域或者一个点的坐标转换到目标坐标系内,这里的view区域以及点是存在父子关系的。只能将父控件所在坐标系(即以父控件的左上角为坐标原点(0,0))内部的区域或者点转换坐标系,否则会出现意想不到的错误。frame描述的是控件在其父控件坐标系内的位置和尺寸,同一个坐标系内的位置比较才是有意义的。

    6、扩大UIButton的点击区域

    要扩大UIButton的点击事件响应范围,只需要重写UIButton的hitTest方法

    //将点击事件响应范围扩大到周边20个点
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
        CGRect rect = self.frame;
    
        //新的响应事件的区域
        //这里的区域是在父控件坐标系内的
        rect.origin.x -= 20;
        rect.origin.y -= 20;
        rect.size.height += 20 * 2;
        rect.size.width += 20 * 2;
    
        //将点转换到与响应事件的区域在同一个坐标系内
        CGPoint p = [self convertPoint:point toView:self.superview];
        
        //判断点是否在新的事件响应区域内
        if (CGRectContainsPoint(rect, p))
        {
            return self;
        }
        else
        {
            return [super hitTest:point withEvent:event];
        }
    }
    

    6、HitTest

    1)将当前当前坐标系上的点转换到指定坐标系上的点
    2)判断点不在目标坐标系上
    3)如果点在目标坐标系上,且目标坐标系内存在多个控件,需要调用坐标的HitTest找到最合适的控件;如果找不到这个更合适的View(或者没有子控件,因为只有自己)就返回自己,作为最终的事件接收者

    
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    
    {
        //1、当按钮超出父控件的范围,使其可以响应事件
        //1.1 将父控件相应点,转换到按钮内部坐标系
        CGPoint cartButtonPoint = [self convertPoint:point toView:self.cartButton];
        
        //1.2 判断转换后的点是否在按钮上
        if ([self.cartButton pointInside:cartButtonPoint withEvent:event])
        {
            //1.3 转换后的点在按钮上,且按钮没有子控件可以遍历,返回按钮本身
            return self.cartButton;
        }
        
        //2、当UITableView超出父控件的范围,使其可以响应事件
        //2.1 将父控件相应点,转换到UITableView内部坐标系
        CGPoint goodsListViewPoint = [self convertPoint:point toView:self.goodsListView];
    
        //2.2 判断转换后的点是否在UITableView上
        if ([self.goodsListView pointInside:goodsListViewPoint withEvent:event])
        {
             //2.3 转换后的点在UITableView上,且存在子控件,所以调用HitTest方法查找最合适的View响应事件
            UIView *subView = [self.goodsListView hitTest:goodsListViewPoint withEvent:event];
             //2.3 如果找到更合适的View返回即可,如果没有返回UITableView自响应事件
            return subView ? subView : self.goodsListView;
        }
    
       //3、 其余没有特殊处理的子控件调用父类的HitTest方法找到最合适的View来响应事件
        return [super hitTest:point withEvent:event];
    }
    

    6、在使用AFnetwotking的时候,报错提示一堆的二进制流数据并且以“text/html”结束,问题基本就在于你们服务端返回的二进制流的解析格式(text/html)不是常用的json,xml等,需要手动修改afnetworking源码,在框架内查找“text/json”字符串并参照添加不能解析的格式串“text/html”在后面,即可,记得要全部添加

    7、在使用iOS原生扫码的时候奔溃在self.output.metadataObjectTypes(设置扫码类型),通常是权限为题。

    8、设置标题栏

    如果要同时改变导航栏标题和UITabBar对应的标题,使用self.title
    如果单独改变导航栏标题建议使用self.navigationItem.title
    如果导航控制器和UITabBarController都自定义了,建议使用self.navigationItem.title
    最后,建议尽量使用self.navigationItem.title设置标题。在没有特殊需要,我们尽量不需要迁一发动全身的代码,也是遵循低耦合的编码规范

    9、UICollectionView cellForItematIndexpath返回为nil

    在reloadData后调用 layoutIfNeeded

    [collectionView reloadData];
    [collectionView layoutIfNeeded];
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        
        
        if (scrollView.contentOffset.y > 200 - 64) {
            CGFloat x = scrollView.contentOffset.x;
            scrollView.contentOffset = CGPointMake(x, 200 - 64);
            self.v.alpha = 1.0;
        }
        
        else if ((scrollView.contentOffset.y < 200 - 64))
        {
            if (scrollView.contentOffset.y > 0)
            {
                
                CGFloat alpha = (1.0 / (200 - 64)) * scrollView.contentOffset.y;
                
                NSLog(@"%f==%f",alpha,scrollView.contentOffset.y);
                
                self.v.alpha = alpha;
            }
            else
            {
                self.v.alpha = 0;
            }
            
        }
    }
    

    10、字典setValuevalue值为空,导致奔溃

    给NSMutableDictionary添加一个分类实现setValue: forUndefinedKey:方法

    #import "NSMutableDictionary+ValueForNil.h"
    
    @implementation NSMutableDictionary (ValueForNil)
    
    - (void)setValue:(id)value forUndefinedKey:(NSString *)key
    {
        
    }
    
    @end
    

    11、@dynamic和@synthesize

    1、@dynamic 属性名称;//表示属性的setter和getter方法将会动态添加,不用手动实现
    2、@synthesize 属性名称;//表示属性的setter和getter方法由编译器合成,(使用@property关键字的默认实现,不用显示的告知编译器)
    3、在category中使用@property关键字编译器是不能自动合成setter和getter方法的,而且编译器不允许显示的使用@synthesize来合成setter和getter方法,因为category中的@property关键字并不生成默认的下划线成员变量,没有办法合成。
    4、在category添加的属性是动态添加的,setter和getter方法页需要我们动态添加可以直接实现setXXX:和xxx方法,也可以使用class_addMethod来动态添加(推荐使用,可以减少不必要的重复代码,使用此方法,通常会配合dynamic关键字,以消除警告)

    动态添加属性.png

    12、swift的xib在iOS8系统下的使用

    在iOS8系统下使用xib的时候要格外小心,通常我们无论控制器是xib
    还是纯代码只需要使通用的()创建控制器,但是iOS颠覆了这一切,如
    果控制器使用xib,必须使用xxx(nibName: "", bundle: nil),否则xib
    中的所有控件是不会创建,初始化的,如果过此时你将xib中的控件连
    线到控制器的class中(默认的xib的连线属性都是!强解包的),就会报option变量强解包失败而奔溃
    

    13、iOS和JSON串转换

    无论OC还是swift不要使用NSJSONWritingPrettyPrinted会带有回车,服务器无法解析
    //OC版
    NSArray *params = @[@{@"name":@"小史",@"age":@"80"},
    @{@"name":@"小史",@"age":@"80"},
    @{@"name":@"小史",@"age":@"80"}];
    
    NSData *data = [NSJSONSerialization dataWithJSONObject:params options:0 error:nil];
        
    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    NSLog(@"%@",string);
    
    //Swift版
    let data = try? JSONSerialization.data(withJSONObject: params!, options: [])
    jsonString = String.init(data: data!, encoding: String.Encoding.utf8) ?? jsonString
    print(jsonString)
    

    14、线程保命使用Runloop而不是strong强引用

    线程保命不能使用strong强引用,线程一旦task结束就会自动回收,指向线程的指针就会成为野指针,如果使用强引用指针去保命,在线程结束后去用强引用的指针去使用线程,直接野指针crash,线程保命应该使用Runloop,可以参见AFNetworking中的异步实现,如果给线程保命,那么AFNetworking的异步回调将不可能回调,因为在一步回调之前,线程已经结束,线程结束线程中所有的task将不会再被执行

    15、关闭隐式动画

    对非Root Layer的部分属性进行修改时,默认会自动产生一些动画效果,而这些属性称为Animatable

    CATransaction.begin()
    CATransaction.setDisableActions(true)
    //图层CALayer操作        
    CATransaction.commit()
    

    16、可变参数传递解析

    1、可变参数传递使用...,注意在方法声明后添加宏NS_REQUIRES_NIL_TERMINATION(数组的创建等等就会用到),表示方法调用已nil结束,因为可变参数的接收会用到C语言结构的遍历,已nil结束帮助遍历结束
    2、安全考虑指针操作,va_start(不包含启始元素)va_end成对出现
    3、可变参数接收的数据结构是一个栈,va_arg指针将移动到下一个参数,上一个参数出栈

    参考文档:
    blog.csdn.net/grozy_sun/article/details/27634575?utm_source=tuicool

    //声明
    - (void)test:(id)arg1,...NS_REQUIRES_NIL_TERMINATION;
    //实现
    - (void)test:(id)arg1,...
    {
        va_list args;
        va_start(args, arg1);
        
        if (arg1)
        {
            
            id otherArg = nil;
            NSLog(@"%@",arg1);
            
            while (1)
            {
                otherArg = va_arg(args, id);
                
                if (otherArg == nil)
                {
                    break;
                }
                
                NSLog(@"%@",otherArg);
            }
            va_end(args);
        }
    }
    

    17、隐藏导航栏,但是保留侧滑

    //保留侧滑
    self.navigationController.navigationBar.hidden = YES;
    //不保留侧滑
    self.navigationController.navigationBarHidden = YES;
    

    18、使用快速构建数组的方法构建数组,必须确保数组的元素非空,非直接创建的数组最好不要使用快速构建的方法

    19、微信授权

    微信授权在sdk中有直接的接口,其实就是授权登录,授权的之前必须

    20、pathforresource返回路径为nil

    json,等一些资源文件需要在Build Phaeses->Copy Bundle Resource进行手动添加

    21、UIImage旋转显示

    self.idCardFrontImageView.image = [UIImage 
    imageWithCGImage:image.CGImage scale:1.0 
    orientation:UIImageOrientationLeft];
    

    22、%和&

    当集合容量是2的n次幂的时候,计算元素在集合的位置的时候
    集合编号 % 集合容量 <=> 集合编号 & (集合容量 - 1)
    未完待续。。。

    相关文章

      网友评论

          本文标题:iOS小技巧

          本文链接:https://www.haomeiwen.com/subject/erbikttx.html