UITabBar personalization, color and separator lines - ios

I'm making this iOS app (Swift 3) that has a tab bar and I don't find a way to personalized it to the way I want to. I want to add lines in between each icon and I want it to be red when selected, like this:

Custom a class inherited from UITabBarController, and then use it as your TabBarController's class. The main procedure provided here:
import UIKit
class MainTabBarController: UITabBarController,UITabBarControllerDelegate {
var firstBackgroundView:UIView!
//var secondBackgroundView:UIView!
//......
override func viewDidLoad() {
super.viewDidLoad()
//Lines:
let topline = CALayer()
topline.frame = CGRect(x: 0, y: 0, width: self.tabBar.frame.width, height: 2)
topline.backgroundColor = UIColor.gray.cgColor
self.tabBar.layer.addSublayer(topline)
let firstVerticalLine = CALayer()
firstVerticalLine.frame = CGRect(x: self.tabBar.frame.width / 5, y: 0, width: 2, height: self.tabBar.frame.height)
firstVerticalLine.backgroundColor = UIColor.gray.cgColor
self.tabBar.layer.addSublayer(firstVerticalLine)
//Continue to add other lines to divide tab items...
//......
//Background views
firstBackgroundView = UIView(frame: CGRect(x: 0, y: 0, width: self.tabBar.frame.width / 5, height: self.tabBar.frame.height))
firstBackgroundView.backgroundColor = UIColor.red
firstBackgroundView.isHidden = false //true for others.
self.tabBar.addSubview(firstBackgroundView)
self.tabBar.sendSubview(toBack: firstBackgroundView)
//Continue to add other background views for each tab item...
//......
self.delegate = self
}
public func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if self.selectedIndex == 0 {
firstBackgroundView.isHidden = false
//othersBackgroundView.isHidden = true
}
else if self.selectedIndex == 1 {
//......
}
}
}

Related

How do you pass a gesture between subviews in UIKit?

I've got a ViewController with three subviews. I'm trying to get them to detect touches in their bounds from a starting point outside their bounds without the user lifting their finger (ie the user dragging into the view). I thought hitTest would do this but it only works for separate taps. I assume this is probably passing a gesture through instead but I've not found out how to implement this.
class SuperViewController: UIViewController {
var view01 = UIView(frame: CGRect(x: 0, y: 0, width: 1000,
height: 800))
var view02 = UIView(frame: CGRect(x: 0, y: 0, width: 600,
height: 400))
let view03 = UIView(frame: CGRect(x: 0, y: 0, width: 300,
height: 200))
override func viewDidLoad() {
super.viewDidLoad()
self.view = TestView()
view01.backgroundColor = .orange
view02.backgroundColor = .blue
view03.backgroundColor = .green
self.view.addSubview(view01)
self.view.addSubview(view02)
self.view.addSubview(view03)
}
}
Which produces this
And then I've subclassed UIView for the SuperViewController's view.
class TestView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard self.isUserInteractionEnabled, !isHidden, alpha > 0.01 else {return nil}
if self.point(inside: point, with: event) {
for subview in subviews.reversed() {
let hitView = subview.hitTest(point, with: event)
if hitView != nil {
hitView?.backgroundColor = .red
return hitView
}
}
return self
}
return nil
}
}
So each one turns red when the user taps. But ideally I want them to each respond with one drag from the top left corner of the screen to the other.
You can accomplish this with a UIPanGestureRecognizer.
Here's an example below:
class ViewController: UIViewController {
var view01 = UIView(frame: CGRect(x: 0, y: 0, width: 1000,
height: 800))
var view02 = UIView(frame: CGRect(x: 0, y: 0, width: 600,
height: 400))
let view03 = UIView(frame: CGRect(x: 0, y: 0, width: 300,
height: 200))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view01.backgroundColor = .orange
view02.backgroundColor = .blue
view03.backgroundColor = .green
self.view.addSubview(view01)
self.view.addSubview(view02)
self.view.addSubview(view03)
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
self.view.addGestureRecognizer(gestureRecognizer)
}
#objc
private func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) {
guard let view = gestureRecognizer.view else {
return
}
let translation = gestureRecognizer.translation(in: view)
for subview in view.subviews.reversed() {
if let hitView = subview.hitTest(translation, with: nil) {
hitView.backgroundColor = .red
return
}
}
}
}

