阅读视图

发现新文章,点击刷新页面。

iOS小技能:UITableView的适配 (iOS10/iOS14/iOS16.0)

本文正在参加「金石计划」

引言

如果按照开发规范写代码,不会存在关于UITableView的适配问题。

  • 如果按照规范使用UITableViewHeaderFooterView,就不会存在iOS16横竖屏切换场景的页脚问题。
  • 如果按照规范添加UITableViewCell的子试图到UITableViewCellContentView,就不会存在iOS14被遮挡的问题。
  • 如果将tableView:viewForFooterInSection: 和tableView:heightForFooterInSection:方法都注释掉,就不会存在iOS10即使heightForFooterInSection高度返回0 也会显示视图的问题。

I iOS16.0 横竖屏切换适配

调用完转屏方法以后,view需要重新更新frame

1.1 获取当前屏幕横竖屏状态

  • (UIInterfaceOrientation)interfaceOrientation {// 获取当前屏幕横竖屏状态 if (@available(iOS 13.0, *)) { return [UIApplication sharedApplication].windows.firstObject.windowScene.interfaceOrientation; } return [[UIApplication sharedApplication] statusBarOrientation]; }

1.2 iOS16.0调完转屏方法后,需要重新更新view的frame

问题:从电子签名界面回来之后,页脚的frame不正确

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
        UIButton *nextBtn = [ControlManager getButtonWithFrame:CGRectMake(KWratio(30), KWratio(25), kWidth - KWratio(60), KWratio(45)) fontSize:displayFontSize(15.0f) title:@"提交" titleColor:[UIColor whiteColor] image:[UIImage new] backgroundColor:BASE_RED_COLOR cornerRadius:KWratio(5)];
return xxx;
}

原因:iOS16.0调用完转屏方法以后,如果不是使用自动布局,view需要重新更新frame。

解决方法:自定义页脚和页眉(UITableViewHeaderFooterView代替 titleForHeaderInSection)

1.3 自定义页脚

UITableViewHeaderFooterView代替 titleForHeaderInSection

blog.csdn.net/z929118967/…

  1. 注册FooterView
        [_tableView registerClass:[CRMNextBtnHeaderFooterView class] forHeaderFooterViewReuseIdentifier:@"CRMNextBtnHeaderFooterView"];
  1. 返回FooterView
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    CRMNextBtnHeaderFooterView *footerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"CRMNextBtnHeaderFooterView"];

//    footerView.models = self.viewModel.passwordRuleDescModel;

    return footerView;
}
  1. 实现FooterView

@interface CRMNextBtnHeaderFooterView : UITableViewHeaderFooterView

@property (nonatomic , weak) UIButton *nextBtn;

@property (nonatomic , weak) UIButton *cancelBtn;


@end

II 解决UITableViewCell兼容问题(iOS14适配)

kunnan.blog.csdn.net/article/det…

2.1 问题分析

iOS14 UITableViewCell的子试图不能点击或者滑动等手势响应问题,发现有问题的cell基本都是直接添加到Cell的,通过Xcode自带的DebugViewHierarchy视图分析发现问题的原因是:被系统自带的UITableViewCellContentView遮挡在底部了

2.2 解决方案

需要改规范的做法

cell.contentView.addSubView(tempView1)

全局修改适配

//
//  UITableViewCell+CRMaddSubView.m
//  Housekeeper
//
//  Created by mac on 2020/9/18.
//  Copyright © 2020 QCT. All rights reserved.
//

#import "UITableViewCell+CRMaddSubView.h"

@implementation UITableViewCell (CRMaddSubView)
+ (void)load {
    // Swizzle addSubView
    [UITableViewCell sensorsdata_swizzleMethod:@selector(addSubview:) withMethod:@selector(kunnan_addSubview:)];
    
}

- (void)kunnan_addSubview:(UIView *)view {

    
    
    if  ([view isKindOfClass:NSClassFromString(@"UITableViewCellContentView")]) {//允许 addSubView UITableViewCellContentView 
        
        [self kunnan_addSubview:view];//实现方法,因为已经进行了 swizzle,相当于调用原来的方法
        

        
    } else {//
        
        [self.contentView addSubview:view];
        
    }



}



@end

III iOS10 系统关于UITableView的适配问题

因为目前基本找不到iOS10的真机了,要测试iOS10 可以下载模拟器。

  1. tableView numberOfRowsInSection:QCTReceiptsubFilterViewSection4KeyTypeTitle] 的使用执行顺序在iOS10的很特殊,不能在在方法中 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section调用,否则会造成死循环。
  2. heightForFooterInSection 在iOS10 即使高度返回0 也会显示视图

3.1 代理方法的执行顺序

tableView:numberOfRowsInSection 不能在方法中 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ 调用,否则会造成死循环。

3.2 尾部视图的展示

在iOS10 中不显示viewForFooterInSection的正确做法是,将tableView:viewForFooterInSection: 和tableView:heightForFooterInSection:方法都注释掉


//- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
//    
//    return 0;
//    
//    return kAdjustRatio(92+10);
//}
//
//- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
//     *footerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@""];
//    
//    footerView.type =  self.type;
//    footerView.models = self.viewModel.datas[section];
//    return footerView;
//}

