简介

文章将简要讲述何时使用@State, @Binding, @StateObject, @ObservedObject, @EnvironmentObject。

@State

@State属性包裹器可用于当你的视图对象响应任何状态改变时候。换句话说,视图初始化@State修饰的属性变量。该属性变量的变更只发生在内部View,而不被外界所改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct StateExample: View {
@State private var intValue = 0

var body: some View {
VStack {
Text("intValue equals \(intValue)")

Button("Increment") {
intValue += 1
}
}
}
}

在内部,SwiftUI将会存储 @State修饰的属性值,然后贯穿整个渲染或重复渲染的生命周期中将持久化存储这个值,当在视图刷新或者重新创建的时候,该属性值可以很好地被视图自身管理。当然,你也可以用private修饰词修饰@State属性,保证只在内部修改@State属性。
@State使用场景:

  • 在被修饰的属性需要响应你视图变化的状态时候
  • 你修饰的是一个值类型(结构体、枚举)

大多数产品在本地Sqlite存储的时候,基本都是用了串行模式。即整个应用全局只有一个sql操作句柄,用单例管理着这个SqlHandler。另外还有一个模式叫线程模式。这种模式下很容易出现Sqlite_Busy错误。

  1. 当有写操作时,其他读操作会被驳回。
  2. 当有写操作时,其他写操作会被驳回。
  3. 当开启事务时,在事务提交之前,其他写操作会被驳回。
  4. 当开启事务时,在事务提交之前,其他事务请求会被驳回。
  5. 当有读操作时,其他写操作会被驳回。
  6. 读操作之间能够并发执行。

第三方库WCDB支持多线程度于读与读,读与写并发执行,写与写串行执行。WCDB 在多线程方面明显优于 FMDB 和 ModelSqliteKit,通过 WCDB 的改造,使得SQLite的性能发挥到极致。

简介

图片编辑是基于 CLImageEditor 第三方进行改装得到仿微信的图片编辑交互。原CLImageEditor库是一个日本人写的图片编辑,支持涂鸦,裁剪,旋转,文字帖功能。但有几项我们进行改进。

  • 所有的这些图片操作我们汇总到一起进行编辑,最终保存的时候才处理所有操作汇总成一张处理图片。
  • 涂鸦撤销功能实现真正的上一步撤销,而不是仅仅透明色进行橡皮擦擦除。
  • 新增马赛克操作功能。
  • 把图片裁剪和旋转进行结合到一个页面操作,另外新增裁剪、旋转之后的图片恢复。
  • 改变颜色选择,文字编辑的交互,新增图片裁剪区域的边角重点标注。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CLImageEditorViewController:

    usedToolDic:使用过的工具【toolName:CLImageToolBase】

    -(CLImageToolBase *)currentEditTool; 当前正在使用的工具。

    -(UIImage *)getMergeDrawImageForCropTool; 最后合成的图片。用于业务转发、保存、回调给用户。

    //图片编辑器所支持的工具类都继承于CLImageToolBase, 通过CLClassList辅助类runtime方式自动将相关工具类注入到菜单项中。
    + (NSArray*)subtools
    {
    return [CLImageToolInfo toolsWithToolClass:[CLImageToolBase class]];
    }
    CLImageToolBase 工具基类:持有editor编辑器方便拿到图片,同时具有setup, cleanUp 这几个“生命周期”方法,做相关工具的视图创建与清除。

我们每个人的手机都安装有微信App,肯定大家都使用过以下场景在聊天群中,当有新的成员进入的时候,可以看到 某某邀请了某某1进入群聊,或者当你是群主的时候,某管理人员邀请了几个人进入时候看到 某某管理员邀请了张三、李四,王五,张三(同名)进入了群聊。对于邀请人员或者被邀请人员,我们发现人员的名称是可以点击的,即使同名的人,点击其名字也可以跳转到各自的主页详情中。

如何存储人员的id?

一条群成员变更通知就是一串文本,我们点击的时候肯定要存储好他的人员id,然后点击的时候就能拿到对应人员的id。从最简单的存储方案来看,这条内容可以写成以下格式: 管理员刘某某邀请了张三李四王五张三进入了群聊。换成代码的格式即为:“管理员\”刘某某::1000\”邀请了\”张三::1001、李四::1002、王五::1003、张三::1004\”进入了群聊”。其中的代码格式为userName::userId这样一种形式进行拼接,中间的符号可以采用其他特殊符号代替。这里为了区别用户特殊性输入用了双冒号。多个成员之间用顿号隔开,人员与普通内容之间用符号双引号\”进行分割(斜杆为转义符)。 我们需要对这一串文字表达式进行解析进行UI显示,把人员名字后的拼接符号用户id进行过滤掉,组合成不带用户id的易懂文字内容给到用户。

群插件的基础属性

