在另一个视图中嵌入共享图标
Posted
技术标签:
【中文标题】在另一个视图中嵌入共享图标【英文标题】:Embedding Share Icons Inline Within Another View 【发布时间】:2017-06-01 00:52:57 【问题描述】:我正在尝试在我的 ios 应用中实现“分享”功能,其外观与 Google 照片 iOS 应用相似:
底部两行图标是我关心的。它们看起来与使用UIActivityViewController
时显示的几乎相同。
在 Google 照片应用中,这些图标显示在屏幕的“选择照片”部分内(例如,您仍然可以与屏幕的上部进行交互)。但是,UIActivityViewController
的文档指出“在 iPhone 和 iPod touch 上,您必须以模态方式呈现 [视图控制器]。”
这对我来说非常重要——我希望“分享”图标与我的其他内容内联显示,而不是在我的内容顶部显示模式。
是否可以使用UIActivityViewController
来实现与上面屏幕截图类似的效果?如果没有,我是否可以使用推荐的方法来实现此类功能?
【问题讨论】:
【参考方案1】:正如另一个答案中所讨论的,逆向工程UIActivityViewController
是能够达到类似效果的唯一选择。我使用 iPhone 6s - 10.3 Simulator 进行了尝试。对于 iOS 9.x 或 11.x 或更高版本,以下发现可能不准确。
A.找出UIActivityViewController
的所有内部变量
var variablesCount: UInt32 = 0
let variables = class_copyIvarList(UIActivityViewController.self, &variablesCount)
for i in 0..<variablesCount
if let variable = variables?[Int(i)]
let name = String(cString: ivar_getName(variable))
let typeEncoding = String(cString: ivar_getTypeEncoding(variable))
print("\(name)\n\(typeEncoding)\n\n")
free(variables)
第一眼就引起我注意的是(按顺序)-
_activityViewController
@"UIViewController"
_contentController
@"_UIActivityViewControllerContentController"
_activityAlertController
@"UIAlertController"
在进一步检查它们时,我发现_contentController
是我们应该寻找的那个。我们需要深入了解UICollectionViewController
的层次结构,才能到达我们想要的位置。
if let activityContentController = activityVC.value(forKeyPath: "_contentController") as? UIViewController
print("Found _contentController!")
for child in activityContentController.childViewControllers
print(String(describing: child))
if child is UICollectionViewController
print("Found UICollectionViewController!")
break
我为什么要查找UICollectionViewController
?
Debug View Hierarchy
有答案。
我尝试将此作为childViewController
添加到我的UIViewController
-
self.addChildViewController(child)
child.didMove(toParentViewController: self)
self.view.addSubview(child.view)
child.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
child.view.topAnchor.constraint(equalTo: self.view.topAnchor),
child.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
child.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
child.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
])
只有在您首先加载/呈现UIActivityViewController
时,它才会正确显示。
我能够使用无声的出席/解雇呼叫来实现这一点 -
self.present(activityVC, animated: false, completion:
self.dismiss(animated: false, completion: nil)
)
这个应用商店安全吗? - 很可能不是。
一旦您开始从UIKit
的标准组件中窃取view(s)
或viewController(s)
,行为就不稳定,并且肯定会随着即将到来的更新而中断。
Google 相册所拥有的是更高级的逆向工程的结果。 在上述实现中,您看不到 更多 选项屏幕。 UIActivityViewController
预期的层次结构已损坏。
希望这会有所帮助。
【讨论】:
虽然我希望有比这更成熟的东西,但我还是继续奖励了赏金,因为您花时间尝试了一个可行的解决方案。感谢您的努力。【参考方案2】:好的,我想到了这个,我在网络上做了一些深入的研究,但似乎没有人需要像你想要的那样修改它。以下是我猜测 Google 工程师是如何解决这个问题的:
-
他们对 UIActivityViewController 进行了逆向工程,并调用了一些私有 API 以显示相同的图标并重新排序控制器
他们使用 UIViewController 转换 API 并破解模态呈现的 UIActivityViewController 的视图层次结构,移除取消按钮并在其视图顶部添加一些自定义视图
第二个选项的指示可能是显示的“工作表”的顶部具有白色背景,而底部具有灰色背景。
不幸的是,我不太适合过渡 API,因为我即将学习它,但我的理解是您可以提供自定义对象作为过渡委托。
然后,当您呈现/关闭或推送/弹出 UIViewController 时,将调用此对象。它将获得对呈现和呈现的视图控制器的引用,您可以享受它们的两个视图。
这应该使它“安静”,易于删除和添加一些子视图、更改框架和颜色等,同时仍具有所有默认行为。
我希望这个答案可以帮助你实现你想要的。但是,请注意控制器的结构可能随时更改,因此请始终确保再次测试 beta,以免在 Apple 发布破坏您 UI 的更新时感到意外。 :)
【讨论】:
绝对有可能;但是,无论他们实际使用什么,都尊重用户的系统偏好,即共享图标的顺序以及实际启用的图标。例如,如果您通过点击“更多”图标访问“活动”屏幕(如此处所述:howtogeek.com/232489/how-to-customize-the-ios-sharing-menu),相同的偏好将反映在使用相同共享控件的其他应用程序中(例如,股票照片应用程序)。所以大概这是iOS的一个功能..但我怎么能做同样的事情呢?如果不是 UIActivityViewController,那是什么? 也许他们正在读取一些私有 API,以某种方式设法对控制器进行子类化或将其嵌入到自定义容器视图控制器中,并通过移动其子视图来破解它的视图层次结构。 可能,尽管当我尝试对UIActivityViewController
做任何事情时,除了以模态方式呈现它(如文档所述),我得到了一个运行时错误,它基本上说明了同样的事情......因此赏金:)
真的很难说,因为我们没有 UIActivityController 的洞察力。谁知道他们在哪里实施了安全检查,所以看看它是否以模态方式呈现。我的猜测是,如果你覆盖了正确的函数,你可能会“破解”它。以上是关于在另一个视图中嵌入共享图标的主要内容,如果未能解决你的问题,请参考以下文章
在 Interface Builder 中嵌套 XIB/通过引用嵌入
Swift 使用完成处理程序将视图锁定在一个控制器中以反映在另一个控制器中