大多数浏览器和
Developer App 均支持流媒体播放。
-
自定义 UIKit 中工作表及调整其大小
了解如何在 UIKit 中创建分层和自定义的工作表体验。我们将探索如何在 app 中构建非模态体验,以允许同时与工作表中和工作表背后的内容进行交互。我们还将带您完成工作表尺寸自定义、显示或隐藏控制柄控件以及在 app 中的弹出窗口和自定义工作表之间进行调整。为了充分了解本节内容,我们建议首先观看 WWDC19 中 9:45 开始的“iOS 13 UI 的现代化”的演示部分。
资源
相关视频
WWDC22
WWDC21
WWDC19
-
下载
♪ ♪ 哈啰! 我叫罗素 我是UIKit 团队的工程师 在iOS 13中 我们引进了 更精炼的面板外观 将它们带到了手机应用上 并加上了通用的“下拉以关闭”手势 想了解更多详情 请看视频 《让你的UI在iOS 13上更现代化》 尤其是有个环节 在9分45秒开始的部分 在iOS 15中 我们的基础建立在 面板上新增的不少自定义选项上 所以你能以前所未见的 有趣方式利用它们 我们为中型detent增加了支持 能让你创建一个 可重新调整的垂直面板 并且只会盖住半个屏幕
现在你也可以移除调光视图了 可以让你打造非模态的UI 让用户能够在桌面展示的时候 与面板后的内容互动 接下来 我会讲解几个新的视觉选项 包括如何在手机水平模式下 获得非全屏的外观 最后 我会示范如何设置UI 来在一般大小类别中 无缝地适应弹出框 以及紧凑尺寸类别中的自定义面板
为了探讨这点 我们做了一款范例应用 来制作数字明信片 在本视频中我们就用它来介绍 在每张明信片上我可以定义相片 还有文字和字体 现在 在我自定义面板之前 我得先找来一个 这个面板是个实例 来自新的UIPresentationController 次类别 叫做 UISheetPresentationController 所有自定义选项都对 这个类别的属性暴露 得到这个类别中的实例的典型方法是 在呈现它之前 在视图控制器上去读取 sheetPresentationController属性 这个方法会返回一个非空值的实例 只要视图控制器的 modalPresentationStyle 是窗体面板或页面面板 也就是默认值
从这里 你可以对实例 设置各种属性来自定义它 模式都是一样的:获取视图控制器的 popoverPresentationController 和设置它的属性 说到这里 那我们来讲detensts
什么是detents? detent是面板自然设定的高度 是由完全展开的 面板框架的比例所定义 完全展开的框架 可以在iPhone和iPad这里看到 如果你之前使用过这些面板 那你应该不陌生 我们在iOS 15上也曝光了 两个系统定义的detents 中型detent也就是面板整体 高度的一半 还有大型的detent 高度跟完全展开的面板高度一样
为了指定你要面板支持的 是哪个detent 请在定位属性上 设置想要的detent数组 这个属性的默认值就是 大型detent的数组 也就是为什么如果你们 完全不设置 你会得到标准的全高度面板
如果你们设置中型 或大型detent的数组 你会得到可调整为 中型和大型尺寸的面板 但你也可以设置为 中型detent的数组 这能让你得到一个中型高度的 且不能调整为完整高度的面板 让我们在范例app上使用它 我要从对准iOS 14 软件开发工具包的代码讲起 在标准面板中展示系统图片选择器 当一个按钮被点击时 第一个函数将被调用 它会创建了一个图像选择器 将选择器的代理 设置为self 然后呈现选择器
然后当一张图片被选中时 选择器的didFinishPicking 代理方法会被调用 然后设置选中的图片 在图像视图上 并关闭选择器
来运行它吧 当我点击照片按钮时 照片选择器涵盖了整个应用 请注意 我最近的旅行都是虚拟的 当我选择一张照片时 照片选择器会关闭 露出我在明信片中挑选的照片 但是如果我想选择 不同的图片怎么办? 我必须再次经历这整个流程 如果我能同时展示 我的照片库和我的明信片 那就太好了 通过中型detent 我可以
这是与之前相同的代码 但有一些更改 在我介绍选择器之前 我访问它的 sheetPresentationController 并将其detent设置为中和大 同样在选择器 didFinishPicking 代理回调中 我删除了 关闭照片选择器的那一行 因为现在我不希望照片 选择器在选择图片时关闭
现在当我运行它 并点击照片按钮时 我的照片库来到一半高度 我可以挑一张照片 你瞧 我可以在我的明信片上看到它 照片库还在下面 如果我想尝试不同的照片 只需轻轻一按即可 另外 因为我的detents数组 包含了大型detent 我可以拖动栏 将这面板调整到全高
另外 因为滚动视图 被滚动到了顶部 卷动滚动视图 也会将面板展开来 对于像系统分享面板这样的动作面板 这是一个很棒的功能 可以在列表底部 逐步显示更高级的操作 但是对于这个照片库范例 我可能更喜欢滚动 而不是展开面板 这样明信片才会一直是可见的 除非我通过从栏中拖动 来明确调整面板的大小 为了获得这种替代行为 我只需要设置一个额外的属性: PrefersScrollingExpands- WhenScrolledToEdge 默认情况下 该属性为true 因此将其设置为false 可防止滚动扩展面板
现在照片选择器 在滚动时不会调整大小 但我仍然可以拖动栏 来更好地查看我的照片库
但是现在当我点击一张照片时 并没有明显看到发生了什么 这与之前的行为相反 点击照片会关闭照片选择器 清楚地表明我的选择已收到
我想要的是当我点击照片时 将面板调整到中等detent 既能表明我的选择已收到 又使我的选择在明信片中可见 我可以通过以编程方式更改 选定的detent来实现这一点 所以如果我回到每当点击照片时 调用的图像选择器代理方法 我可以在这里添加一些代码来获取 sheetPresentationController 并设置 selectedDetentIdentifier 到中等 来试试这个吧 请注意我点击照片时的转场
哇! 那个转场太快了 我眉毛差点掉光了 它实际上根本没有动画 我可以通过包装属性的设置 轻松地为这个转场设置动画 在sheet.animateChanges模块中 如果需要 这将使用标准动画曲线 将面板动画化到中等detent 并为堆栈中的其他面板设置动画 例如根部面板按比例放大
奶油般顺滑 另一项好处是删除调光视图 以全彩色显示所选照片 要做到这一点 还有一个属性要查看 叫做smallestUndimmed- DetentIdentifier 默认情况下 该属性为空值 这意味着所有的detent都变暗了 但如果你想去除调光 就把它设置为你不希望变暗的 最小detent的标识符 在这种情况下 我会设置为中等
请注意 当我呼出选择器时 中等的detent没有变暗 你看! 但是如果我调整到大的detent 调光仍然会淡入
不仅仅是视觉上去除调光 此属性还允许你 打造高级非模态的体验 因为我现在不仅可以与 面板中的内容进行交互 但也有面板之外的内容
至于字体选择器就更深奥了 在那里我构建了一个UI 允许我在 字体选择器启动时选择一系列文本 将字体应用于该范围 调整我的选择 并再次应用字体 请下载范例应用程序 以了解有关实现的详细信息
另外值得注意的是 中等高度的面板 支持自动键盘避让 所以如果我在这里搜索字体 面板会自动 增长以适应键盘 当键盘关闭时 工作表会自动向下折叠
所以有很多关于detent的信息 但现在我想把注意力 转向其他一些有关面板的、新的 视觉定制选项 从iPhone上横向面版的 新可选外观开始说起 在iOS 13中 我们将所有工作表 横向全屏显示 现在我们提供了一种替代外观 就是面板仅仅是附加到屏幕上 在它们的底部边缘
要获得这个新外观 只需设置 prefersEdgeAttached- InCompactHeight为true 现在只要设置它就会始终为你提供 与安全区域一样宽的面板 如果你想要一个宽度遵循 presentedViewControllers preferredContentSize的面板 设置widthFollows- PreferredContentSize- WhenEdgeAttached设为true 这将使面板的默认宽度变窄 并且你可以设置 preferredContentSize 以进一步自定义此宽度 除了这些属性 如果你愿意 还有一个属性可以显示抓取器 抓取器通常不是必需的 但在面板的可调整性 不太明显的情况下 比如当滚动无法调整面板大小时 显示抓取器可以作为 可调整大小的有用指标 现在注意面板的角落 我们曝光的另一个选项是 自定义拐角半径的能力 如果你的应用有更圆润的外观 你可能想要匹配这种审美 请注意 系统会保持堆叠的角 看起来一致 因此如果此照片选择器展开了 把根面板推回去 根面板将有更大的角落来匹配 最后 虽然可以在iPad上创建 带有detent的面板 通常反而还需要一个弹出框 来适应紧凑的面板 也许会被像detents之类的 东西被自定义 让我们在范例应用程序中 采用这种方法 要在iPad上为图像选择器 获取一个弹出框 我需要做一些小的修改 首先 我将选择器的 modalPresentationStyle 设置为popover 然后 不是抓取 sheetPresentationController 因为这现在将返回空值 由于modalPresentationStyle 不是面板 我会得到 popoverPresentationController 我将弹出框的来源 设置为我们的barButtonItem 然后我将在弹出框上 获取一个新属性 称为adaptiveSheet PresentationController 此属性返回弹出窗口将在 紧凑尺寸类中适应的工作表实例 然后我会像我之前做的那样配置它
现在当我点击照片按钮时 照片选择器出现在弹出窗口中 如果我调整场景大小 它适用于中等高度的面板 成功了! 如果我展开选择器 然后选择一张照片… 不好了! 它没有像我们之前实现的那样 自动调整到中等高度 嗯 让我们回到选择器 didFinishPicking代理方法
啊! 要获得自适应表 我需要读取 adaptiveSheet- PresentationController 在代码中的 popoverPresentationController上 也是如此 现在注意我选择照片的时候 万岁!它再次调整回中等大小
现在 我在这里讲了很多东西 可以让你轻松构建新类型的UI 使用以前不容易建造的面板 重新查看你的应用程序 了解可从中等高度的面板 或非模态体验中受益的领域 如果你今天的应用中 有任何半高自定义卡片 就用这些新增强的 UIKit面板替换它们吧 感谢你的收看 我期待看到你 用面板做出很酷的新东西
-
-
0:01 - Get a sheet
if let sheet = viewController.sheetPresentationController { // Customize the sheet } present(viewController, animated: true)
-
0:02 - Detents (large only)
if let sheet = picker.sheetPresentationController { sheet.detents = [.large()] } present(picker, animated: true)
-
0:03 - Detents (medium and large)
if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] } present(picker, animated: true)
-
0:04 - Detents (medium only)
if let sheet = picker.sheetPresentationController { sheet.detents = [.medium()] } present(picker, animated: true)
-
0:05 - Present image picker in a standard sheet
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image dismiss(animated: true) }
-
0:06 - Present at medium detent, and don’t dismiss automatically
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image }
-
0:07 - Prevent scrolling from expanding the sheet
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image }
-
0:08 - Select medium detent when a photo is picked
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image if let sheet = picker.sheetPresentationController { sheet.selectedDetentIdentifier = .medium } }
-
0:09 - Animate selection of medium detent
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image if let sheet = picker.sheetPresentationController { sheet.animateChanges { sheet.selectedDetentIdentifier = .medium } } }
-
0:10 - Remove dimming at medium detent
func showImagePicker() { let picker = PHPickerViewController() picker.delegate = self if let sheet = picker.sheetPresentationController { sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false sheet.smallestUndimmedDetentIdentifier = .medium } present(picker, animated: true) } func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image if let sheet = picker.sheetPresentationController { sheet.animateChanges { sheet.selectedDetentIdentifier = .medium } } }
-
0:11 - iPhone in landscape
if let sheet = fontPicker.sheetPresentationController { sheet.prefersEdgeAttachedInCompactHeight = true sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true } present(fontPicker, animated: true)
-
0:12 - Show a grabber
if let sheet = fontPicker.sheetPresentationController { sheet.prefersGrabberVisible = true } present(fontPicker, animated: true)
-
0:13 - Customize the corner radius
if let sheet = fontPicker.sheetPresentationController { sheet.preferredCornerRadius = 20.0 } present(fontPicker, animated: true)
-
0:14 - Adapt a popover to a customized sheet
func showImagePicker(_ sender: UIBarButtonItem) { let picker = PHPickerViewController() picker.delegate = self picker.modalPresentationStyle = .popover if let popover = picker.popoverPresentationController { popover.barButtonItem = sender let sheet = popover.adaptiveSheetPresentationController sheet.detents = [.medium(), .large()] sheet.prefersScrollingExpandsWhenScrolledToEdge = false sheet.smallestUndimmedDetentIdentifier = .medium } present(picker, animated: true) }
-
0:15 - Be consistent when using adaptiveSheetPresentationController
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // assign result to imageView.image if let sheet = picker.popoverPresentationController?.adaptiveSheetPresentationController { sheet.animateChanges { sheet.selectedDetentIdentifier = .medium } } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。