ios - 幻灯片菜单(汉堡菜单)iOS Swift

标签 ios swift menu

我需要创建从右到左过渡的幻灯片菜单,我已成功创建它,但是当我按菜单图标时显示幻灯片菜单 without animation !!我不能hide it按幻灯片菜单之外的按钮!!

如何创建从右到左过渡的动画? 在幻灯片菜单之外按时如何设置隐藏?

SlideInMenu 类:

import UIKit

class SlideInMenu: NSObject, UIViewControllerAnimatedTransitioning {

var isPresenting = false
let dimmingView = UIView()

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    return 3
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

    guard let toVC = transitionContext.viewController(forKey: .to),
        let fromVC = transitionContext.viewController(forKey: .from) else { return }

    let containerView = transitionContext.containerView
    let finalWidth = toVC.view.bounds.width * 0.8375
    let finalHeight = toVC.view.bounds.height

    if (isPresenting) {
        dimmingView.backgroundColor = UIColor(red: 32/255, green: 70/255, blue: 86/255, alpha: 0.8)
        containerView.addSubview(dimmingView)
        dimmingView.frame = containerView.bounds
        containerView.addSubview(toVC.view)
        toVC.view.frame = CGRect(x: -(finalWidth - toVC.view.bounds.width), y: 0, width: finalWidth, height: finalHeight)
    }

    let transform = {
        self.dimmingView.alpha = 0.9
        toVC.view.transform = CGAffineTransform(translationX: finalWidth - toVC.view.bounds.width, y: 0)
    }

    let identiy = {
        self.dimmingView.alpha = 0.9
        fromVC.view.transform = .identity
    }

    let duration = transitionDuration(using: transitionContext)
    let isCancelled = transitionContext.transitionWasCancelled

    UIView.animate(withDuration: duration, animations: {
        self.isPresenting ? transform () : identiy()
    }) { (_) in
        transitionContext.completeTransition(!isCancelled)
    }
 }

}

ViewController 类:

import UIKit

class ViewController: UIViewController {

let transition = SlideInMenu()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.

}

@IBAction func tapBtnMenu(_ sender: Any) {
    guard let menuVC = storyboard?.instantiateViewController(withIdentifier: "MenuViewController") else { return }
    menuVC.modalPresentationStyle = .overCurrentContext
    menuVC.transitioningDelegate = self
    present(menuVC, animated: true)
 }

}

extension ViewController: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        transition.isPresenting = true
        return transition
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    transition.isPresenting = false
    return transition
 }
}

最佳答案

输出:

enter image description here

您可以复制粘贴以下代码以查看实际操作。

说明:

View Controller :

  1. 在 ViewController 中添加一个用于打开侧边栏的按钮
  2. 它向委托(delegate)进行确认,以便它可以监听侧边栏何时打开、何时关闭以及用户是否选择了任何选项

侧边栏:

  1. 这完成了主要工作的繁重工作。

  2. 黑色透明View是关闭按钮,另一部分是NavigationViewController

  3. 在 show 方法中,关闭按钮设置为 60 宽度,并且导航 vc View 使用约束在其旁边对齐。注意navigation vc的x位置设置为screenwidth的宽度。

  4. 在 UIView.animate 的帮助下,X 位置设置为 0,从而从右到左进行动画处理。

  5. 单击任何选项或单击 nav vc 外部(即关闭按钮)时,将调用 closeSidebar 方法。

  6. closeSidebar 再次有一个 UIViw.animate block ,它再次将 X 位置设置为屏幕宽度,从而从左到右动画。当它完成时,它将从 View 层次结构中删除。

ViewController 正在该委托(delegate)的 sidebarDidClose 方法中确认 SidebarDelegate,您将了解侧边栏已关闭以及用户是否选择了任何选项.

View Controller :

import UIKit

class ViewController: UIViewController {