iOS小技能:短信验证码的Checklist、格式校验、获取验证码处理流程(限制60s)

本文正在参加「金石计划」

前言

短信验证码处理的实际应用:注册登陆界面获取验证码功能

  1. 获取验证码(限制60s):获取成功之后验证码输入框得到焦点,并开始计时器
  2. 格式校验
  3. 短信验证码的Checklist

I、实际应用的例子: 获取验证码(限制60s)

1.1 点击获取验证码处理

请求接口获取验证码,获取成功之后,开始到60S倒计时

  • 监听点击事件,并处理获取成功之后的倒计时
        UITapGestureRecognizer *cutTap = [[UITapGestureRecognizer alloc] init];
        [[cutTap rac_gestureSignal] subscribeNext:^(id x) {
            
            
            NSLog(@"获取验证码");
            
            
            if(weakSelf.model.rightBtnblock){
                
                // 参数为block
                
                
                weakSelf.model.rightBtnblock(
                                              
                                              
                                              ^(id  _Nonnull sender) {
                                
                //                                  []
                                                  [self.textF becomeFirstResponder];
                                                  [self startCountdown];//test code

                                                  
                            }// 参数
                                           );
                
            }
            
            
            
            
            
        }];
        [tmpView addGestureRecognizer:cutTap];
        

  • 请求短信验证码:SendValidateCode

 
/** 本方法的参数类型时block,是获取完短信验证码的回调
 ,回调主要是执行60S倒计时
 
 */
- (void)SendValidateCode:(void (^)(id sender))tmp {

        [QCTNetworkHelper Post:post parameters:params success:^(id  _Nonnull responseObj) {
            
            
            if(tmp){// 开始计时器
                tmp(nil);
            }
//
            
            
            
        } failure:nil bizFailure:nil isShowLoadingDataGif:YES];
        

1.2、计时方法处理

  • 去掉输入框的内容,短信验证码获取成功调用这个方法


#pragma mark - 计时方法, 去掉输入框的内容,短信验证码获取成功调用这个方法
//开始倒计时
- (void)startCountdown {
//
//    //1\ 清空文本输入框
    [self updateText:nil]; //
//
    _textF.userInteractionEnabled = YES;
    
    self.countdownTime = 60;
    
    self.rightBtn.enabled = NO;
    
    
    [self.rightBtn setTitle:[NSString stringWithFormat:@"%dS",self.countdownTime] forState:UIControlStateDisabled];
    if (_timer) {
        [_timer invalidate];
        _timer = nil;
    }
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}


//
//结束倒计时
- (void)stopCountdown {
    if (_timer) {
        [_timer invalidate];
        _timer = nil;
    }
    [self.rightBtn setTitle:@"重新发送" forState:UIControlStateNormal];
//    //    self.renewBtn.userInteractionEnabled = YES;
    self.rightBtn.enabled = YES;
    
    self.rightBtn.layer.borderColor = RGB(255,83,85).CGColor;
    [self.rightBtn setTitleColor:RGB(255,83,85) forState:UIControlStateNormal];
    
}



//
- (void)countdownAction:(NSTimer *)timer {
    self.countdownTime --;
    if (self.countdownTime < 0) {
        self.countdownTime = 0;
        [self stopCountdown];
    } else {
        [self.rightBtn setTitle:[NSString stringWithFormat:@"%dS",self.countdownTime] forState:UIControlStateDisabled];
    }
}
//
//
//
//
//
- (NSTimer *)timer {
    if (_timer == nil) {
        _timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(countdownAction:) userInfo:nil repeats:YES];
    }
    return _timer;
}


II 格式正则校验

2.1 短信验证码格式正则校验

+ (BOOL)isSMSshouldChangeCharactersInRange:(NSString*)str{
    //匹配以0开头的数字
    
    //^\d{6}$"
    
//    NSPredicate * predicate0 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",@"^[0][0-9]+$"];
    
    
    //
    NSPredicate * predicate1 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",@"^[0-9]{0,6}$"];
    
    
    
    
    return  [predicate1 evaluateWithObject:str] ? YES : NO;
    
    
}


2.2 金额校验格式正则校验

需求背景:商户进件新增、编辑、变更;费率设置支持到小数点后3位,如微信支付宝费率0.385;d0费率0.015

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
    NSInteger len = range.length > 0?([textField.text length] - range.length): ([textField.text length] + [string length]);
    
    if(len > 20){// 限制20位
        return false;
    }
    
    NSMutableString * futureString = [NSMutableString stringWithString:textField.text];
    if(range.length > 0){//允许删除
        [futureString replaceCharactersInRange:range withString:string];
    }else if(range.length<=0){
        [futureString  insertString:string atIndex:range.location];
    }
    if(futureString.length<1){//允许删除全部
        return YES;
    }
    if(jjkFdTextF == textField){// 支持2位小数
        return [QCT_Common isAmoutshouldChangeCharactersInRange:futureString.copy ];
    }
    //支持三位小数
    return [QCT_Common isAmoutshouldChangeCharactersInRange3:futureString.copy];
    
}

支持三位小数

/**
 前端商户费率填写支持三位小数
 */
