Tableview 上下文菜单:从行到预览视图控制器的自定义动画
Tableview context menu: custom animation from row to a preview view controller
我已经为 tableview 实现了特定于行的上下文菜单,其中包括一个简单的预览视图控制器,如下所示。
我想实现一个过渡或动画,以便表格视图中的图标 (a UIImageView
) 动画到与预览视图控制器中的图标相同的位置。
但是,查看 documentation(特别是 UITargetedPreview
和 UIPreviewTarget
),我不知道如何在具有多个表视图的上下文中创建这样的动画行。
预览视图控制器是根据行使用数据模型中的图标生成的,如下所示:
class DrugPreviewViewController: UIViewController {
private let imageView = UIImageView()
override func loadView() {
view = imageView
}
init(item: CustomItem) {
super.init(nibName: nil, bundle: nil)
// Set up image view
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.backgroundColor = .clear
imageView.image = UIImage(named: item.icon ?? "defaultIcon")?.withRenderingMode(.alwaysTemplate).tinted(with: Color.colors[item.iconBackgroundColor ?? 0])
// Preview to have the same aspect ratio as the image
preferredContentSize = UIImage(named: item.icon ?? "defaultIcon")!.size
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
缩短的上下文菜单代码如下所示:
@available(iOS 13.0, *)
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
// Selected Drug and notes
let selectedItem: CustomItem = dataArray[indexPath.row]
var notes = selectedItem.notes ?? ""
var titleString = selectedItem.name
// Code for menu items and actions
// Defines actions array
// Omitted for brevity
let actionProvider: UIContextMenuActionProvider = { _ in
return UIMenu(title: titleString, children: actions)
}
return UIContextMenuConfiguration(identifier: indexPath as NSCopying, previewProvider: {
return CustomPreviewViewController(item: selectedItem)
}, actionProvider: actionProvider)
}
我认为实施协议 UIViewControllerAnimatedTransitioning
可以让您朝着正确的方向前进。首先有点复杂。这是一个如何做到这一点的例子
,我找到了原生 Cocoa 解决方案,而无需自己进行详细转换
当您在 table 视图中实现上下文菜单时,似乎所有标准的单视图上下文菜单 UIContextMenuInteractionDelegate
方法都具有等效的 UITableViewDelegate
方法。
我设法实现了如下所示的结果。它没有将图标动画化到中间的预览中,但它足够接近我想要的并且看起来比我以前的要好得多。
为此,我需要在上面的代码中将 previewProvider
设为 nil,return 是上下文菜单:
return UIContextMenuConfiguration(identifier: indexPath as NSCopying, previewProvider: nil, actionProvider: actionProvider)
然后我不得不实现这两个委托方法 - 都调用相同的代码块 return a UITargetedPreview
:
@available(iOS 13.0, *)
override func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
return makeTargetedPreview(for: configuration)
}
@available(iOS 13.0, *)
override func tableView(_ tableView: UITableView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
return makeTargetedPreview(for: configuration)
}
@available(iOS 13.0, *)
private func makeTargetedPreview(for configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
guard let indexPath = configuration.identifier as? IndexPath else { return nil }
// Get the cell for the index of the model
guard let cell = tableView.cellForRow(at: indexPath) as? DrugTableViewCell else { return nil }
// Set parameters to a circular mask and clear background
let parameters = UIPreviewParameters()
parameters.backgroundColor = .clear
parameters.visiblePath = UIBezierPath(ovalIn: cell.iconView.bounds)
// Return a targeted preview using our cell previewView and parameters
return UITargetedPreview(view: cell.iconView, parameters: parameters)
}
cell.iconView
是我自定义的 UIImageView
UITableViewCell
.
请注意,为了安全起见,如果 table 数据在显示菜单时发生变化,Kyle 建议使用实际数据模型(通过索引路径访问)来获取所需的数据预览(而不是单元格)。有关详细信息,请参阅他的文章。
我已经为 tableview 实现了特定于行的上下文菜单,其中包括一个简单的预览视图控制器,如下所示。
我想实现一个过渡或动画,以便表格视图中的图标 (a UIImageView
) 动画到与预览视图控制器中的图标相同的位置。
但是,查看 documentation(特别是 UITargetedPreview
和 UIPreviewTarget
),我不知道如何在具有多个表视图的上下文中创建这样的动画行。
预览视图控制器是根据行使用数据模型中的图标生成的,如下所示:
class DrugPreviewViewController: UIViewController {
private let imageView = UIImageView()
override func loadView() {
view = imageView
}
init(item: CustomItem) {
super.init(nibName: nil, bundle: nil)
// Set up image view
imageView.clipsToBounds = true
imageView.contentMode = .scaleAspectFill
imageView.backgroundColor = .clear
imageView.image = UIImage(named: item.icon ?? "defaultIcon")?.withRenderingMode(.alwaysTemplate).tinted(with: Color.colors[item.iconBackgroundColor ?? 0])
// Preview to have the same aspect ratio as the image
preferredContentSize = UIImage(named: item.icon ?? "defaultIcon")!.size
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
缩短的上下文菜单代码如下所示:
@available(iOS 13.0, *)
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
// Selected Drug and notes
let selectedItem: CustomItem = dataArray[indexPath.row]
var notes = selectedItem.notes ?? ""
var titleString = selectedItem.name
// Code for menu items and actions
// Defines actions array
// Omitted for brevity
let actionProvider: UIContextMenuActionProvider = { _ in
return UIMenu(title: titleString, children: actions)
}
return UIContextMenuConfiguration(identifier: indexPath as NSCopying, previewProvider: {
return CustomPreviewViewController(item: selectedItem)
}, actionProvider: actionProvider)
}
我认为实施协议 UIViewControllerAnimatedTransitioning
可以让您朝着正确的方向前进。首先有点复杂。这是一个如何做到这一点的例子
当您在 table 视图中实现上下文菜单时,似乎所有标准的单视图上下文菜单 UIContextMenuInteractionDelegate
方法都具有等效的 UITableViewDelegate
方法。
我设法实现了如下所示的结果。它没有将图标动画化到中间的预览中,但它足够接近我想要的并且看起来比我以前的要好得多。
为此,我需要在上面的代码中将 previewProvider
设为 nil,return 是上下文菜单:
return UIContextMenuConfiguration(identifier: indexPath as NSCopying, previewProvider: nil, actionProvider: actionProvider)
然后我不得不实现这两个委托方法 - 都调用相同的代码块 return a UITargetedPreview
:
@available(iOS 13.0, *)
override func tableView(_ tableView: UITableView, previewForHighlightingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
return makeTargetedPreview(for: configuration)
}
@available(iOS 13.0, *)
override func tableView(_ tableView: UITableView, previewForDismissingContextMenuWithConfiguration configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
return makeTargetedPreview(for: configuration)
}
@available(iOS 13.0, *)
private func makeTargetedPreview(for configuration: UIContextMenuConfiguration) -> UITargetedPreview? {
guard let indexPath = configuration.identifier as? IndexPath else { return nil }
// Get the cell for the index of the model
guard let cell = tableView.cellForRow(at: indexPath) as? DrugTableViewCell else { return nil }
// Set parameters to a circular mask and clear background
let parameters = UIPreviewParameters()
parameters.backgroundColor = .clear
parameters.visiblePath = UIBezierPath(ovalIn: cell.iconView.bounds)
// Return a targeted preview using our cell previewView and parameters
return UITargetedPreview(view: cell.iconView, parameters: parameters)
}
cell.iconView
是我自定义的 UIImageView
UITableViewCell
.
请注意,为了安全起见,如果 table 数据在显示菜单时发生变化,Kyle 建议使用实际数据模型(通过索引路径访问)来获取所需的数据预览(而不是单元格)。有关详细信息,请参阅他的文章。