小知识记录

Published on

scrollView的自动偏移问题

[iOS7 , iOS11):

viewcontroller 参数 automaticallyAdjustsScrollViewInsets 决定其中scrollview偏移量参数 contentInset

默认情况下:automaticallyAdjustsScrollViewInsets = YEScontentInset = {64,0,0,0}tabbarcontroller 情况下为 {64,0,49,0},很容易理解假设设置 scorllview 的 frame 为屏幕大小,其可控高度为其高度减掉导航条的 64 和 tabbar 的 49。

导航条隐藏时,两种情况下 contentInset 分别是 {20,0,0,0},{20,0,49,0},20 是状态栏的高度

[iOS11,~):

viewcontroller 新增所谓安全区域,带来一系列参数:SafeAreaInsets、additionalSafeAreaInsets、adjustedContentInset、contentInsetAdjustmentBehavior……;

原来的 automaticallyAdjustsScrollViewInsets 失效了,新的方案认为状态栏导航条、tabbar 之间的区域为安全区域

scrollview 的最终偏移量 adjustedContentInset = safeAreaInset + contentInsetsafeAreaInset 的值与iOS 11 之前的 contentInset 相同,iOS11中 contentInset 默认为 {0,0,0,0}

导航条隐藏时,两种情况下 safeAreaInset 分别是 {20,0,0,0},{20,0,49,0},20 是状态栏的高度。

如果你想抵消这种偏移,添加 additionalSafeAreaInsets 参数。

if (@available(iOS 11.0, *)) {
        self.table.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } else {
        self.automaticallyAdjustsScrollViewInsets = NO;
    }

按钮取消各种状态对应的效果

如果给按钮设置一张图片,我们在按下的时候会有一个阴影的效果。

取消阴影:self.ghostModeButton.adjustsImageWhenHighlighted = NO;

如果我们设置按钮状态是disabled,按钮会变灰,设置不变灰self.ghostModeButton.adjustsImageWhenDisabled = NO;


经常搞混的坐标转换

UIView中的坐标转换

// 将像素point由point所在视图转换到目标视图view中,返回在目标视图view中的像素值
- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view;
// 将像素point从view中转换到当前视图中,返回在当前视图中的像素值
- (CGPoint)convertPoint:(CGPoint)point fromView:(UIView *)view;
// 将rect由rect所在视图转换到目标视图view中,返回在目标视图view中的rect
- (CGRect)convertRect:(CGRect)rect toView:(UIView *)view;
// 将rect从view中转换到当前视图中,返回在当前视图中的rect
- (CGRect)convertRect:(CGRect)rect fromView:(UIView *)view;

iOS系统框架层级

1723306-282bb6b02b3edce8.png

开发的时候优先考虑高层级,如果需要深度定制或者性能优化方面的特性时,再考虑使用低层级的框架和技术。

Cocoa Touch 层

Cocoa Touch Layer 包含了构建iOS App的关键框架. 这些框架定义了你的App显示的样式. 它们还提供了基本App的构造基础和对关键技术的支持, 如多线程, 基于触摸的输入, 推送通知, 和许多高层级的系统服务. 在计划App时, 你应该优先调查Cocoa Touch层中的技术看看是否满足你的需求.

0FCFA63C-6B10-4C20-9AAD-18F667DE9FBE.png

Media 层

Media Layer 包含了绘图(graphics), 音频(audio)和视频(video)等多媒体体验的技术实现。

9234E8D6-6318-4902-87D4-3DF6201AD24E.png

E400C3DA-9183-4F51-A942-7FAFD5F3E703.png

8C368C44-055F-49FB-831D-36118D48A403.png

Core Services层

Core Services Layer 包含了App的基本系统服务. 其中关键的框架是 Core Foundation 与 Foundation, 定义了所有App使用的基本数据类型. Core Services Layer还包含了支持定位, iCloud, 社交媒体和网络的特征对应的特殊技术.

995F45A7-A22E-43A2-B9DB-4BB07C210B09.png

Core OS 层

Core OS Layer 包含了大部分其它技术依赖的底层功能. 即使你没有在App中直接使用这些技术, 但他们很可能在其他框架中被使用. 在某些情况下, 当你需要直接处理数据加密或与外部硬件附件进行通讯时, 你确实需要使用该层的相关框架。

C382B1A9-A70F-4A25-8E08-20BE1B99DD91.png


@try @catch 处理异常

@try
{
    // 业务逻辑
}
@catch (异常类型名1 ex)
{
    //异常处理代码
}
@catch (异常类型名2 ex)
{
    //异常处理代码
}
// 可以捕捉 N 个 异常 ...
@finally
{
//回收资源
}

异常处理过程:

  1. 生成异常对象 : @try 中出现异常, 系统会生成一个异常对象, 该对象提交到系统中 系统就会抛出异常;
  2. 异常处理流程: 运行环境接收到 异常对象时, 如果存在能处理该异常对象的 @catch 代码块, 就将该异常对象交给 @catch 处理, 该过程就是捕获异常, 如果没有 @catch 代码块处理异常, 程序就会终止;
  3. @catch 代码块捕获过程: 运行环境接收到 异常对象 时, 会依次判断该异常对象类型是否是 @catch 代码块中异常或其子类实例, 如果匹配成功, 被匹配的 @catch 就会处理该异常, 都则就会跟下一个 @catch 代码块对比;
  4. @catch 处理异常 : 系统将异常对象传递给 @catch 形参, @catch 通过该形参获取异常对象详细信息;

不可见的字符

BY.赔钱货🎁收秒🌂并 这一串字符里面带有反字符,不可见,但是放在聊天软件里面发送的时候会变成BY.赔钱货🎁收秒🌂并。这是因为聊天软件对它进行了过滤。

