自定义ViewControllers演示
您可以在我的博客上阅读此文章
我们的应用程序中发生的大多数屏幕转换都属于以下类别:
- 模态演示
- 在导航堆栈上推送/弹出
尽管这足以完成我们需要做的大多数工作,但在某些极端情况下,我们可能希望采用一种“原生”方式少而定制化的解决方案。
对我们来说幸运的是,UIKit为这项工作提供了正确的工具。
与往常一样,我们不必离官方文档(View Controller编程指南)太远,更详细地说,我们正在寻找创建自定义演示文稿。 本指南在文档档案库中,并带有Objective-C示例,但它们仍然是您可以在其中找到的最佳内容。
阅读指南将揭示一些有趣的观点。 当将要显示视图控制器时,UIKit将执行以下操作:
- 调用过渡委托的
presentationControllerForPresentedViewController:presentingViewController:sourceViewController:
方法以检索您的自定义表示控制器。 - 向过渡委托者询问动画师和交互式动画师对象(如果有)。
- 调用演示文稿控制器的
presentationTransitionWillBegin
方法。 - 执行过渡动画。 在动画过程中,UIKit调用演示文稿控制器的
containerViewWillLayoutSubviews
和containerViewDidLayoutSubviews
方法,以便您可以根据需要调整自定义视图的布局。 - 过渡动画结束时,调用
presentationTransitionDidEnd
:方法。
因此,我们有几点可以操作和更改动画和演示样式。
展示横幅
假设我们要将文件上传到我们的服务器,并且我们希望在操作完成后通知用户:
func upload(file: File, using uploader: FileUploader) {
uploader.send(file, then: {
let banner = Banner(message: "File successfully uploaded ✅")
self.present(banner, animated: true)
})
}
现在这将以模态形式全屏显示我们的横幅,但是我们可能希望使其看起来像屏幕底部的横幅,以减少入侵。
我们将专注于自定义演示,同时使用底部的默认过渡。
首先,让我们创建我们的自定义表示控制器:
class BannerPresentationController: UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
//here we should compute the frame for the presented banner
}
override func containerViewDidLayoutSubviews() {
super.containerViewDidLayoutSubviews()
presentedView?.frame = frameOfPresentedViewInContainerView
}
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
presentedView?.layer.cornerRadius = 12
}
}
这里的关键是frameOfPresentedViewInContainerView
属性,我们将使用该属性来计算横幅的适当大小。
我们应该考虑安全区域,并在给定固定宽度(屏幕宽度减去插图)的情况下计算所需的高度。
这项工作的工具是UIView.systemLayoutSizeFitting
systemLayoutSizeFitting(targetSize:horizontalFittingPriority:verticalFittingPriority:)
方法,该方法根据视图的约束或固有内容大小来计算视图的所需大小。
我们的实现应如下所示:
override var frameOfPresentedViewInContainerView: CGRect {
let safeBounds = containerView.bounds.inset(by: containerView.safeAreaInsets)
let inset: CGFloat = 16
let targetWidth = safeBounds.width - 2*16
let targetSize = CGSize(
width: targetWidth,
height: UIView.layoutFittingCompressedSize.height
)
let targetHeight = presentedView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow).height
return CGRect(x: inset, y: yPosition, width: targetWidth, height: targetHeight)
}
拼凑在一起
现在,我们需要做的就是为Banner
分配全新的BannerPresentationController
作为演示控制器:
extension Banner: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return BannerPresentationController(presentedViewController: presented, presenting: presenting)
}
}
//when presenting the banner
banner.transitioningDelegate = banner
并指定自定义演示样式:
banner.modalPresentationStyle = .custom
结果将是这样的:
结论
尽管与响应式概念等UI开发的最新趋势相去甚远,但UIKit已表明它是一个具有多个自定义点的功能强大的框架。
还有很多事情可以做,例如更改演示动画和时间安排,但我们将其保存以备将来使用。
希望您喜欢这个小实验😊,如果喜欢,请访问我的网站:http://marcocapano.vapor.cloud/