+ (BOOL)isAmoutshouldChangeCharactersInRange3:(NSString*)str{
    //匹配以0开头的数字
    NSPredicate * predicate0 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",@"^[0][0-9]+$"];
    //匹配两位小数、整数
    NSPredicate * predicate1 = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",@"^(([1-9]{1}[0-9]*|[0])\.?[0-9]{0,3})$"];
    return ![predicate0 evaluateWithObject:str] && [predicate1 evaluateWithObject:str] ? YES : NO;
    
}

III、短信验证码的Checklist

  • 一次一用
  • 发送频率控制(建议60s获取一次)
  • 验证码有效期(建议60s内有效,发短信时进行友好提示)
  • 杂度(短信验证码建议6位数字)
  • 安全提示:是否是个人自己操作等风险提示信息
  • 在前端校验(客户端的校验只能作为辅助手段,很容易被绕过),必须使用服务端代码对输入数据进行最终校验
  • 短信验证码需要限制频率使用

例如:每天一个手机号码只允许发送5次,防止被黑客恶意消耗短信

  • 不同场景的短信验证码不可通用
  • 单个短信验证码限制有效验证次数
  • 验证码需要对应手机号不可通用

iOS小技能:【设备日志查看工具】syslog、deviceconsole和socat

本文正在参加「金石计划」

前言

本文介绍iOS设备日志查看工具syslog、deviceconsole和socat,如果上述工具都不满意,你也可以使用Mac系统自带的console控制台进行查看。

>

I syslog

1.1 安装syslog

在cydia搜索syslogd to /var/log/syslog安装即可

1.2 syslog用法

syslog是把系统日志写入到/var/log/syslog文件里,用法很简单,执行tail -f /var/log/syslog就能看到了

如果需要过滤某一应用的日志,只需加上grep即可,比如过滤微信

tail -f /var/log/syslog |grep WeChat

II socat

2.1 安装

  • 在iOS设备安装

使用 APT 0.6 Transitional 安装socat 几乎所有流行的黑客工具都可以在 BigBoss Recommendation tools这个包中找到 ( APT 0.6 Transitional, Git, GNU Debugger, less, make, unzip, wget 和 SQLite 3.x)

apt-get install socat

如果找不到安装包的时候,运行一下 apt-get update, 获得最新的包列表.

2.2 连接到系统日志的sock文件

socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock

  • 进入到命令行交互界面,这时可以输入help查看帮助
iPhone:~ root# socat - UNIX-CONNECT:/var/run/lockdown/syslog.sock

========================
ASL is here to serve you
> 

2.3 日志的查看

输入watch查看,输入stop停止

2.4 清除日志文件数据

cat /dev/null >/var/log/syslog

III deviceconsole

自从iOS8之后,我就习惯使用Mac系统自带的console ,后来发现有些同事的Mac中console 版本低,没有device 选项;于是乎,就推荐他们使用deviceconsole

  • deviceconsole --help
➜  bin git:(master) ✗ deviceconsole --help
Usage: deviceconsole [options]
Options:
 -d      Include connect/disconnect messages in standard out
 -u <udid>    Show only logs from a specific device
 -p <process name>  Show only logs from a specific process

Control-C to disconnect
Mail bug reports and suggestions to <ryan.petrich@medialets.com>
  • knlog -help

    Usage: knlog [options]
    Options:
    -i | --case-insensitive     Make filters case-insensitive
    -f | --filter <string>      Filter include by single word occurrences (case-sensitive)
    -x | --exclude <string>     Filter exclude by single word occurrences (case-sensitive)
    -p | --process <string>     Filter by process name (case-sensitive)
    -u | --udid <udid>          Show only logs from a specific device
    -s | --simulator <version>  Show logs from iOS Simulator
         --debug                Include connect/disconnect messages in standard out
         --use-separators       Skip a line between each line
         --force-color          Force colored text
         --message-only          Display only level and message
    Control-C to disconnect
    
  • 编译之后的可执行文件 knlog

IV shell 脚本完成对日志文件的提取之sed 学习

sed 是一种在线编辑器,它一次处理一行内容。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。

文件内容并没有改变,除非你使用重定向存储输出。

4.1 ## sed的参数

    sed [-nefr] [动作]
选项与参数:
-n :使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到终端上。但如果加上 -n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e :直接在命令列模式上进行 sed 的动作编辑;
-f :直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的 sed 动作;
-r :sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)
-i :直接修改读取的文件内容,而不是输出到终端。

动作说明: [n1[,n2]]function
n1, n2 :不见得会存在,一般代表『选择进行动作的行数』,举例来说,如果我的动作是需要在 10 到 20 行之间进行的,则『 10,20[动作行为] 』

function:
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
d :删除,因为是删除啊,所以 d 后面通常不接任何咚咚;
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
p :列印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~
s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 
————————————————
版权声明:本文为CSDN博主「iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/z929118967/article/details/49489527

4.2 数据的搜寻并替换

`sed 's/要被取代的字串/新的字串/g'`

    /etc>ifconfig -a | grep inet 
        inet 172.16.49.224 netmask 0xffffff00 broadcast 172.16.49.255
        inet 192.168.10.4 netmask 0xffffff00 broadcast 192.168.10.255
        inet 127.0.0.1 netmask 0xff000000 broadcast 127.255.255.255
        inet6 ::1%1/0