    public let button: UIButton = {
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.setTitle("Open Menu", for: .normal)
        v.setTitleColor(.blue, for: .normal)
        v.setTitleColor(UIColor.blue.withAlphaComponent(0.5), for: .highlighted)
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(button)
        let constrains = [
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ]

        NSLayoutConstraint.activate(constrains)
        button.addTarget(self, action: #selector(didSelect(_:)), for: .touchUpInside)
    }

    @objc func didSelect(_ sender: UIButton){
        SidebarLauncher.init(delegate: self).show()
    }


}

extension ViewController: SidebarDelegate{
    func sidbarDidOpen() {

    }

    func sidebarDidClose(with item: NavigationModel?) {
        guard let item = item else {return}
        switch item.type {
        case .home:
            print("called home")
        case .star:
            print("called star")
        case .about:
            print("called about")
        case .facebook:
            print("called facebook")
        case .instagram:
            print("instagram")
        }
    }
}

侧边栏:

import UIKit
protocol SidebarDelegate {
    func sidbarDidOpen()
    func sidebarDidClose(with item: NavigationModel?)
}
class SidebarLauncher: NSObject{

    var view: UIView?
    var delegate: SidebarDelegate?
    var vc: NavigationViewController?
    init(delegate: SidebarDelegate) {
        super.init()
        self.delegate = delegate
    }

    public let closeButton: UIButton = {
        let v = UIButton()
        v.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    func show(){
        let bounds = UIScreen.main.bounds
        let v = UIView(frame: CGRect(x: bounds.width, y: 0, width: bounds.width, height: bounds.height))
        v.backgroundColor = .clear
        let vc = NavigationViewController()
        vc.delegate = self
        v.addSubview(vc.view)
        v.addSubview(closeButton)
        vc.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([

            closeButton.topAnchor.constraint(equalTo: v.topAnchor),
            closeButton.leadingAnchor.constraint(equalTo: v.leadingAnchor),
            closeButton.bottomAnchor.constraint(equalTo: v.bottomAnchor),
            closeButton.widthAnchor.constraint(equalToConstant: 60),

            vc.view.topAnchor.constraint(equalTo: v.topAnchor),
            vc.view.leadingAnchor.constraint(equalTo: closeButton.trailingAnchor),
            vc.view.bottomAnchor.constraint(equalTo: v.bottomAnchor),
            vc.view.trailingAnchor.constraint(equalTo: v.trailingAnchor, constant: 0)
            ])

        closeButton.addTarget(self, action: #selector(close(_:)), for: .touchUpInside)
        self.view = v
        self.vc = vc
        UIApplication.shared.keyWindow?.addSubview(v)

        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
            self.view?.frame = CGRect(x: 0, y: 0, width: self.view!.frame.width, height: self.view!.frame.height)
            self.view?.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        }, completion: {completed in
            self.delegate?.sidbarDidOpen()
        })

    }

    @objc func close(_ sender: UIButton){
        closeSidebar(option: nil)
    }
    func closeSidebar(option: NavigationModel?){
        UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
            if let view = self.view{
                view.frame = CGRect(x: view.frame.width, y: 0, width: view.frame.width, height: view.frame.height)
                self.view?.backgroundColor = .clear

            }
        }, completion: {completed in
            self.view?.removeFromSuperview()
            self.view = nil
            self.vc = nil
            self.delegate?.sidebarDidClose(with: option)
        })
    }

}
extension SidebarLauncher: NavigationDelegate{
    func navigation(didSelect: NavigationModel?) {
        closeSidebar(option: didSelect)
    }
}

为了代码的完整性

导航 View

import Foundation
import UIKit
class NavigationView: UIView{

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit(){
        addSubview(mainView)
        mainView.addSubview(collectionView)
        setConstraints()
    }
    func setConstraints() {
        let constraints = [
            mainView.topAnchor.constraint(equalTo: topAnchor),
            mainView.leadingAnchor.constraint(equalTo: leadingAnchor),
            mainView.bottomAnchor.constraint(equalTo: bottomAnchor),
            mainView.trailingAnchor.constraint(equalTo: trailingAnchor),

            collectionView.topAnchor.constraint(equalTo: mainView.topAnchor),
            collectionView.leadingAnchor.constraint(equalTo: mainView.leadingAnchor),
            collectionView.bottomAnchor.constraint(equalTo: mainView.bottomAnchor),
            collectionView.trailingAnchor.constraint(equalTo: mainView.trailingAnchor)
        ]
        NSLayoutConstraint.activate(constraints)

    }

