可能是全网第一个适配iOS灵动岛的Toast库-JFPopup

17,656 阅读2分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情

前言

我去年的一篇文章详细的介绍了我编写的一套Swift弹窗组件库一个优雅的Swift弹窗组件-JFPopup。里面适配了一套ToastView,恰逢今年苹果iPhone14 Pro以上系列新出了一套灵动岛的交互风格,所以就意外想到能否把ToastView也适配进去灵动岛,所以此文就应运而生。我上篇文章已经很详细的介绍了JFPopup具体用法,这篇文章主要讲解适配灵动岛的心路历程。

具体效果:

用法

虽然我上篇文章已经介绍了一遍,这里我还是再写一下。另外灵动岛Toast默认适配iPhone14 Pro以上机型,无需另外操作,若不是灵动岛机型,则是默认居中,还支持top及bottom。更多详细参数请看一个优雅的Swift弹窗组件-JFPopup

Toast:


//默认仅文案

JFPopupView.popup.toast(hit: "默认toast,支持灵动岛")

//带logo ,内置success or fail

JFPopupView.popup.toast(hit: "支付成功", icon: .success)

JFPopupView.popup.toast(hit: "支付失败", icon: .fail)

//自定义logo

JFPopupView.popup.toast(hit: "自定义", icon: .imageName(name: "face"))

Loading:


DispatchQueue.main.async {

JFPopupView.popup.loading()

}

DispatchQueue.main.asyncAfter(deadline: .now() + 3) {

JFPopupView.popup.hideLoading()

JFPopupView.popup.toast(hit: "刷新成功")

}

适配灵动岛具体过程

由于苹果官方已经说了要在下半年推出的ActivityKit才会加入适配灵动岛的Api。所以目前并没有官方的api可以给我们适配。所以只能硬着头皮自己去思考适配方案了。

- 首先要知道灵动岛的区域大小

我们用最笨的方法,直接给模拟器截个图自己去算大小。至少能还原99%的效果了。如图得知,灵动岛的区域大概是宽120dt,高34dp,那半圆圆角自然为17dt。居顶部大约10dp,以及在屏幕居中。有了这些信息,我们自然就能模拟灵动岛的放大缩小转场效果了。

- ToastView新增灵动岛动画

我们在原先基础上新增灵动岛动画枚举


public enum JFToastPosition {

case center

case top

case bottom

case dynamicIsland //新增灵动岛位置动画

}

重新实现下present 及 dismiss协议的转场动画代码如下

展开:


let originSize = contianerView.jf.size

if config.toastPosition == .dynamicIsland {

contianerView.jf_size = CGSize(width: 120, height: 34)

contianerView.center = CGPoint(x: CGSize.jf.screenSize().width / 2, y: 27)

}

let updateV = {

contianerView.center = CGPoint(x: CGSize.jf.screenSize().width / 2, y: CGSize.jf.screenSize().height / 2)

if config.toastPosition == .top {

contianerView.jf_top = CGFloat.jf.navigationBarHeight() + 15

} else if config.toastPosition == .bottom {

contianerView.jf_bottom = CGSize.jf.screenHeight() - CGFloat.jf.safeAreaBottomHeight() - 15

} else if config.toastPosition == .dynamicIsland {

contianerView.jf_size = originSize

contianerView.center = CGPoint(x: CGSize.jf.screenSize().width / 2, y: originSize.height / 2 + 10)

}

contianerView.layoutIfNeeded()

}

guard config.withoutAnimation == false else {

updateV()

transitonContext?.completeTransition(true)

completion?(true)

return

}

if config.toastPosition == .dynamicIsland {

UIView.animate(withDuration: 0.25) {

updateV()

} completion: { finished in

transitonContext?.completeTransition(true)

completion?(finished)

}

return

}

消失:


UIView.animate(withDuration: 0.25, animations: {

if config.toastPosition == .dynamicIsland {

contianerView?.layer.cornerRadius = 17

contianerView?.jf_size = CGSize(width: 120, height: 34)

contianerView?.center = CGPoint(x: CGSize.jf.screenSize().width / 2, y: 27)

}

contianerView?.subviews.forEach({ v in

if config.toastPosition == .dynamicIsland {

v.isHidden = true

} else {

v.alpha = 0

}

})

contianerView?.alpha = 0

}) { (finished) in

transitonContext?.completeTransition(true)

completion?(finished)

}

末尾

以上即是我JFPopup内置组件JFToastView适配灵动岛动画的全过程,假如下半年苹果更新了Api我也会第一时间重新适配。