————————————————
版权声明:本文为CSDN博主「iOS逆向」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/z929118967/article/details/49489527

see also

更多服务和咨询请关注#公号:iOS逆向

iOS小技能:核心动画(CoreAnimation)

本文正在参加「金石计划」

引言

预备知识1: RootLayer

在创建一个UIView对象时,UIView内部会自动创建一个图层(即CALayer对象);通过UIView对象的layer属性可以访问这个层,通常称这个层为RootLayer。 iOS小技能:CALayerblog.csdn.net/z929118967/…

预备知识2. 关于CALayer(可移植性) QuartzCore框架和CoreGraphics框架是可以跨平台使用的,在iOS和Mac OS X上都能使用,但是UIKit只能在iOS中使用。所以: 为了保证可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef

  • CALayer是定义在QuartzCore框架中的(CoreAnimation)

  • CGImageRef、CGColorRef两种数据类型是定义在CoreGraphics框架中的

  • UIColor、UIImage是定义在UIKit框架中的

本文核心内容:

  • CALayer的position、anchorPoint属性的作用
  • 核心动画基本概念
  • 基本动画
  • 关键帧动画
  • 动画组
  • 转场动画

I 隐式动画

所有的非Root Layer,也就是手动创建的CALayer对象,都存在着隐式动画。 隐式动画:当对非Root Layer的部分属性进行修改时,默认会自动产生一些动画效果;而这些属性称为可动画属性(Animatable Properties)。 几个常见的Animatable Properties:

  1. bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画
  2. backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变动画
  3. position:用于设置CALayer的位置。修改这个属性会产生平移动画效果

关闭隐式动画(使用CATransation关闭隐式动画): 可以通过动画事务(CATransaction)关闭默认的隐式动画效果


[CATransaction begin];
[CATransaction setDisableActions:YES];
self.myview.layer.position = CGPointMake(10, 10);
[CATransaction commit];

II 核心动画

核心动画的执行过程都是在后台操作的,不会阻塞主进程;核心动画直接作用于CALayer,而非UIView

2.1 是所有动画对象的父类

负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类

CAAnimation的继承结构

2.2 属性

duration:动画的持续时间 repeatCount:重复次数,无限循环可以设置HUGE_VALF或者MAXFLOAT repeatDuration:重复时间 nremovedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards fillMode:决定当前对象在非active时间段的行为。比如动画开始之前或者动画结束之后 beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间 timingFunction:速度控制函数,控制动画运行的节奏 delegate:动画代理

(粗体代表来自CAMediaTiming协议的属性)

2.3 动画填充模式(fillMode )

填充模式的枚举值

要想fillMode有效,最好设置removedOnCompletion = NO



CA_EXTERN NSString * const kCAFillModeForwards//当动画结束后,layer会一直保持着动画最后的状态
CA_EXTERN NSString * const kCAFillModeBackwards//在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始。
CA_EXTERN NSString * const kCAFillModeBoth//上面两个的合成,动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态
CA_EXTERN NSString * const kCAFillModeRemoved //这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态

速度控制函数(CAMediaTimingFunction)


1.kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉
2.kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加速离开
3.kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地
4.kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。这个是默认的动画行为。

动画的代理方法

@interface NSObject (CAAnimationDelegate)
/* Called when the animation begins its active duration. */
- (void)animationDidStart:(CAAnimation *)anim;
/* Called when the animation either completes its active duration or
 
 * is removed from the object it is attached to (i.e. the layer). 'flag'
 
 * is true if the animation reached the end of its active duration
 
 * without being removed. */
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
@end

III 开发步骤

  1. 获取到CALayer(CoreAnimation是直接作用在CALayer上的,并非UIView。)
  2. 初始化CAAnimation对象,并设置一些动画相关属性
  3. 通过调用CALayer的addAnimation: forKey: 方法来添加CAAnimation到CALayer中,这样就能开始执行动画了。
  4. 通过调用CALayer的removeAnimationforKey: 方法来停止CALayer的动画。

CALayer上动画的暂停和恢复



#pragma mark 暂停CALayer的动画
-(void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime =
[layer convertTime:CACurrentMediaTime() fromLayer:nil];
//让CALayer的时间停止走动
  layer.speed = 0.0;
//让CALayer的时间停留在pausedTime这个时刻
layer.timeOffset = pausedTime;
 
}
 
 
 
 
 
 
 