原因在于 \\u0000202d \\u0000202c,这两个字符不属于unicode码,但是界面显示是正常的,但实实在在存在并且导致字符串长度不对。

如何去掉?

  1. 替换字符
[str stringByReplacingOccurrencesOfString:@"\\\\p{Cf}" withString:@"" options:NSRegularExpressionSearch range:NSMakeRange(0, str.length)];
  1. 过滤字符
NSCharacterSet *characterSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
    NSCharacterSet *specCharacterSet = [characterSet invertedSet];
    NSArray *strArr = [str componentsSeparatedByCharactersInSet:specCharacterSet];
    return [strArr componentsJoinedByString:@""];

iPhone的不同机型系统控件高度

systembar-height.png


限制带有emoji的字符长度

ObjC 中国 - NSString 与 Unicode

__block NSString *cutName = [NSString string];
    [name enumerateSubstringsInRange:NSMakeRange(0, [name length]) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString * _Nullable substring, NSRange substringRange, NSRange enclosingRange, BOOL * _Nonnull stop) {
        cutName = [cutName stringByAppendingString:substring];
        if ([cutName lengthOfBytesUsingEncoding:NSUTF32StringEncoding] / 4 == MaxLenth) {
            cutName = [cutName stringByAppendingString:@"…"];
            *stop = YES;
        }
    }];

mask的理解

mask是CALayer的一个属性,它本身也是一个CALayer类。我们使用该属性主要是为了让两个图层搭配出一个新的视觉效果,简单理解就是一个遮罩,mask图层区域外的任何区域不显示。

影响mask行为的因素

就是mask图层(是遮罩层,而不是被遮罩层哦~)的透明度,透明度的取值范围(0,1),而CALayer里有两个主要的属性和透明度有关,就是contents属性和backgroundCorlor属性。我们用contents最多的就是给它赋值一个图片,而图片是有透明通道和无透明通道的,backgroundColor属性也是有透明度的(mask不关心是什么颜色,只关心颜色的透明度),而且clearColor的透明度是0,可以去查看UIColor的API。

1. 当mask图层完全透明时,即透明度为0,则遮罩区域不显示

2. 当mask图层完全不透明时,即透明度为1,则遮罩区域显示

3. 当mask图层的透明度值在0~1之间,则mask图层会和被遮罩层内容混合


static inline 和 static 和 全局函数

表现为

// 内联函数  static inline
static inline UIEdgeInsets UIEdgeInsetRotateVertical(UIEdgeInsets insets) {
     UIEdgeInsets one;
     one.top = insets.left;
     one.left = insets.bottom;
     one.bottom = insets.right;
     one.right = insets.top;
     return one;
}

// 静态函数 static
static void YYTextGetRunsMaxMetric(CFArrayRef runs, CGFloat *xHeight, CGFloat *underlinePosition, CGFloat *lineThickness) { }

// 全局函数
CGFloat YYTextScreenScale() {
    static CGFloat scale;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        scale = [UIScreen mainScreen].scale;
    });
    return scale;
}

static inline,能够使函数一作为一个标准的内联函数,函数的代码被放入符号表中,在使用时直接进行替换。建议编译器在编译的时候做内联处理,不需要调用call指令,提升调用效率。

static,声明静态变量。

1)修饰局部变量的时候,让局部变量只初始化一次,局部变量在程序中只有一份内存,但是并不会改变局部变量的作用域,仅仅是改变了局部变量的生命周期(只到程序结束,这个局部变量才会销毁)。

2)修饰全局变量的时候,全局变量的作用域仅限于当前文件。

3)当修饰函数的时候,对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。

没有使用标识符,默认是extern的,可以被其它代码文件调用该函数。

为什么inline函数能取代宏?

1)#define定义的函数要有特别的格式要求,并不是每个人都能熟练使用,而使用`inline`则就行平常写函数那样。
2)和其他的宏定义一样,使用define宏定义的代码,编译器不会对其进行参数有效性检查,很容易出现无法察觉的错误,调试过程中会出现很多麻烦。
3)不仅是输入类型,#define宏定义的代码,返回值不能被强制转换成可转换的适合的转换类 。
4)#define是文本替换,需要在预编译时展开,内联函数是编译时候展开。

view是否显示在当前界面显示

// view是否显示在当前界面显示
- (BOOL)isShowInCurrentWindowWithView:(UIView *)view {
    if (!view) {
        return NO;
    }
    UIViewController *currentVC = [ULJumpUtil currentVisibleViewController];
    // 是否是当前VC
    if ([currentVC.view isEqual:view]) {
        return YES;
    }
    //是否是当前的子view
    return [self hasSubViewWithSubView:view inView:currentVC.view];
}

// 是否包含对应的子view
- (BOOL)hasSubViewWithSubView:(UIView*)subView inView:(UIView*)inView {
    if( !inView  ||  !inView.subviews.count ||  !subView) return NO;

    BOOL foundView = NO;
    for(UIView*view in inView.subviews) {
        if([view isEqual:subView]) {
            foundView = YES;
            break;
        }

        foundView = [self hasSubViewWithSubView:subView inView:view];
        //如果找到了,则终止循环递归,最终返回
        if (foundView) break;
    }
    return foundView;
}

Content Hugging Priority 以及 Content Compression Resistance Priority

Content Hugging Priority 指的是谁的内容优先级较高,view默认都是250,label和button默认是251,如果提高这个值,会优先根据内容进行约束,我们自己在xib中拉的约束默认优先级是1000,例如将label的优先级设置成600,那么这个label就会优先根据内容确定宽度。

Content Compression Resistance Priority 指的是压缩阻力优先级,优先级设置的越高,越难被压缩,如果两个label并排显示,屏幕宽度变窄,优先级设置的越高的越难被压缩,优先压缩优先级低的label。