How to create a vertical TarBarController? VerticalTabBar, commonly used on iPad s

This kind of demand is not much, encountered, or to be handled

The idea is a container controller, ContainerViewController.

Encapsulate a VerticalTabBar to manage the controller.

There is also a button bar on the left to use Table View, UITableView

Each button is a Cell, and then bind the button clicks to the corresponding managed controller, and you're done

First, the management of container controllers

There are multiple controllers, and the user clicks tabBar

Switch controller, the current controller, is the selected controller

  // To successfully initialize, set to avoid duplicate operations, make judgments
   var _selectedIndex: Int = -1
    var selectedIndex: Int{
        get{
            return _selectedIndex
        }
        set(newValue){
            // Be Judged
            if newValue != selectedIndex, newValue < viewControllers.count{
                let selectedViewController = viewControllers[newValue]
               //  Add a new controller
                addChild(selectedViewController)
                selectedViewController.view.frame = CGRect(x: tabBarWidth, y: 0, width: view.bounds.size.width - tabBarWidth, height: view.bounds.size.height)
                view.addSubview(selectedViewController.view)
                if selectedIndex >= 0{
                   // Remove old controller
                    let previousViewController = viewControllers[selectedIndex]
                    previousViewController.view.removeFromSuperview()
                    previousViewController.removeFromParent()
                }
                _selectedIndex = newValue
                guard let items = tabBar.items else{
                    return
                }
                if selectedIndex < items.count{
                    tabBar.selectedItem = items[selectedIndex]
                }
               // Agent method, do something else when switching controllers
                delegate?.tabBarController?(self, didSelect: selectedViewController)
            }
        }
    }

Implemented by the set method of selectedIndex, selecting a button specifies a controller selectedViewController, adds a new controller, and removes the old controller

Add a new controller, standard four steps

  • Call addChildViewController:, telling UIKit that your container controller now manages your child controller
  • view.addSubview(selectedViewController.view), which adds the root view of the subcontroller to the view level of the container controller

Remember the layout, provide location information

  • Layout constraints can be added to the root view of some subcontrollers
  • didMoveToParentViewController that calls the subcontroller:

Add a new controller, standard four steps

  • Calling the subcontroller willMoveToParentViewController:, the value on the right is nil
  • Remove all layout constraints from child controller root view
  • previousViewController.view.removeFromSuperview(), removes the root view of the subcontroller from the view level of the container controller's root view
  • Call removeFromParentViewController to end the parent-child relationship
To change both new and old values in the set method of Swift, you need an auxiliary property_selectedIndex
Initialized dummy

If newValue!= selectedIndex, make a judgment when setting, to avoid repetition,
For smooth initialization, var _selectedIndex: Int = -1, which is an impossible value and is valid once

(People who like to play with algorithms know dummy)

Second, click on the callback tab, which is the proxy method

@objc protocol TabBarControllerDelegate: class {
    @objc optional
    func tabBarController(_ tabBarController: VerticalTabBarController, didSelect viewController: UIViewController)
    @objc optional
    func tabBarController(_ tabBarController: VerticalTabBarController, shouldSelect viewController: UIViewController) -> Bool
}

The first proxy method, didSelect, can do something else and bury a point while switching controllers
His timing is at the end of the last block of code

The second proxy method, shouldSelect, can make a judgment that you can't enter the Personal Center without logging in, you need to log in

func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        let new = viewControllers[indexPath.row]
        let result = delegate?.tabBarController?(self, shouldSelect: new)
        if let answer = result, answer{
            return indexPath
        }
        else{
            return tableView.indexPathForSelectedRow
        }
    }

When he did it, in the proxy callback for tableView,

The previous method, also in the proxy callback of tableView, bypassed the source code through the set method of selectedIndex

Text Code https://github.com/coyingcat/VerticalTabBar

This article is based on Objective-C futuresimple/FSVerticalTabBarController.

Third, whizzy, draw the selected image gradient

override func draw(_ rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()
         // flip the coordinates system
        context?.translateBy(x: 0, y: bounds.height)
        context?.scaleBy(x: 1, y: -1)
        
        // draw an image in the center of the cell
        guard let imageSize = icon?.size else {
            return
        }
        
        let imageRect = CGRect(x: (bounds.size.width - imageSize.width)/2.0,  y: (bounds.size.height - imageSize.height)/2.0 + 15, width: imageSize.width, height: imageSize.height)
        // draw either a selection gradient/glow or a regular image
        guard isSelected else {
            if let iconImage = icon {
                UIImage(cgImage: iconImage.cgImage!, scale: iconImage.scale, orientation: UIImage.Orientation.down).withHorizontallyFlippedOrientation().draw(in: imageRect)
            }
            return
        }
        
        // setup shadow
        let shadowOffset = CGSize(width: 0, height: 1)
        let shadowBlur: CGFloat = 3
        let cgShadowColor = UIColor.black.cgColor
        // setup gradient
        let alphas: [CGFloat] = [0.8,0.6, 0, 0.1,0.5]
        let locations: [CGFloat] = [0, 0.55, 0.55, 0.7, 1]
        let components: [CGFloat] = [1, 1, 1, alphas[0], 1,
                                     1, 1, alphas[1], 1, 1,
                                     1, alphas[2], 1, 1,1,
                                     alphas[3],1, 1,1,alphas[4]]
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        guard let colorGradient = CGGradient(colorSpace: colorSpace, colorComponents: components, locations: locations, count: 5) else {
            return
        }
        // set shadow
        context?.setShadow(offset: shadowOffset, blur: shadowBlur, color: cgShadowColor)
        // set transparency layer and clip to mask
        context?.beginTransparencyLayer(auxiliaryInfo: nil)
        context?.clip(to: imageRect, mask: icon!.cgImage!)
        // fill and end the transparency layer
        context?.setFillColor(selectedImageTintColor.cgColor)
        context?.fill(imageRect)
        let start = CGPoint(x: imageRect.midX, y: imageRect.origin.y)
        let end = CGPoint(x: imageRect.midX - imageRect.height/4, y: imageRect.height + imageRect.minY)
        context?.drawLinearGradient(colorGradient, start: end, end: start, options: [])
        context?.endTransparencyLayer()
    }

Because the override func draw (rect: CGRect) {method comes with a drawing context, first get the current context, flip the coordinate system, shade, gradient

Another such method in Objective-C is CGContextDrawImage(context, imageRect,self.iconImage.CGImage);

Swift doesn't have this, it's all direct iconImage.draw(in: imageRect).

There are pits in the drawing, the coordinate system reasons,

How do I do a flip? I rotate 180 degrees first, then do a left-right flip

UIImage(cgImage: iconImage.cgImage!, scale: iconImage.scale, orientation: UIImage.Orientation.down).withHorizontallyFlippedOrientation().draw(in: imageRect)

Keywords: iOS Swift github

Added by bobthebullet990 on Sun, 25 Aug 2019 20:04:25 +0300