如何将滑出/汉堡菜单合并到现有的标签栏控制器中

时间:2018-10-15 20:19:31

标签: ios uitabbarcontroller swift4

所以我想创建一个滑出菜单。我希望在触摸选项卡栏项目/按钮时菜单会滑出。 到目前为止,我已经创建了具有4个不同标签栏按钮的标签视图控制器。每个按钮都指向一个不同的视图控制器,并且每个视图控制器都被分隔到各自的情节提要中。

我尝试将侧面菜单的UIViewController添加到已经建立为UITabBarController的类中,但出现错误:

从类'UITabBarController'和'UIViewController'的多重继承。

有没有办法解决这个问题?

感谢所有帮助

3 个答案:

答案 0 :(得分:2)

应用程序倾向于具有不能嵌入到View内部的顶级容器,例如Tab Bar Controller。这里的方法是将主体和左侧菜单包装在Container View中。现在,这两个元素都可以放在包装器View Controller中。

Container Storyboard

Scroll View用于通过在屏幕上/屏幕外移动左侧菜单来模拟打开和关闭菜单。

包含View Controller

View Controller拖放到情节提要中。这是您进入应用程序的入口。 选中Is Initial View Controller框。 将Simulated SizeFixed更改为Freeform。将“宽度”设置为568,以便我们可以并排放置菜单和选项卡栏。 创建一个新的Swift文件,并将该View Controller的类设置为ContainerVC。

import UIKit
class ContainerVC : UIViewController {
}

滚动视图

在Container View Controller内部,放入Scroll View并在各个方向添加约束。

选中Scrolling Enabled框。这使您可以平移屏幕以滑动菜单。如果您的应用程序使用水平滚动元素,则可能需要禁用此功能。

选中Paging Enabled框。这会将菜单锁定为打开或关闭状态。

取消选中Bounces的复选框。您真的不想滚动到标签栏控制器的右边缘。

将IBOutlet连接到ContainerVC:

@IBOutlet weak var scrollView: UIScrollView!

左容器

左边的容器保存菜单,并且不完全是屏幕的整个宽度。

Container View拖到Scroll View的左侧。

在包含Scroll View的顶部,左侧和右侧添加约束。

将宽度硬编码为260。

使用ContainerVC的嵌入式视图为Equal height添加约束。注意:请勿将高度限制在“滚动视图”中。 删除View Controller随附的嵌入式Container View

将新的Table View Controller(根据您的需要按任何视图)拖放到情节提要中,然后使用嵌入的segue进行连接。

右侧容器

正确的容器包含应用程序的主体,即Tab Bar Controller

将第二个Container View拖到Scroll View的右侧。

将约束添加到包含Scroll View的顶部,右侧和底部。水平连接到先前创建的左侧“容器视图”。

Equal heightEqual width都约束到ContainerVC的嵌入式视图。

注意:请勿将其限制在“滚动视图”中。

setConstraintsFromContainers

同样,删除Container View附带的嵌入式View Controller。相反,请为embed创建一个Tab Bar Controller序列。

要在两个容器之间创建一些视觉上的分隔,请在“正确的容器”中添加一个Runtime Attribute。添加layer.shadowOpacity和多个0.8

标签

将每个标签嵌入Navigation Controller中。这样可以为您提供免费的Navigation Bar

Bar Button Item拖到每个Navigation Bar的左上角。

IBAction连接到每个控制器。这些将触发Notification向曾祖父ContainerVC切换菜单。

@IBAction func toggleMenu(sender: AnyObject) {
  NotificationCenter.default().post(name: Notification.Name("toggleMenu"), object: nil)
}

最后将以下代码添加到ContainerVC中:

class ContainerVC : UIViewController {

    // This value matches the left menu's width in the Storyboard
    let leftMenuWidth:CGFloat = 260

    // Need a handle to the scrollView to open and close the menu
    @IBOutlet weak var scrollView: UIScrollView!

    override func viewDidLoad() {

        // Initially close menu programmatically.  This needs to be done on the main thread initially in order to work.
        DispatchQueue.main.async() {
            self.closeMenu(animated: false)
        }

        // Tab bar controller's child pages have a top-left button toggles the menu
        NotificationCenter.default.addObserver(self, selector: #selector(ContainerVC.toggleMenu), name: NSNotification.Name(rawValue: "toggleMenu"), object: nil)

        NotificationCenter.default.addObserver(self, selector: #selector(ContainerVC.closeMenuViaNotification), name: NSNotification.Name(rawValue: "closeMenuViaNotification"), object: nil)

        // Close the menu when the device rotates
        NotificationCenter.default.addObserver(self, selector: #selector(ContainerVC.rotated), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

        // LeftMenu sends openModalWindow
        NotificationCenter.default.addObserver(self, selector: #selector(ContainerVC.openModalWindow), name: NSNotification.Name(rawValue: "openModalWindow"), object: nil)

    }

    // Cleanup notifications added in viewDidLoad
    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc func openModalWindow() {
        performSegue(withIdentifier: "openModalWindow", sender: nil)
    }

    @objc func toggleMenu() {
        scrollView.contentOffset.x == 0  ? closeMenu() : openMenu()
    }

    // This wrapper function is necessary because closeMenu params do not match up with Notification
    @objc func closeMenuViaNotification(){
        closeMenu()
    }

    // Use scrollview content offset-x to slide the menu.
    func closeMenu(animated:Bool = true){
        scrollView.setContentOffset(CGPoint(x: leftMenuWidth, y: 0), animated: animated)
    }

    // Open is the natural state of the menu because of how the storyboard is setup.
    func openMenu(){
        print("opening menu")
        scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
    }

    @objc func rotated(){
        if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
            DispatchQueue.main.async() {
                print("closing menu on rotate")
                self.closeMenu()
            }
        }
    }
}

extension ContainerVC : UIScrollViewDelegate {
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("scrollView.contentOffset.x:: \(scrollView.contentOffset.x)")
    }

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        scrollView.isPagingEnabled = true
    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        scrollView.isPagingEnabled = false
    }
}

该代码完成以下操作:

  • 收听“ toggleMenu”通知
  • 通过打开或关闭基于 当前contentOffset.x
  • 通过更改contentOffset-x打开和关闭菜单。

希望您有一个简单的左侧滑动菜单,可以在其上构建应用程序的其余部分。

答案 1 :(得分:0)

请勿使用UITabBarController,而应使用一个视图控制器和按钮操作来管理选项卡控件。

答案 2 :(得分:0)

@Ajo 答案是正确的。在容器视图中嵌入汉堡包和标签栏。

您可以从下面的链接中找到完整的源代码

source code link

我也找到了详细的教程 tutorial link

使用初始视图控制器作为容器视图。 使用滑入/滑出通知

storyboard image