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)