Slide Sidebar Menu IOS 8 Swift [closed]

2019-01-10 02:07发布

问题:

Is there a way to implement Slide Sidebar Menu (Like Facebook app) in IOS Swift without any third party library? I look for solutions but I only founded this feature implemented in Objective-C.

回答1:

I believe you can start form UISplitViewController which was drastically updated in iOS8. Watch sessions View Controller Advancements in iOS8 and Building Adaptive Apps with UIKit for details. They provide code example from second session (but not form first one :/). At this point it feels natural for me to make this kind of UI based on split view controller in iOS8.

Update: It looks like not all API they talking about are landed yet. In current beta4 mentioned in video condensesBarsOnSwipe is not presented, for example.



回答2:

Update: Please consider using my updated answer rather than this one. There's a lot of edge cases with the Scrollview/Container View approach that can be avoided by using Custom View Controller Transitions instead.

I looked all over the place for a Swift menu solution that didn't require a library. Ended up creating a tutorial of my own, which is fairly similar to Fengson's approach:

Here are some of the main points:

  • Create a scrollview which will handle the menu sliding motion, along with the pan gesture
  • Put two container views side by side, inside the scrollview. Containers allow you to embed top level controllers.
  • On the scrollview, set paging enabled but disable bounces. This will coerce the slideout into an open or closed state
  • embed a UITableViewController in the left container
  • embed a UITabBarController in the right container
  • On the right container, add a runtime attribute layer.shadowOpacity of 0.8. This gives you a free shadow separator without any code.
  • Add a menu button. You can use NSNotificationCenter to communicate with the scrollview
  • For the secret ingredient: use scrollView.setContentOffset.x to take care of the actual opening and closing of the menu

Here's a working example project of a tab bar application with a left slideout menu, including screenshots and instructions.

https://github.com/ThornTechPublic/LeftSlideoutMenu

Along with a more generic explanation of how it works:

http://www.thorntech.com/2015/06/want-to-implement-a-slideout-menu-in-your-swift-app-heres-how/



回答3:

Slide side bar menus for iOS7 and iOS8, Swift coded.

If you want it at NavigationController level (it means, for al View controllers behind it):

https://github.com/evnaz/ENSwiftSideMenu

If you want it just in one ViewController:

Video: https://www.youtube.com/watch?v=qaLiZgUK2T0

Source-code: http://goo.gl/ULWxJh

In this case, for iOS7 compatibility, just add this "if" condition where the "Add blur view" comment is placed, like this:

if (NSClassFromString("UIVisualEffectView") != nil) {        
    // Add blur view
    let blurView:UIVisualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.Light))
    blurView.frame = sideBarContainerView.bounds
    sideBarContainerView.addSubview(blurView)
}


回答4:

I think using Custom View Controller Transitions is a solid approach for making slideout menus:

  • You can customize the animation.
  • The transition is interactive, so you can drag to progress forward and backward. The transition always finishes or rolls back, and never gets stuck in-between states.
  • You use Pan Gestures and Screen Edge Pan Gestures to drive the interaction. This means you can place them strategically to minimize conflict with horizontally gestured content.
  • You don't have to resort to Container Views. This means your app architecture is flatter, and you can use protocol-delegate instead of NSNotifications.
  • There's only one ViewController active at a time. Snapshots are used to give the illusion that there's a second VC on the screen.

Custom View Controller Transitions are difficult to learn at first (they were for me, at least). I wrote a blog post on how to create an interactive slideout menu, and tried my best to make it as easy to understand as possible.

You can also jump straight into the code on GitHub.

At a very high level, this is how it works:

  1. You open the slideout menu as a modal.
  2. You create custom animations for the present and dismiss transitions using the UIViewControllerAnimatedTransitioning protocol. You use a snapshot to represent the main view controller during animations.
  3. You create Pan Gesture Recognizers to present/dismiss the modal interactively.
  4. You wire the Pan Gesture events to a UIPercentDrivenInteractiveTransition object to keep the transition in sync with the user's movements.
  5. The presenting controller adopts the UIViewControllerTransitioningDelegate protocol and wires up all the custom animations and interactive transitions.

I actually have a previous answer on this thread that uses a Scrollview / Container View. This approach is OK for a prototype, but runs into a lot of edge cases and bugs when making your app production ready. Spending every week responding to blog comments and fixing edge cases is what motivated me to write a second blog post on the subject.



回答5:

Here's another SideMenu library I made to add to the mix: https://github.com/jonkykong/SideMenu.

Simple side menu control for iOS in Swift inspired by Facebook. Right and Left sides. No coding required.

  • It can be implemented in storyboard without a single line of code.
  • Four standard animation styles to choose from (even parallax if you want to get weird).
  • Highly customizable without needing to write tons of custom code.
  • Supports continuous swiping between side menus on boths sides in a single gesture.
  • Global menu configuration. Set-up once and be done for all screens.
  • Menus can be presented and dismissed the same as any other View Controller since this control uses custom transitions.



回答6:

Here is a small example of how I do it, this drawer control has uiviewcontrollers for left, center, and right. This one works like the Slack IPad app where either one side or the other is open.