群插件一般是用H5配置的网页小插件,然后和群构造一个入口。本地通过sqlite维护一个插件表,以及群插件关系表。插件信息升级一下可分为平台系统必须插件以及业务自定义插件。具体可由业务决定。
插件有其基本属性:名称,插件id,类型,默认排序值,跳转内容(本地模块名/h5链接地址),跳转类型:h5/本地模块 等等。
插件关系表: 群id,插件id,角标数量,排序值,是否显示,等等。

聊天输入框@某人逻辑分类点说明

IM 应用软件无论是App端或者是PC端都有@某人这样一个功能点,由于用户输入框输入条件的复杂随机性种类多,@的针对不同情况进行拼接或者新增样式显示蓝色字体表示该人名可链接。由输入框开始,用户输入@的方式进行分类:

  1. 首字母为空,用户直接输入@,然后跳转到成员页面进行选择相关人员,选完即带回人名 结果为:“@某人”
  2. input输入框输入了文字的基础上: 在最前面下班为0的第1个位置进行插入@ 结果为:“@某人原input输入框的文字”
  3. input输入框输入了文字的基础上,在最后面下班为length-1的第后一个位置进行插入@ 结果为:“原input输入框的文字@某人”
  4. input输入框输入了文字的基础上,在文字内容中输入@。即条件 0 < index < length-1 结果为:“原input输入@某人框的文字”

在以上四种输入情况中,3,4 输入拼接@某人的时候,需要判断前一个字是否为@, 如果是,则后面拼接的时候直接+某人即可。另外当在第4种情况文字中间输入的时候,中英文下输入可能遇到文本框的值为 “ @@文字”,按照之前的逻辑当光标输入在第2个@后时,然后键盘输入@跳转页面选择成员,最后的结果应该是“ @@某人文字”这个组合,为了更友好地显示,还需检查当前两个也是@的时候,即出现1个@. 即结果为“ @某人文字” 以便更友好地显示。

核心逻辑:根据文本框原输入文字是否有内容,无内容时候直接拼接。有内容时候分两种情况,即光标的输入位置情况:当location等于0的时候,即在首位置输入@某人拼接原文本内容。当location大于0的时候,即光标可能出现在内容文字之间,也可能是在最后面。也是分两种情况:根据location+1判断是否小于原文本框文字长度length.如果小于,则表示在内容之间插入。否则,在内容最后尾部插入。无论是之间还是尾部做插入,都需检查前一个字符是否为@,如果是,则在做拼接人名时候就无需再追加@。当在之间做插入的时候,为了友好显示,可适当判断前两个字符为@@时候进行替换为一个@。

开发中由于系统版本以及sdk,第三方库等原因,都会影响成出现一些奇怪的问题。当时是引用YYTextview控件解决消息cell长文本的光标选择范围内容复制的小功能。由于YYTextview光标弹出层自定义在另外一个新的Windown上,然后左侧侧滑出的view是新增在另一个新的Windown中。当侧边栏dismiss的时候,可能由于windown层级的影响,当在聊天输入框再次输入文字长按时候,系统出现的UIMenu层的Button不见了。当时想从根本问题去排查为啥系统的UIMenu当长按的时候操作功能的button为啥不见去解决,花费了将近2个多小时通过Xcode调试查看层级,阅读YYTextview的光标自定义层的实现以及添加到windown的层级,查看左侧侧边栏添加动画实现方式等都未解决。然后想到如果不通过新创建window来添加view 实现动画,而是通过present一个控制器实现侧边栏动画。最终问题得到了解决。

旧Slide侧滑方案

旧有的侧滑方案是创建一个新window,然后设置其windowLevel 为正常的+1,这样就在最顶层了,然后设置其rootViewController为侧滑的控制器。很有可能是新创建的window与YYtextView的光标选择层创建的window有冲突,然后造成UIMenu按钮消失了。

改进方案,Present一个控制器 SlideVC

实则我们可以通过拿到最顶层控制器topVC,用导航控制器把SlideVC包裹好,设置它的 modalPresentationStyle 模式为UIModalPresentationOverCurrentContext,然后present出来 .这里要注意的是,系统的present方法是否有被runtime method swizled进行方法替换来公共统一处理modelPresentStyle问题,因为有些App交互不想使用系统的抽屉式present效果, 如果有需要做相关的过滤业务控制器逻辑判断。 侧边动画的实现:在SlideVC中定义一个半透明层背景bgView,添加tap手势,点击即关闭。往bgView添加一个左侧内容视图leftView 75%宽,右侧rightView。self.view添加一个Pan手势,用来计算leftView拖拽时候X的值位移变化。将内容控制器交由SlideVC管理:

2020 年下半年iOS工作技术点 tips

背景:疫情原因导致之前云喵工作不能够继续,2020年6月还是面试找了一份相对稳定的工作,去了南航的一家外包供应商。