#pragma mark 恢复CALayer的动画
 
 
-(void)resumeLayer:(CALayer*)layer{
CFTimeInterval pausedTime = layer.timeOffset;
//1. 让CALayer的时间继续行走
  layer.speed = 1.0;
//2. 取消上次记录的停留时刻
  layer.timeOffset = 0.0;
//3. 取消上次设置的时间
  layer.beginTime = 0.0;    
//4. 计算暂停的时间(这里也可以用CACurrentMediaTime()-pausedTime)
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
//5. 设置相对于父坐标系的开始时间(往后退timeSincePause)
  layer.beginTime = timeSincePause;
 
 

IV CAAnimation的子类

4.1 CAPropertyAnimation

是CAAnimation的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类:

  1. CABasicAnimation
  2. CAKeyframeAnimation

keyPath属性说明:通过指定CALayer的一个属性名称为keyPath(NSString类型),并且对CALayer的这个属性的值进行修改,达到相应的动画效果。 比如,指定@“position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果

4.2 CABasicAnimation(基本动画)


是CAPropertyAnimation的子类

属性说明:

  1. fromValue:keyPath相应属性的初始值
  2. toValue:keyPath相应属性的结束值

动画过程说明:

随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValuekeyPath内容是CALayer的可动画Animatable属性。

如果fillMode=kCAFillModeForwards同时removedOnComletion=NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。

4.3 CAKeyFrameAnimation(关键帧动画)

也是CAPropertyAnimation的子类,与CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值。

CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation

属性说明:

  1. values:上述的NSArray对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
  2. path:可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,那么values将被忽略
  3. keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的

4.4 CAAnimationGroup(动画组)

动画组,是CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行 1.属性说明:

nanimations:用来保存一组动画对象的NSArray
默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间

4.5 CATransition(转场动画)


CATransition是CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点

UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果

动画属性:

  1. type:动画过渡类型
  2. subtype:动画过渡方向
  3. startProgress:动画起点(在整体动画的百分比)
  4. endProgress:动画终点(在整体动画的百分比)

  1. 使用UIView动画函数实现转场动画——单视图

+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0);
 

duration:动画的持续时间

view:需要进行转场动画的视图

options:转场动画的类型

animations:将改变视图属性的代码放在这个block中

completion:动画结束后,会自动调用这个block

  1. 使用UIView动画函数实现转场动画——双视图

+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion NS_AVAILABLE_IOS(4_0); // toView added to fromView.superview, fromView removed from its superview
 

duration:动画的持续时间

options:转场动画的类型

animations:将改变视图属性的代码放在这个block中

completion:动画结束后,会自动调用这个block

4.6 CADisplayLink (时钟机制)

CADisplayLink是一种以屏幕刷新频率触发的时钟机制,每秒钟执行大约60次左右 CADisplayLink是一个计时器,可以使绘图代码与视图的刷新频率保持同步,而NSTimer无法确保计时器实际被触发的准确时间。

使用方法:

  1. 定义CADisplayLink并制定触发调用方法
  2. 将显示链接link添加到主运行循环队列

通常保障一个View只拥有一个link