    public let collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .vertical
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        let v = UICollectionView(frame: .zero, collectionViewLayout: layout)
        v.translatesAutoresizingMaskIntoConstraints = false
        v.register(NavigationCell.self, forCellWithReuseIdentifier: "NavigationCell")
        v.backgroundColor = .clear
        return v
    }()

    public let mainView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .blue
        return v
    }()

}

NavigationViewController 加上导航模型

import Foundation
import UIKit
protocol NavigationDelegate{
    func navigation(didSelect: NavigationModel?)
}

enum NavigationTypes{
    case home,star,about,facebook,instagram
}

struct NavigationModel {
    let name: String
    let type: NavigationTypes
}

class NavigationViewController: UIViewController{

    var myView: NavigationView {return view as! NavigationView}
    unowned var collectionView: UICollectionView {return myView.collectionView}
    var delegate: NavigationDelegate?
    var list = [NavigationModel]()

    override func loadView() {
        view = NavigationView()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        setupList()
        collectionView.delegate = self
        collectionView.dataSource = self
    }

    func setupList(){
        list.append(NavigationModel(name: "Home", type: .home))
        list.append(NavigationModel(name: "Star", type: .star))
        list.append(NavigationModel(name: "About", type: .about))
        list.append(NavigationModel(name: "Facebook", type: .facebook))
        list.append(NavigationModel(name: "Instagram", type: .instagram))
    }
}


extension NavigationViewController: UICollectionViewDataSource{
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return list.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "NavigationCell", for: indexPath) as! NavigationCell
        let model = list[indexPath.item]
        cell.label.text = model.name
        return cell
    }
}

extension NavigationViewController: UICollectionViewDelegate{

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        delegate?.navigation(didSelect: list[indexPath.item])
    }
}

extension NavigationViewController: UICollectionViewDelegateFlowLayout{
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: 45)
    }
}

导航单元

import Foundation
import UIKit

class NavigationCell: UICollectionViewCell{

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    func commonInit(){
        [label,divider].forEach{contentView.addSubview($0)}
        setConstraints()
    }

    func setConstraints() {
        let constraints = [
            label.centerYAnchor.constraint(equalTo: centerYAnchor),
            label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),

            divider.leadingAnchor.constraint(equalTo: leadingAnchor),
            divider.bottomAnchor.constraint(equalTo: bottomAnchor),
            divider.trailingAnchor.constraint(equalTo: trailingAnchor),
            divider.heightAnchor.constraint(equalToConstant: 1)
        ]
        NSLayoutConstraint.activate(constraints)
    }

    public let label: UILabel = {
        let v = UILabel()
        v.text = "Label Text"
        v.textColor = .white
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    public let divider: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .white
        return v
    }()
}

关于ios - 幻灯片菜单(汉堡菜单)iOS Swift,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57408345/

相关文章:

html - 菜单按钮帮助

ios - 在iPad上的Playground应用中创建按钮

ios - 具有多个单元模板的 CollectionView(使用未声明的类型,<typeName> 对于此上下文中的类型查找是不明确的)

ios - Swift 5 - 中间 View Controller 短暂出现 - 应该删除吗?

ios - 我可以在主从应用程序的每个部分中使用不同数量的行吗?

css - 似乎无法将 Logo 与导航栏对齐

ios - 在 iOS7 上将 CIFilter 应用于 SKEffectNode

ios - XCode 6.3 在归档应用程序时崩溃

ios - 使用 UISegmentedControl 更改 UITableView 的数据源(就像 Twitter 那样)

jquery - 在一条线上使用 Css 透明箭头