我只是看着Facebook和/或松弛的上下文菜单,想在我的应用中创建类似的内容。
我尝试了两种方法。
第一种方法。拥有一个in View Table视图,并从底部滑动它以创建动画,就像在视图上进行动画处理一样。但这是因为导航控制器和Tab栏控制器没有隐藏,并且在黑色(Alpha 30%)上方显示了白色补丁。
我尝试的第二种方法是在当前视图控制器上显示一个新的View控制器,并显示为Modal presentation
。
let vc = CustomActionTableViewController(nibName: "CustomActionTableViewController", bundle: nil)
vc.modalPresentationStyle = .overFullScreen
self.present(vc, animated: false, completion: nil)
这可以,但是该方法太慢,因为我必须处理大量通知(将选定的索引发送到主视图然后执行操作)。太慢了。
任何人都可以帮助我改善实施方式,以便获得类似于Facebook的操作表,该操作表非常流畅且非常流畅
答案 0 :(得分:1)
检查此示例:Bottom pop Up 目前,我正在我的应用程序中使用它,并且可以正常工作。
答案 1 :(得分:1)
自从您提到Slack以来,他们实际上已经公开了其底层工作表PanModal
的源代码。
答案 2 :(得分:1)
使用 UIPresentationController 和 UIPanGestureRecognizer
1- 创建 BottomMenu 演示控制器,它将处理您的视图控制器的高度和模糊
class BottomMenuPresentationController: UIPresentationController {
// MARK: - Properties
var blurEffectView: UIVisualEffectView?
var tapGestureRecognizer = UITapGestureRecognizer()
private var topHeightRatio: Float
private var bottomHeightRatio: Float
init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, topHeightRatio: Float, bottomHeightRatio: Float) {
let blurEffect = UIBlurEffect(style: .systemThickMaterialDark)
blurEffectView = UIVisualEffectView(effect: blurEffect)
self.topHeightRatio = topHeightRatio
self.bottomHeightRatio = bottomHeightRatio
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
blurEffectView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissController))
self.blurEffectView?.isUserInteractionEnabled = true
self.blurEffectView?.addGestureRecognizer(tapGestureRecognizer)
}
override var frameOfPresentedViewInContainerView: CGRect {
CGRect(origin: CGPoint(x: 0, y: self.containerView!.frame.height * CGFloat(topHeightRatio)),
size: CGSize(width: self.containerView!.frame.width, height: self.containerView!.frame.height * CGFloat(bottomHeightRatio)))
}
override func presentationTransitionWillBegin() {
self.blurEffectView?.alpha = 0
if let blurEffectView = blurEffectView {
self.containerView?.addSubview(blurEffectView)
}
self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (_) in
self.blurEffectView?.alpha = 0.66
}, completion: { (_) in })
}
override func dismissalTransitionWillBegin() {
self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (_) in
self.blurEffectView?.alpha = 0
}, completion: { (_) in
self.blurEffectView?.removeFromSuperview()
})
}
override func containerViewWillLayoutSubviews() {
super.containerViewWillLayoutSubviews()
presentedView!.roundCorners([.topLeft, .topRight], radius: 14)
}
override func containerViewDidLayoutSubviews() {
super.containerViewDidLayoutSubviews()
presentedView?.frame = frameOfPresentedViewInContainerView
blurEffectView?.frame = containerView!.bounds
}
@objc func dismissController() {
self.presentedViewController.dismiss(animated: true, completion: nil)
}
}
2- 创建您的 ViewController
class BottomMenuVC: UIViewController {
// MARK: - Instances
var hasSetPointOrigin = false
var pointOrigin: CGPoint?
// MARK: - Properties
let topDarkLine: UIView = {
let view = UIView()
view.backgroundColor = UIColor(hexString: "#E1E1E1")
view.layer.cornerRadius = 2
return view
}()
let cancelButn: UIButton = {
let button = UIButton(type: .custom)
button.setAttributedTitle(NSAttributedString(string: "Cancel", attributes: [NSAttributedString.Key.font: UIFont.LatoMedium(size: 17),
NSAttributedString.Key.foregroundColor: UIColor(hexString: "#515151")
]), for: .normal)
button.backgroundColor = UIColor(hexString: "#F1F3F4")
button.layer.cornerRadius = 5.0
button.addTarget(self, action: #selector(cancelButnPressed), for: .touchUpInside)
return button
}()
// MARK: - viewLifeCycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.isUserInteractionEnabled = true
setupMenuView()
}
override func viewDidLayoutSubviews() {
if !hasSetPointOrigin {
hasSetPointOrigin = true
pointOrigin = self.view.frame.origin
}
}
// MARK: - SetupView
func setupMenuView() {
self.view.addSubview(topDarkLine)
self.view.addSubview(cancelButn)
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerAction(_:)))
view.addGestureRecognizer(panGesture)
topDarkLine.constrainHeight(constant: 4)
topDarkLine.constrainWidth(constant: view.frame.size.width * 0.10)
topDarkLine.centerXInSuperview()
topDarkLine.anchor(top: view.topAnchor, leading: nil, bottom: nil, trailing: nil, padding: .init(top: 8, left: 0, bottom: 0, right: 0))
cancelButn.anchor(top:view.topAnchor, leading: view.leadingAnchor, bottom: nil, trailing: view.trailingAnchor,
padding: .init(top: 16, left: 16, bottom: 0, right: 16))
cancelButn.constrainHeight(constant: 44)
}
// MARK: - Actions
@objc func panGestureRecognizerAction(_ sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
// Not allowing the user to drag the view upward
guard translation.y >= 0 else { return }
// setting x as 0 because we don't want users to move the frame side ways!! Only want straight up or down in the y-axis
view.frame.origin = CGPoint(x: 0, y: self.pointOrigin!.y + translation.y)
if sender.state == .ended {
let dragVelocity = sender.velocity(in: view)
if dragVelocity.y >= 1300 {
// Velocity fast enough to dismiss the uiview
self.dismiss(animated: true, completion: nil)
} else {
// If the dragging isn’t too fast, resetting the view back to it’s original point
UIView.animate(withDuration: 0.3) {
self.view.frame.origin = self.pointOrigin ?? CGPoint(x: 0, y: 400)
}
}
}
}
@objc func cancelButnPressed() {
dismiss(animated: true, completion: nil)
}
}
3- 使包含将显示您的菜单的按钮的 viewController 符合 UIViewControllerTransitioningDelegate
extension viewController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
BottomMenuPresentationController(presentedViewController: presented, presenting: presenting, topHeightRatio: 0.6, bottomHeightRatio: 0.4)
}
}
4- 将过渡委托设置为 self 并展示您的自定义演示控制器
func showBottomMenu() {
let menu = BottomMenuVC()
menu.coordinator = self
menu.modalPresentationStyle = .custom
menu.transitioningDelegate = self
present(menu, animated: true, completion: nil)
}
查看这篇 PanGesture Slidable View 文章