- (CADisplayLink *)link{
 
    if (nil == _link) {
 
        _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
        [self.link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
 
    return _link;
 
}
- (void)startRotating{
 
    /** 核心动画的缺点是改变不了真是的属性*/
 
//    CABasicAnimation *animation = [CABasicAnimation animation];
 
//    //设置动画对象属性
 
//   
 
//    [self.roattionImageView.layer addAnimation:animation forKey:nil];
 
    [self.link setPaused:NO];
 
     
}
 
 
- (void) update{
 
    [self.roattionImageView setTransform:CGAffineTransformRotate(self.roattionImageView.transform, angle2Radian(45/60.0))];//60 次转45弧度,每次转45/60 弧度
 
    
}
 
- (void) stopRotating{
 
    [self.link setPaused:YES];
}
 

V iOS Core Animation&CALayer 的使用例子

kunnan.blog.csdn.net/article/det…

iOS小技能:和uni-app、unity的融合方案

本文正在参加「金石计划」

引言

项目背景:iOS app内嵌H5和 AR功能,AR使用unity2020开发。

技术点:unity导出的目标工程Unity-iPhone.xcodeproj 与原生进行融合

目前的方案:uniapp 桥接IOS, ios 桥接unity, uniapp 采用插件的形式进行iOS原生代码开发, 然后集成为ipa文件。

I 实现思路

思路1: uniapp 桥接IOS,ios 桥接unity,然后集成为ipa文件。

Unity发布到iOS平台得到的是一个Xcode工程,uniapp 这边是个本地资源包。

思路2: 原生APP集成Unity导出的Xcode工程(需要将导出的工程封装为静态库)zhuanlan.zhihu.com/p/103759507www.jianshu.com/p/00dcac5b2…

思路3: Unity3D 嵌入iOS原生代码www.jianshu.com/p/82e34d9a1…

思路4: Unity导出的web工程部署到服务器,原生APP加载对应的H5地址。(视加AR不支持导出Web)

小程序的AR是图片识别,视加AR是空间识别; 小程序是在屏幕上,app可以做到3D的跟随图片。

如果有更好的思路和相关文章,欢迎留言交流。

II 桥接

2.1 IOS中调用Unity的方法

在这里插入图片描述

在这里插入图片描述

使用unityengine.dll提供的C接口UnitySendMessage 第1个char* 表示接受该消息的GameObject的name, 第2个表示该GameObject的脚本中接受消息的函数名, 第3个表示传递的参数。

    //参数1 场景中模型的名字
    //参数2 脚本名称方法
    //参数3 想unity传递一个char类型的数据
   UnitySendMessage("iOSSendMessageToUnity""ChangeCameraDirection""");

结合后unity里面的你先打开后是进入这个场景(SelectScene) 物体名SceneManager 脚本名SceneSelect 函数ChangeScene(string str) 传参数ZhenWuMiaoAR就是真武庙;或者传参数modelShow就是沙盘。

2.2 uniapp 桥接iOS

除了 uni-app 框架内置的跨端 API,各端自己的特色 API 也可通过条件编译 自由使用。因此uniapp可直接使用原生API进行发布通知,实现通信。 在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

III uni-app运行原理

kunnan.blog.csdn.net/article/det…

3.1 js引擎

浏览器的js引擎,就是jscore或v8的基础上新增了一批浏览器专用API,比如dom;

node.js引擎,则是v8基础上补充一些电脑专用API,比如本地io;

uni-app的App端和小程序端的js引擎,其实是在jscore上补充了一批手机端常用的JS API,比如扫码。

3.2 运行原理

uni-app 在非H5端运行时,从架构上分为逻辑层和视图层两个部分。

  1. 逻辑层负责执行业务逻辑,也就是运行js代码。

逻辑层是运行在一个独立的jscore里的,它不依赖于本机的webview,所以一方面它没有浏览器兼容问题,可以在Android4.4上跑es6代码,另一方面,它无法运行window、document、navigator、localstorage等浏览器专用的js API。

  1. 视图层负责页面渲染。

h5和小程序平台,以及app-vue,视图层是webview。

而app-nvue的视图层是基于weex改造的原生渲染视图。

3.3 编译器

vue2:uni-app编译器基于wepback实现

vue3:uni-app编译器基于Vite实现,编译速度更快

HBuilderX 3.3.0+ , uni-app在App/H5/小程序全平台支持Vue 3.0开发,且全平台支持Vite编译器。

Webpack1
   |
   |
Rollup 出现(推崇 ESM 规范,可以实现 tree shaking, 打包出来的代码更干净)
   |
   |
Webpack2(也实现了 tree shaking, 但是配置还是太繁琐了)
   |
   |
Parcel (号称 0 配置)
   |
   |
Webpack4(通过 mode 确定 development 和 production 模式,各个模式有自己的默认配置)
   |
   |
Webpack5(持久化缓存、module federation)
   |
   |
Esbuild(采用 go 语言开发,比 Webpack 更快)
   |
   |
Vite(推崇 ESM 规范,开发模式采用 nobundle,更好的开发体验)

组件模块化成为前端开发的主流模式,以 React 和 Webpack 为例:将一个应用涉及到的所有的功能拆分为一个个组件,一个组件对应一个源文件,然后通过 Webpack 将这些源文件打包。在开发过程中,可通过 Webpack 开启一个 local server,实时查看代码的运行效果。

IV 常见问题

4.1 支持 ARCore 的设备

developers.google.cn/ar/devices#…

4.2 uni-app和原生App混合开发问题

首先务必确认uni-app和原生代码,谁是主谁是从的问题。

uniapp.dcloud.io/hybrid.html

  1. 如果你的应用是uni-app开发的,需要扩展一些原生能力,那么首先去插件市场看看有没有现成的插件,如果没有,就自己开发原生插件。
  2. 如果你的App是原生开发的,部分功能栏目想通过uni-app实现,有2种方式: a. 在原生App里集成uni小程序sdk,然后即可运行用uni-app框架开发的小程序前端项目(小程序应用资源包wgt)。nativesupport.dcloud.net.cn/README b. 如果不想集成原生sdk,那就把uni-app代码发布成H5方式,在原生App里通过webview打开。

see also

公众号:iOS逆向

iOS小技能:Makefile的使用(Makefile的规则、部署脚本、config管理ssh连接)

本文正在参加「金石计划」

前言

make是一个命令工具,是一个解释makefile中指令的命令工具。其本质是文件依赖,Makefile文件制定编译和链接所涉及的文件、框架、库等信息,将整个过程自动化。 一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。

例子: 使用别名配置IP

I 预备知识

make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。那么先来了解下什么是编译?

  1. 编译: 把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译。
  2. 链接: 把大量的Object File合成执行文件

源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。

每个源文件都应该对应于一个中间目标文件(OBJ文件)

1.1 编译

在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。

只有所有的语法正确,编译器才可以编译出中间目标文件。至于函数与变量的声明的正确,就是指头文件的所在位置。

1.2 链接

主要是链接函数和全局变量,所以可以使用这些中间目标文件来链接我们的应用程序。

在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)。

II Makefile的规则

    target... : prerequisites ...
          command
          ...
          ...
         -------------------------------------------------------------------------------
  • 例子

THEOS_DEVICE_IP=iphone#5C9 #配置IP的host 别名
TARGET = iphone:latest:8.0
ARCHS = armv7 arm64
THEOS=/opt/theos
THEOS_MAKE_PATH=$(THEOS)/makefiles
include $(THEOS)/makefiles/common.mk

TWEAK_NAME =
Taoke_FILES = Taoke.xm 

Taoke_FRAMEWORKS = UIKit, Foundation, Security, IOKit, JavaScriptCore

include $(THEOS_MAKE_PATH)/tweak.mk

after-install::
install.exec "echo '' > /var/log/syslog"
install.exec "killall -9 WeChat"
    install.exec "killall -9 Moon"

  • target也就是一个目标文件,可以是Object File ,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。

  • prerequisites就是要生成那个target所需要的文件或是目标

  • command也就是make需要执行的Shell命令

  • make clean清除所有的目标文件,以便重编译。

一个简单的例子

ARCHS = armv7 arm64
TARGET = iphone:latest:7.0

include theos/makefiles/common.mk

TWEAK_NAME = iOSREProject
iOSREProject_FILES = Tweak.mk
iOSREProject_PRIVATE_FRAMEWORK = BaseBoard
incluce $(THEOS_MAKE_PATH)/tweak.mk

after-install:: install.exec "killall -9 SpringBoard"

注意:BaseBoard这个private framework只存在于8.0以上的版本的SDK中,在IOS7 里并没有,所以这个tweak在IOS 7 中因为不会找到framework 而无法进行工作。这种情况可以通过弱引用( “makefile weak linking”)或dlopen()、dlsym() 和 dlclose()系列函数动态调用private framework来解决。

  • 链接Match-o对象(Match-O object)

Theos 采用GUN Linker来链接Mach-O对象,包括.dylib、 .a 和 .o 。 例如,要链接libsqlite3.0.dylib 、libz.dylib 和dylib1.0像下面这样写就OK

iOSREProject_LDFLAGS = -lz -lsqlite3.0 -dylib1.o 

  • 开启arc: 不用自己手动进行内存管理
$(TWEAK_NAME)_CFLAGS += -fobjc-arc

  • 单独为单个文件指定mrc
ZKSwizzle/ZKSwizzle.m_CFLAGS = -fno-objc-arc

III 集成第三方库

3.1 编译参数指定一些编译链接参数、搜索路径

TweakDemo_CFLAGS = -fobjc-arc -Wno-int-to-void-pointer-cast -Wno-int-to-pointer-cast -I./BookLib/include -F./BookFramework # 指定头文件位置
ZKSwizzle/ZKSwizzle.m_CFLAGS = -fno-objc-arc # 设置单个文件的MRC
TweakDemo_LDFLAGS = -L./BookLib -F./BookFramework # 链接
TweakDemo_LIBRARIES = BookLib #.a
TweakDemo_FRAMEWORKS = BookFramework # framke

  • 导入头文件直接调用
// 这个第三方库或者头文件也可以放到 /opt/theos/对应的目录
#import <BookLib/BookLib.h> //.a
#import <BookFramework/BookFramework.h>// framework

3.2 使用 ASIHTTPRequest 的例子

 20 RESOURCE_DIR = Resources
#编译 ----------------------------ASIHTTPRequest--------------------
 32 service_FILES += ASIHTTPRequest/ASIAuthenticationDialog.m
 33 service_FILES += ASIHTTPRequest/ASIDownloadCache.m
 34 service_FILES += ASIHTTPRequest/ASIFormDataRequest.m
 35 service_FILES += ASIHTTPRequest/ASIHTTPRequest.m
 36 service_FILES += ASIHTTPRequest/ASIInputStream.m
 37 service_FILES += ASIHTTPRequest/ASINetworkQueue.m
 38 service_FILES += ASIHTTPRequest/Reachability.m
 39 
 40 #编译 ----------------------------Encoder---------------------------
 41 service_FILES += Encoder/CrypHelper.m
 42 service_FILES += Encoder/GTMBase64.m
 43 service_FILES += Encoder/NSData+Base64.m
 44 service_FILES += Encoder/NSString+MD5Addition.m
 45 service_FILES += Encoder/UIDevice+IdentifierAddition.m
 46 
 47 #编译 ----------------------------TBXML-----------------------------
 48 service_FILES += TBXML/NSDataAdditions.m
 49 service_FILES += TBXML/TBXML.m
 50 
 51 #编译 ----------------------------Regex-----------------------------
 52 service_FILES += Regex/RegexKitLite.m
 53 
 54 #编译 ----------------------------Constant--------------------------
 55 service_FILES += Constant/Constant.m
 56 service_FILES += Constant/UserDefaults.m
 57 

#连接单元
 87 #
 88 #连接 ----------------------------framework--------------------------
 89 service_FRAMEWORKS = Foundation UIKit QuartzCore Foundation CoreFoundation CoreGraphics  MobileCoreServices
 90 service_FRAMEWORKS += SystemConfiguration  CFNetwork CoreTelephony CoreLocation MapKit
 91 
 92 
 93 #连接 ----------------------------dylib------------------------------
 94 service_LDFLAGS = -lxml2 -lz -licucore -lgcc -lgcc_eh -lstdc++ 
 95 service_LDFLAGS += -L./BaiduMap/inc -R./BaiduMap/inc/ -lbaidumapapi
 96 
 97 #编译器 ----------------------------------------------------------
 98 #service_CFLAGS += -std=c99 
 99 #service_CFLAGS += -all_load
100 
101 #资源文件 ----------------------------------------------------------
102 internal-package::
103    ifneq ($(wildcard $(RESOURCE_DIR)/*.png), )
104     mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/$(TWEAK_NAME)
105     cp $(RESOURCE_DIR)/*.png $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/$(TWEAK_NAME)/
106    endif
107    ifneq ($(wildcard $(RESOURCE_DIR)/*.lproj), )
108     mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/$(TWEAK_NAME)
109     rsync -avC $(RESOURCE_DIR)/*.lproj $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/$(TWEAK_NAME)
110    endif
111    ifneq ($(wildcard $(RESOURCE_DIR)/$(TWEAK_NAME).plist), )
112     mkdir -p $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/$(TWEAK_NAME)
113     cp $(RESOURCE_DIR)/$(TWEAK_NAME).plist $(THEOS_STAGING_DIR)/Library/PreferenceLoader/Preferences/$(TWEAK_NAME)
114    endif
115 

20行存放资源文件的本地目录名称:可以自定义 主要是在生成包拷贝时使用。

  • CFLAGS 指定头文件(.h文件)的路径
CFLAGS=-I/usr/include -I/path/include
 TWEAK_NAME_CFLAGS += -I./include

有时候LDFLAGS指定-L虽然能让链接器找到库进行链接,但是运行时链接器却找不到这个库,如果要让软件运行时库文件的路径也得到扩展,那么我们需要增加这两个库给"-Wl,R":

LDFLAGS = -L/var/xxx/lib -L/opt/mysql/lib -Wl,R/var/xxx/lib -Wl,R/opt/mysql/lib

IV deploy 部署脚本

#!/bin/sh
cd `dirname $0` 
make clean
make package install

scp ./taoke.lua iphone:/private/var/mobile/Media/TouchSprite/lua/#配置IP的host 别名

rm -f ./debs/*
exit 0

4.1 layout的应用

应用场景: 新增图片、文件等资源文件,或者二进制可执行文件;然后在代码中调用使用它们。这个时候可以使用layout目录指定文件到特定的路径。

  • 获取文件的代码如下
NSString* resourcePath = @"/Library/Application Support/TweakDemo/";
NSDictionary* resourceDict = [[NSDictionary alloc] initWithContentsOfFile:[resourcePath stringByAppendingPathComponent:@"resource.plist"]];
NSLog(@"resourceDict:%@", resourceDict);

4.2 使用ssh config配置文件来管理ssh连接

config 文件:设置IP

blog.csdn.net/z929118967/…

# Private 192.168.2
Host iphone
HostName  192.168.2.131
User root 
IdentityFile ~/.ssh/id_rsa_Theos125

Host gitlab.cn
HostName  gitlab.cn
User git
IdentityFile ~/.ssh/id_rsa_q



# Private github
Host github.com
HostName  github.com
User git
IdentityFile ~/.ssh/id_rsa

一个SSH的认证文件可以重复使用到不同的host

devzkndeMacBook-Pro:.ssh devzkn$  ssh-copy-id -i id_rsa_Theos125.pub root@192.168.2.150

V 总结

# 系统配置部分    export THEOS=/opt/theos/
#   RESOURCE_DIR = Resources  存放资源文件的本地目录名称:可以自定义 主要是在生成包拷贝时使用  #资源文件 ----------------------------------------------------------102 internal-package::
# 调试设备的IP地址
THEOS_DEVICE_IP=iphone150    #5C9
TARGET = iphone:latest:8.0
ARCHS = armv7 arm64
THEOS=/opt/theos
THEOS_MAKE_PATH=$(THEOS)/makefiles
# 引入常用的一般模板
include $(THEOS)/makefiles/common.mk
# 工程名称
TWEAK_NAME =
# :常用的一个宏设置是否显示NSLog 区分是debug还是release 也可以自己尝试设置其他宏变量
DEBUG = 0

# 编译所需要的的源文件
Taoke_FILES = Taoke.xm 
# sdk中的framework
Taoke_FRAMEWORKS = UIKit, Foundation
#CXXFLAGS 表示用于 C++ 编译器的选项。
ADDITIONAL_CCFLAGS  = -Qunused-arguments
# gcc等编译器会用到的一些优化参数 也可以在里面指定库文件的位置;例如需要的一些系统静态库和第三方静态库 例如,要链接libsqlite3.0.dylib 、libz.dylib 和dylib1.0  TWEAK_NAME_LDFLAGS = -lz -lsqlite3.0 -dylib1.o   或者使用TWEAK_NAME_OBJ_FILES += libcurl.a  指定库文件的位置的用法:LDFLAGS=-L/usr/lib -L/path/to/your/lib -WL,R 运行时库文件的路径也得到扩展-Wl,R/var/xxx/lib
ADDITIONAL_LDFLAGS  = -Wl,-segalign,4000
# CFLAGS 表示用于 C 编译器的选项, 际上涵盖了编译和汇编两个步骤 编译器参数设置 #service_CFLAGS += -std=c99  #service_CFLAGS += -all_load   RedRobert_CFLAGS += -Wno-error 忽略的编译错误  TWEAK_NAME_CFLAGS += -I./include
ADDITIONAL_CFLAGS = -Werror -Wobjc-method-access
# 引入tweak模板
include $(THEOS_MAKE_PATH)/tweak.mk

after-install::
    install.exec "echo '' > /var/log/syslog"
    install.exec "killall -9 WeChat"
    install.exec "killall -9 Moon"

see also

iOS 逆向: 批量部署Tweak插件到iOS设备的方案(how to host cydia repo?)

kunnan.blog.csdn.net/article/det…

❌