iOS 知识小集不知不觉已经更新了 15 期,也许有些同窗会问,前 14 期在哪里能找到呢?天天一条iOS知识小集在哪里能第一时间看到呢?我有必要说明一下,咱们的文章会在公众号上首发,其它平台只会发部分文章,并且更新都会比公众号晚。天天早晨 7:30左右,会在小集微信群和微博发出天天一条iOS知识小集和公众号文章。若是你想关注咱们的公众号或加小集微信群(2号群还差13个名额就满了),请看文章结尾。javascript
这周公众号发布的文章:css
本期知识小集:html
截止目前为止iOS知识小集已经发布183条,我想看看前端
做者: halohilyjava
在 iOS 开发中,自定义的弹层组件很是常见,好比分享框、自定义的 actionSheet 组件等。有的场景下,会选择使用 UIViewController 类型来实现,这时定制这个视图的出现、隐藏动画很是方便。然而,有时候须要选择轻量级的 UIView 类型来实现。这时该怎么定制它的出现、隐藏动画呢?这里提供一个思路:git
使用 UIView 的
willMoveToSuperview:
和didMoveToSuperview
这组方法,它们会在UIView
做为subView 被添加到其余 UIView 中时调用。这里须要注意,自身调用removeFromSuperview
方法时,一样会触发这组方法,只不过这时的参数会是一个 nil。github
提供一个例子来讲明:一个选择 UIView 类型实现的自定义 actionSheet 的出入动画,交互基本和微信一致。web
#pragma mark - show & dismiss
- (void)didMoveToSuperview {
if (self.superview) {
[UIView animateWithDuration:0.35 delay:0 usingSpringWithDamping:0.9 initialSpringVelocity:10 options:UIViewAnimationOptionCurveEaseIn animations:^{
_backgroundControl.alpha = 1;
self.actionSheetTable.frame = CGRectMake(0, SCREEN_HEIGHT - _sheetHeight, SCREEN_WIDTH, _sheetHeight);
} completion:^(BOOL finished) {
[super didMoveToSuperview];
}];
}
}
- (void)hideSelf {
[UIView animateWithDuration:0.35 delay:0 usingSpringWithDamping:0.9 initialSpringVelocity:10 options:UIViewAnimationOptionCurveEaseIn animations:^{
_backgroundControl.alpha = 0;
self.actionSheetTable.frame = CGRectMake(0, SCREEN_HEIGHT, SCREEN_WIDTH, _sheetHeight);
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
复制代码
做者: Vong_HUST数据库
一般咱们会遇到这种需求,一个视图除了须要响应子视图的点击事件,其它空白地方但愿能将点击事件透传到,好比自定义了一个“导航栏”,除了左右两边按钮,但愿其它部分点击可以透传到底下的视图。这个时候咱们能够经过复写 hitTest
方法,具体实现以下。编程
@implementation PassthroughView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (self.hidden || self.alpha < FLT_EPSILON || self.userInteractionEnabled) {
return [super hitTest:point withEvent:event];
}
UIView *targetView = nil;
for (UIView *subview in [[self subviews] reverseObjectEnumerator]) {
if ((targetView = [subview hitTest:[subview convertPoint:point fromView:self] withEvent:event])) {
break;
}
}
return targetView;
}
@end
复制代码
以上代码便可实现,只响应子视图的事件,而非子视图区域部分的交互事件则透传到响应链中的下一个响应者。
若是你有其它更好方式,也能够分享出来,一块儿交流下。
做者: Lefe_x
上次的小集中,我主要讨论了如何调试 WebView ,小集发出后 @折腾范儿_味精
提供了另外一种方法来调试 WebView。我以为有必要再扩展一下,原话是这样的:
真说方便仍是植入一个 webview console 在 debug 环境,能够在黑盒下不连电脑不连 safari 调 dom,调 js,另外在开发期间 Xcode 断点 run 的时候,js hook console.log console.alert,接管 window.onerror 全都改 bridge NSLog 输出,也会方便点。
短短几句话,信息量很大,私下向味精学习了下,这里总结一下。写完这个小集特地让味精看了下,以为有必要再补充下第二种调试技巧,但中途踩了几个坑,一直到23:30左右才搞定。
第一,把 WebView 用来调试的 log、alert、error 显示到 NA ,在调试时会方便很多。作 WebView 与端交互的时候,主要用 window.webkit.messageHandlers.xxx.postMessage(params);
来给端发消息,也就是说 WebView 想给端发消息的时候直接调用这个方法便可,端会经过 WKScriptMessageHandler
的代理方法来接收消息,而此时端根据和 WebView 约定的规则进行通讯便可。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
复制代码
而添加调试信息,无非就是给 WebView 添加了 log、alert、error 这些消息的 bridge,这样当 WebView 给端发送消息后,端根据和 WebView 约定的规则解析 log、alert、error 为端对应的事件,好比 log 直接调用端的 NSLog
,alert 调用端的 UIAlertController
。
第二,黑盒下调试 WebView,无需链接电脑和 safari 便可调试 DOM,这个能够参考小程序的 vConsole 或者 eruda 。能够直接在 WebView 中接入,或者在端中接入。这里以在端中接入 eruda 为例,这里踩到几个坑:
1.有些页面显示不出来,估计是故意屏蔽掉的,味精特地使用 JSBox 试了下其它页面,发现百度等都不能够显示调试按钮,而掘金是能够的;
2.使用本地的页面也显示不出来,这是 webview 跨域安全方面的考虑,file 协议下会禁止 js css html 以部分 file,部分网络的方式加载。
下面这段代码直接在 webview 加载完成后执行便可。
NSString *js = @"(function() {var script = document.createElement('script');script.type = 'text/javascript';script.src = 'https://xteko.blob.core.windows.net/neo/eruda-loader.js';document.body.appendChild(script);})();";
[self.webView evaluateJavaScript:js completionHandler: nil];
复制代码
做者: halohily
最近负责下载组件的开发,对于如何设计一个下载模块有一些粗浅体会,今天分享一下我采用的方案,但愿可以抛砖引玉。另外,最近会出两篇主题为“下载组件的设计”和“与 Hybrid 相关的下载方案”的长文,欢迎关注 「 知识小集 」公众号。
“下载”做为一个须要本地结构化、持久化存储的场景,使用数据库是比较天然的选择。因此,咱们首先拆分出一个数据库模块,用来存储下载记录。主要字段为下载任务的信息,如 url、文件大小、时间戳等,以及最重要的文件本地存储路径。这一层能够在接口设计上认真思虑,好比仅涉及当前业务逻辑,而不涉及具体的数据库操做,至关因而较 FMDB 等数据库组件来讲更高层的抽象。后期须要更换底层数据库引擎时,本层封装无需改动,是比较理想的实现。
数据库是用来存储下载记录的,那么所下载的具体文件呢?天然就须要一个文件管理模块,在这个模块里,负责根据文件 url 生成本地的存储路径,以及进行文件校验、存储、移除等操做。
所要下载的文件,咱们能够按体积、类型等进行区分。对于网络请求的结果这类简短内容,我抽象出了一个缓存管理器,用来完成网络请求、图片等内容的缓存。网络请求的 JSON 格式结果,能够选择 YYCache
、EGOCache
等缓存框架。而图片的缓存,则能够选择专一图片缓存的 YYWebImage
、SDWebImage
等框架。
对于体积较大的文件,天然须要一个专一大文件下载的模块。这个模块不关注具体的文件类型,不关注具体的业务场景,它只须要文件 url 、文件管理模块生成的本地目标路径,完成下载任务便可。
在以上通用模块的基础上,有一个业务层的封装,它负责根据提交的下载任务,协调调用各基础组件。举个例子,一个下载任务包括一个视频文件、一个网络请求结果、三张图片。本模块在收到任务后,首先解析出以上的任务具体结构。使用文件管理模块,根据视频文件 url 生成本地存储目标路径,调用大文件下载器完成下载,此为一个子任务。对于网络请求结果,调用缓存模块,进行缓存,此为一个子任务。对于三张图片,使用图片缓存器完成缓存,此为一个子任务。三个子任务均完成,使用数据库模块,对下载记录、媒体文件记录等进行存储。除此以外,本模块还负责对外提供下载中任务、已下载任务等数据。
做者: KANGZUBIN
前两周咱们发了一个小集「iOS 自带九宫格拼音键盘与 Emoji 表情之间的坑」,介绍了如何解决因为输入框限制 Emoji 表情的输入致使中文拼音也没法输入的问题。
后面咱们又有了新需求:对输入框已输入的文本字数进行实时统计,并在界面上显示剩余字数,且不能让所输入的文本超过最大限制长度。但这个简单的功能仍然有很多小坑。
在上一个小集中,咱们讲到,对于 iOS 系统自带的键盘,有时候它在输入框中填入的是占位字符(已被高亮选中起来),等用户选中键盘上的候选词时,再替换为真正输入的字符,以下:
这会带来一个问题:好比输入框限定最多只能输入 10 位,当已经输入 9 个汉字的时候,使用系统拼音键盘则第 10 个字的拼音就打不了(由于剩余的 1 位没法输入完整的拼音)。
怎么办呢?上面提到,输入框中的拼音会被高亮选中起来,因此咱们能够根据 UITextField
的 markedTextRange
属性判断是否存在高亮字符,若是有则不进行字数统计和字符串截断操做。咱们经过监听 UIControlEventEditingChanged
事件来对输入框内容的变化进行相应处理,以下:
[self.textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
复制代码
- (void)textFieldDidChange:(UITextField *)textField {
// 判断是否存在高亮字符,若是有,则不进行字数统计和字符串截断
UITextRange *selectedRange = textField.markedTextRange;
UITextPosition *position = [textField positionFromPosition:selectedRange.start offset:0];
if (position) {
return;
}
// maxWowdLimit 为 0,不限制字数
if (self.maxWowdLimit == 0) {
return;
}
// 判断是否超过最大字数限制,若是超过就截断
if (textField.text.length > self.maxWowdLimit) {
textField.text = [textField.text substringToIndex:self.maxWowdLimit];
}
// 剩余字数显示 UI 更新
}
复制代码
对于 UITextView
的处理也是相似的。
另外,对于“字数”的定义是不少种理解:在 Objective-C 中字符串 NSString
的长度 length
,对于一个中文汉字和一个英文字母都是 1;但若是咱们要按字节来统计和限制,同一字符在不一样编码下所占的字节数也是不一样的;另外有时咱们要统计的是所输入文本的单词个数,而不是字符串的长度,因此咱们须要根据不一样的使用场景进行分析。
coldlight_hh
或 wsy9871
,请注明 iOS 入群
;coldlight_hh
或者 bob5201215
,请注明 Flutter 入群;wsy9871
或 coldlight_hh
,请注明 前端入群
wsy9871
或 coldlight_hh
,请注明 PWA入群
kangzubin
**,请注明 小程序入群
上面技术群群规比较严,主要以讨论技术为主,发广告等行为都一概踢出群。另外,咱们的微信群不是真正的“讨论群”,为了避免浪费你们看消息的时间,除技术问题或很小白的问题,你们不会讨论。
某天「 知识小集 · 前端修行室 」进来个妹子,而后群就炸了,控制不住你们的情绪,因此专门开了个「 知识小集 · 吐槽吹水群 」
,这个群暂时不对外开放,能够加入技术群后再拉群。