您的当前位置:首页iOS小技巧

iOS小技巧

2024-12-13 来源:哗拓教育

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操作        

16、可变参数传递解析

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

//声明
- (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)
未完待续。。。

显示全文