造轮子:打造一款易用的 iOS Toast
一、写在前面
实在是因为太久没有写 iOS 了,快把主业给忘记了,于是心痒痒的打开 Xcode 。想起之前工作中需要这么一个 Toast,它需要满足这些功能:
- 调用时自动按队列顺序显示;
- 不被其它的视图遮挡;
- 淡入淡出动画;
- 自由的指定显示时长;
- 自由的指定显示位置,有三种位置选择;
- 根据内容长度自动调整显示时长(限定最大和最小显示时长);
- 点击吐司关闭。
代码 XXToast 已开源,如果觉得对你有用请给个⭐️
二、主要功能
1、按队列顺序显示
在实际开发中,很多时候需要对一整个执行流程进行 Toast 提示,因此我们是需要 Toast 按顺序显示的,而不是在步骤1的 Toast 显示时,进入步骤2或步骤3时立刻被其 Toast 覆盖,这样起不到很好的提示作用。而因为在 Objective-C 中,数组是有序的,刚好可以用来作为队列使用。
static NSMutableArray *toastQueue;
toastQueue = [NSMutableArray array];
有任务进来,就往后面加:
[toastQueue addObject:xxx];
完成任务时,就移除最前面的:
[toastQueue removeObjectAtIndex:0];
2、不被其它的视图遮挡
很多时候,我们在展示一个 Toast 的时候,刚好需要切换 ViewController,而又不希望 Toast 在切换 ViewController 后就不显示了。那么 Toast 就肯定不能加到当前的 ViewController 上。那么加到 KeyWindow 上呢?实测也是不行的。
+ (UIWindow *)getKeyWindow {
UIWindow *keyWindow = nil;
if (@available(iOS 13.0, *)) {
for (UIWindow *window in [UIApplication sharedApplication].windows) {
if (window.isKeyWindow) {
keyWindow = window;
break;
}
}
} else {
keyWindow = [UIApplication sharedApplication].keyWindow;
}
return keyWindow;
}
实际上需要自行创建一个 window,设置 windowLevel,使其处于较高的 level
if (@available(iOS 13.0, *)) {
// get the currently active scene
for (UIWindowScene* scene in [UIApplication sharedApplication].connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive) {
toastWindow = [[UIWindow alloc] initWithWindowScene:scene];
break;
}
}
} else {
toastWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
}
toastWindow.windowLevel = UIWindowLevelAlert + 1;
toastWindow.backgroundColor = [UIColor clearColor];
toastWindow.hidden = NO;
[toastWindow makeKeyAndVisible];
3、淡入淡出动画
因为 Toast view 我使用 UIView 实现,淡入淡出的动画可以这样实现:
其中最需要注意的是,因为我对 Toast 使用了手势,因此需要对 animateWithDuration
使用 UIViewAnimationOptionAllowUserInteraction
,以允许在动画的过程中允许用户交互。而且 toastView 不能直接设置 alpha 等于或略大于0,否则手势交互也将失效,因此改用 colorWithAlphaComponent
。
__weak typeof(self) weakSelf = self;
// fade in animation
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
toastView.alpha = 1.0;
} completion:^(BOOL finished) {
// fade out animation
[UIView animateWithDuration:0.3 delay:duration options:UIViewAnimationOptionAllowUserInteraction animations:^{
// toastView.alpha = 0.01; using this will disable the gesture
toastView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.0];
} completion:^(BOOL finished) {
// remove the toast view
[weakSelf removeToastView:toastView];
}];
}];
4、根据内容长度自动调整显示时长
在根据内容长度自动调整显示时长的同时,限定最大和最小显示时长,更符合实际使用中的用户习惯。不会因为内容过短而导致用户没看完就关闭了,也不会因为内容太长,导致占用太长时间。结合点击 Toast 关闭的功能,在完整展示信息的同时,可以带给用户更多的自由度。