Custom UITabBarController - DidSelect/AnyCustomization delegate problem

I created custom TabBar class, but I have few problems, how I can call any function from this class when for example tab is hidden? (I would like to hide my stripe when tab bar is hidden) Also when I use tabBarController?.selectedIndex = 3 the delegate didSelect is not called, how can I solve it? That is my simple code. Thanks for any help
class customTabBar: UITabBarController {
var stripe = UIView()
override func viewDidLoad() {
super.viewDidLoad()
let cellWidth = tabBar.frame.width/5
stripe = UIView(frame: CGRect(x: 0, y: tabBar.frame.minY + 20, width: (tabBar.frame.width/5) * 0.6, height: 6))
stripe.center.x = cellWidth/2
stripe.applyGradient(colours: [UIColor.init(hexFromString: "5897ee"),UIColor.init(hexFromString: "5228d8")])
stripe.layer.cornerRadius = 2
stripe.layer.masksToBounds = true
self.view.addSubview(stripe)
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
let index: Int = item.tag + 1
let cellWidth = tabBar.frame.width/5
let newPostion = cellWidth * CGFloat(index)
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.3, options: .curveEaseInOut, animations: {
self.stripe.center.x = newPostion - (cellWidth/2)
})
}
}
class MyTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
//Add the stripe to tabBar,so it will hidden when tabBar hidden
let cellWidth = tabBar.frame.width/5
let stripe = UIView(frame: CGRect(x: 0, y: tabBar.frame.minY + 20, width: (tabBar.frame.width/5) * 0.6, height: 6))
stripe.center.x = cellWidth/2
stripe.layer.cornerRadius = 2
stripe.layer.masksToBounds = true
self.tabBar.addSubview(stripe)
}
//use this to observe tabBarController?.selectedIndex = 3
override var selectedIndex: Int{
didSet{
//do what you want
}
}
}
extension UIViewController {
//use this to call get MyTabBarController instance and call any function
var myTabBarcontroller : MyTabBarController?{
get{
return self.tabBarController as? MyTabBarController
}
}
}

How to overlap status bar when adding UIView on top of UINavigationBar?