/*
 To use simply instantiate SlidingDrawerController as your root view in your AppDelegate, or in the
 StoryBoard.
 Once SlidingDrawerController is instantiated, set the drawerSize of the SlidingDrawerController,
 and its leftViewControllerIdentifier, centerViewControllerIdentifier, and
 rightViewControllerIdentifier to the Storyboard Identifier of the UIViewController
 you want in the different locations.
 */

class SlidingDrawerController: UIViewController {
    var drawerSize:CGFloat = 4.0
    var leftViewControllerIdentifier:String = "leftViewController"
    var centerViewControllerIdentifier:String = "centerViewController"
    var rightViewControllerIdentifier:String = "rightViewController"

    enum Drawers {
        case left
        case right
    }

    private var _leftViewController:UIViewController?
    var leftViewController:UIViewController {
        get{
            if let vc = _leftViewController {
                return vc;
            }
            return UIViewController();
        }
    }
    private var _centerViewController:UIViewController?
    var centerViewController:UIViewController {
        get{
            if let vc = _centerViewController {
                return vc;
            }
            return UIViewController();
        }
    }
    private var _rightViewController:UIViewController?
    var rightViewController:UIViewController {
        get{
            if let vc = _rightViewController {
                return vc;
            }
            return UIViewController();
        }
    }

    static let SlidingDrawerOpenLeft = 1
    static let SlidingDrawerOpenRight = 2
    var openSide:SlidingDrawerController.Drawers {
        get{
            return _openSide;
        }
    }
    private var _openSide = SlidingDrawerController.Drawers.left

    override func viewDidLoad() {
        super.viewDidLoad()

        // Instantiate VC's with storyboard ID's
        self._leftViewController = self.instantiateViewControllers(storyboardID: self.leftViewControllerIdentifier)
        self._centerViewController = self.instantiateViewControllers(storyboardID: self.centerViewControllerIdentifier)
        self._rightViewController = self.instantiateViewControllers(storyboardID: self.rightViewControllerIdentifier)

        self.drawDrawers(size: UIScreen.main.bounds.size)

        self.view.addSubview(self.leftViewController.view)
        self.view.addSubview(self.centerViewController.view)
        self.view.addSubview(self.rightViewController.view)
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        coordinator.animateAlongsideTransition(in: self.view, animation: { (UIViewControllerTransitionCoordinatorContext) -> Void in
            // This is for beginning of transition
            self.drawDrawers(size: size)
        }, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
            // This is for after transition has completed.
        })

    }

    // MARK: - Drawing View

    func drawDrawers(size:CGSize) {
        // Calculate Center View's Size
        let centerWidth = (size.width/drawerSize) * (drawerSize - 1)
        // Left Drawer
        self.leftViewController.view.frame = CGRect(x: 0.0, y: 0.0, width: size.width/self.drawerSize, height: size.height)

        // Center Drawer
        self.centerViewController.view.frame = CGRect(x: self.leftViewController.view.frame.width, y: 0.0, width: centerWidth, height: size.height)

        // Right Drawer
        self.rightViewController.view.frame = CGRect(x: self.centerViewController.view.frame.origin.x + self.centerViewController.view.frame.size.width, y: 0.0, width: size.width/self.drawerSize, height: size.height)

        // Capture the Swipes
        let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(self.swipeRightAction(rec:)))
        swipeRight.direction = .right
        centerViewController.view.addGestureRecognizer(swipeRight)

        let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(self.swipeLeftAction(rec:)))
        swipeLeft.direction = .left
        centerViewController.view.addGestureRecognizer(swipeLeft)

        openDrawer(openSide)
    }

    // MARK: - Open Drawers
    func openDrawer(_ side:Drawers) {
        self._openSide = side
        var rect:CGRect
        switch side{
        case .left:
            rect = CGRect(
                x: 0.0,
                y: 0.0,
                width: self.view.bounds.width,
                height: self.view.bounds.height
            )
        case .right:
            rect = CGRect(
                x: self.view.bounds.origin.x - self.leftViewController.view.bounds.size.width,
                y: 0.0,
                width: self.view.bounds.width,
                height: self.view.bounds.height
            )
        }
        UIView.animate(withDuration: 0.1, delay: 0, options: UIViewAnimationOptions.curveEaseIn, animations:
            { () -> Void in
                // move views here
                self.view.frame = rect
        }, completion:
            { finished in
        })
    }

    // MARK: - Swipe Handling

    @objc func swipeRightAction(rec: UISwipeGestureRecognizer){
        self.openDrawer(.left)
    }

    @objc func swipeLeftAction(rec:UISwipeGestureRecognizer){
        self.openDrawer(.right)
    }

    // MARK: - Helpers

    func instantiateViewControllers(storyboardID: String) -> UIViewController {
        return UIStoryboard(name: "Main", bundle: nil)
                .instantiateViewController(withIdentifier: "\(storyboardID)")

    }
}

Source here.

Image here.

Video here.



回答7:

I have implemented it in 2 ways.
First using Scroll View and second using Container Views.

You can have TableViewController acting as a Menu in one container and a TabBarController with hidden tabs in a second container. Pressing a Cell in a Table View moves you to a n-th tab in Tab Bar.

All you have to do is animate the top container right on button click or a gesture. It then may look like this:

What is neat is that you can then easily add cool effects such as animating UIView using spring damping by using built-in methods to give it realistic bouncy effect. You can also add shadow to your main View (not added in the picture) to make it look like a page above the menu.



标签: swift ios8