E App 是一个企业内部OA即时通讯App. 驻场南航后主要是针对此项目进行功能迭代敏捷开发。主要说一下几个技术任务点:

  1. 首要任务就是将旧有MRC内存运行环境改成ARC运行环境。
    任务到不是复杂,主要修改点是多,体力劳动。将ARC相关的修饰关键词,控制器的dealloc 方法以及设置的代理delegate, 配置文件-fno-objc 相关配置干掉。
    遇到的问题?
    期间由于之前是MRC手动内存回收机制,由于开发人员代码某些变量没有回收或者泄露贮存在内存中,当改成ARC之后会自动释放导致再次使用该变量的时候出现nil值崩溃或者业务的中断开!
    C++代码块Client类某方法的变量被释放造成崩溃?
    当时问题点抛向如何停止某线程的解决方案上去了,而不是终止某条件然后让线程自然停止。当程序进入后台applicationWillTerminate的时候,手动退出IM.然后进入销毁连接阶段。为了使alive_thread , recv_thread 两线程退出,在CLIENT_Disconnect 销毁阶段,通过改变alivethread的条件让while条件中止,线程必然退出。
  2. h5网页打卡有时候定位不到或者崩溃。因App程序使用了百度地图sdk,所以直接升级百度地图sdk即可。sdk编译分xcode11.3 或 xcode 12 。具体看E项目如果采取xcode11.3 编译打包上线,那么需要找到适应的.a包或者framework 进行编译。
  3. 将ASI网络文件下载改成AFNetwroking网络文件下载,将长进度条改成扇形进度条。
    ASI那套网络请求比较老旧,当时还是用NSURLConnnection来进行网络请求,而且需要单独维护一个网络线程长期贮存到内存中。AFN 采取NSURLSession来进行网络请求,线程即用即回收。
    AFN请求时设置支持SSL安全策略。

    1
    2
    3
    4
    5
    AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init];
    securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
    securityPolicy.allowInvalidCertificates = YES;
    securityPolicy.validatesDomainName = NO;
    [_manager setSecurityPolicy:securityPolicy];

    然后通过NSURLSessionDownloadTask 进行下载request,返回对应的进度,通过KVO进行进度条的UI显示,也可通过Block传递到控制器进行显示。

Talk over someone by accident(当不小心打断别人时):

Sorry I interrupted, you were saying … ?
Sorry go ahead/ go on.

useful expressions for video conference(视频会议一些非常有用的表达):

Identify yourself (表明身份)

Gaoying speaking, can we have a break?

Did not hear clearly (听不太清晰时)

Can you speak up a little bit?
Can you say it again ?
Sorry I did’t get that? what did you say?

How to interrupt (如何打断插话)

Would you mind if I jump in here please ?
Can I stop you for just one second please?

Leave for a moment (临时离开)

I will be back later.
I am gonna step away for a few minutes.

Deal with technical issues (网络不畅通)

My internet is a bit slow.
Can you move closer to the router?
Sorry I am late, I had hard time in connecting.
I am having problems with my connection.

Try to adjust your output settings (调整外接设备)

It sounds like you have echoes.
Gaoying, are you still with us?
Sorry my mic was off. / you are muted. Is your microphone on mute?
Gaoying is cutting off.

Office Tips

talk to you later.
Sure, OK, Yes, No problem. Exactly, Absolutely, Of course, Correct.
No, Not yet. Not now. I don’t think So. I Have different opinion; I might disagree with you.

Leave Application

Dear Manager, I would like to apply for a three-day leave, starting from the 13th and finishing on 15th, in order
to return home to fix something.
I should be very much obliged if you will grant me the leave.
Gaoying.

Job Report

Dear XX:
I am writing to report my work progress of this week. The details of XXX project are as follows:
1…
2…
I may need your help for below 2 aspects:
1….
Above all are all my work items during this week, if you have time ,I would like to have a private talk with you for more detials。If any queries, please contact me. thank you.

Email Ask Question

I wanted to follow up on or meeting last week and confirm our plans for this month.
I would like to ask about the budget and whether this will affect our department.
Do you know when the database issues will be fixed ?
Let’s confirm our plans for this month.
How will the new budget affect our department?
I’m concerned about the number of sick days staff have been talking recently.
I suggest that you contact all clients who may have been affected by the data breach.
Can I ask you to design a poster to inform staff about the new policies?

Echart

数据看板首要问题,就是我们要解决数据分组的问题。你可以存储到浏览器的websql中通过sql语句groupBy来进行分组统计,或者你自己用js对数据进行分组。由于数据量有限,也就几百个吧,这里采取js方式对数据进行内存分组统计。

1
2
3
4
5
6
7
8
9
export const groupBy = (list, fn) => {
const groups = {};
list.forEach(function(o) {
const group = JSON.stringify(fn(o));
groups[group] = groups[group] || [];
groups[group].push(o);
});
return groups;
}