I am using UINavigationController as a rootController with UIViewController. In UIViewController, i am adding custom UIView into the navigationBar(because i want to use CAGradientLayer to my custom UIView).
Everything's working fine except that my custom UIView does not overlap the status bar, which means my custom UIView is padding from top of the navigationBar.
Is there any solution and way to make it overlap statusBar? Here i included UI of what i want to achieve.
Status bar is always there so if you don't want it when you add your view you have to override a property prefersStatusBarHidden of the ViewController where your new View is added like so:-
Declare a new variable say:
var myNewViewIsVisible: Bool = false {
didSet {
DispatchQueue.main.async { [weak self] in
guard let self = self else {return}
self.setNeedsStatusBarAppearanceUpdate()
}
}
}
Then override the property like so: -
override var prefersStatusBarHidden: Bool {
return myNewViewIsVisible
}
Explanation:
First you declare your variable and observe it for changes. If it is changed ie.. when you add your new view, change it to true then force the ViewController to update statatusBar, that will cause it to hide because your myNewViewIsVisible will be true.
let gradient = CAGradientLayer()
gradient.frame = CGRect(x: 0, y: 0, width: navigationBar.frame.width, height: 100)
let leftColor = UIColor.red
let rightColor = UIColor.purple
gradient.colors = [leftColor.cgColor, rightColor.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
navigationBar.setBackgroundImage(image(fromLayer: gradient), for: .default)
func image(fromLayer layer: CALayer) -> UIImage {
UIGraphicsBeginImageContext(layer.frame.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
let outputImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return outputImage!
}
You need to hide the navigation bar :
self.navigationController?.setNavigationBarHidden(true, animated: false)
Then you need to show status bar :
override var prefersStatusBarHidden: Bool{
return false
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Also then you have to set the background of the status bar view :
let statusBarView = UIView(frame: CGRect(x: 0, y: 0, width:UIScreen.main.bounds.width, height: UIApplication.shared.statusBarFrame.height))
statusBarView.backgroundColor = UIColor.gray
self.navigationController?.view.addSubview(statusBarView)

Swift add Custom View to screen with UIApplication.shared.keyWindow?.addSubview

hope this is an easy one...
I am in a empty new project.
I have added a custom view called MyCustomView:
import UIKit
public class MyCustomView: UIView{
private var littleView: UIView!
open class func show() -> UIView{
let bigView = MyCustomView()
bigView.configureView()
UIApplication.shared.keyWindow?.addSubview(bigView)
return bigView
}
private func configureView(){
let screenSize = UIScreen.main.bounds.size
self.frame = CGRect(x: 0,
y: 0,
width: screenSize.width,
height: screenSize.height)
littleView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
littleView.backgroundColor = .black
addSubview(littleView)
}
}
In the ViewController doing this:
override func viewDidLoad() {
let test = MyFirstView.show()
}
I hoped this will present the view, but I still have to use self.view.addSubview(test) to see it....
I thought with UIApplication.shared.keyWindow?.addSubview(bigView) and adding a subView to it, it should present the View.
What am I missing?
Add the subview in viewDidAppear
override func viewDidAppear() {
let test = MyFirstView.show()
}

How do I programmatically change the height of a navigationBar in Swift?

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let height = CGFloat(84)
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: height)
}
This code simply inserts space above the titleView. A custom titleView at point (0,0) has ~20 points of space above it. A height >40 starts to run off the navBar.
You can subclass UINavigationBar :
class CustomNavigationBar: UINavigationBar {
override func sizeThatFits(_ size: CGSize) -> CGSize {
let newSize :CGSize = CGSize(width: self.frame.size.width,height: 84)
return newSize
}
}
Then create the navigation controller and use the initialiser to use your custom navigation bar class.
let nav = UINavigationController(navigationBarClass:CustomNavigationBar.self,toolbarClass: nil)
All existing behavior for UINavigationBar is preserved and your custom height is adopted.
OR
Like you already tried :
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let height: CGFloat = 84 //whatever height you want
let bounds = self.navigationController!.navigationBar.bounds
self.navigationController?.navigationBar.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height + height)
}
OR :
You can try this solution Changing the height of the Navigation bar iOS Swift
you can use a custom view to replace the navigation bar.This is more easy and flexible. hide the navi bar and implement a custom view.
class ViewController : UIViewController {
var navBar: UINavigationBar = UINavigationBar()
override func viewDidLoad() {
super.viewDidLoad()
self.setCustomNavBarView()
}
func setCustomNavBarView() {
self.navBar.frame = CGRect(x: 0, y: 0, width: 350, height: 50) // Set you custom width and Height
self.navBar.backgroundColor = UIColor.gray
self.view.addSubview(navBar)
}
}
A simple tutorial on how to do that:
Hope this helps!!
class ViewController: UIViewController {
var navBar: UINavigationBar = UINavigationBar()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.setCustomNavBarView()
}
`func setCustomNavBarView() {
self.navBar.frame = CGRect(x: 0, y: 0, width: 350, height: 100) // Set you custom width and Height
self.navBar.backgroundColor = UIColor.gray
self.view.addSubview(navBar)
}
`
swift 3 updated code here

Resources