How to add LGPlusButton view as subview to tab bar
Hi There,
Can you please help me how to add LGPlusButton view to TabBar. If i add them as subview to TabbarController or the NavigationController there is a issues in touch detection.

Hi,
I just did something similar. In my case, I have a Tab Bar that is 20pt taller than the standard iOS Tab Bar. So, when I tried .BottomRight, the + button would be obstructed by my tab bar.
So, since I already was subclassing UITabBarController for other reasons, I was able to place the + button inside another "view" that I could control. The idea is that I can then place this container view wherever I want, in effect, I can place the + button wherever I want if I place it inside this container view.
Below are my steps:
- Create a subclass of UITabBarController and make sure your Tab Bar is using that. (Either in IB or in code). For simplicity, let's call your subclass "SectionTabBarController".
- In your SectionTabBarController, viewDidLoad, AFTER you call super/base viewDidLoad, create a container view (this is where you'll drop in the LG button later on).
- In my case, I used a view called actionButtonContainer and I added that to self.view in my subclass. I used AutoLayout in my case to place the container view exactly where I wanted it.
- Now, finally, create your LGPlusButtonView and add it to your actionButtonContainer view.
- I also had to add a little magic to "expand" the container view when the buttons are showing - otherwise, the buttons would not get taps. You can see that below in my code.
Below is my code - inside CustomTabBarController viewDidLoad. Note that I use a library for AutoLayout called "PureLayout", so that's why you see all my constraints are in the form "autoPin...". Remember, I'm placing the container view "bottom right".
//
// SectionTabBarController.swift
// SomeApp
//
// Created by Eric A. Soto on 7/9/16.
//
import UIKit
import LGPlusButtonsView
import Intercom
/**
A custom class to style our TabBar + add our Floating Action Button
*/
class SectionTabBarController: UITabBarController, LGPlusButtonsViewDelegate {
var actionButton: LGPlusButtonsView!
var actionButtonContainerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// Create a placeholder view for our Action Button
actionButtonContainerView = UIView()
self.view.addSubview(actionButtonContainerView)
// Constrain it (we start with the action buttons not showing = closed)
actionButtonContainerView.translatesAutoresizingMaskIntoConstraints = false
constrainActionButtonContainer(.Closed)
// Create our button
actionButton = LGPlusButtonsView(numberOfButtons: 3, firstButtonIsPlusButton: true, showAfterInit: true, delegate: self)
actionButton.setButtonsTitles(["?", "Get Help", "View Conversations"], forState: .Normal)
actionButton.setButtonAtIndex(0, title: "X", forState: .Selected)
actionButton.setButtonsAdjustsImageWhenHighlighted(false)
actionButton.setButtonsBackgroundColor(AppFormatting.TabBar.backgroundColorWhenSelected, forState: .Normal)
// Set our default buttons (all of them), then we'll customize the PLUS
actionButton.setButtonsSize(CGSize(width: 170.0, height: 38.0), forOrientation: .All)
actionButton.setButtonsLayerCornerRadius(6.0, forOrientation: .All)
actionButton.setButtonsTitleFont(AppFormatting.Fonts.defaultFont(18.0), forOrientation: .All)
// Format the plus icon
actionButton.setButtonAtIndex(0, size: CGSize(width: 44.0, height: 44.0), forOrientation: .All)
actionButton.setButtonAtIndex(0, layerCornerRadius: 22.00, forOrientation: .All)
actionButton.setButtonAtIndex(0, titleFont: AppFormatting.Fonts.defaultFontBold(24.0), forOrientation: .All)
// Now add to our container
actionButtonContainerView.addSubview(actionButton)
// Constrain it
actionButton.translatesAutoresizingMaskIntoConstraints = false
actionButton.autoCenterInSuperview()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - LGPlusButtonsView Delegate
/**
When the buttons show, we want to constrain our container view for the Action Button to include the
buttons. Otherwise, we won't be able to tap on them!
*/
func plusButtonsViewWillShowButtons(plusButtonsView: LGPlusButtonsView!) {
constrainActionButtonContainer(.Open)
}
/**
When the buttons hide, we want to constrain our container view for the Action Button to just the
plus button. Otherwise, it will receive taps instead of the view underneath!
*/
func plusButtonsViewWillHideButtons(plusButtonsView: LGPlusButtonsView!) {
constrainActionButtonContainer(.Closed)
}
/**
Handle a button Tap on our Floating Action Button
*/
func plusButtonsView(plusButtonsView: LGPlusButtonsView!, buttonPressedWithTitle title: String!, description: String!, index: UInt) {
// On tap of button zero, we let the library do its thing, so we just exit here!
guard index > 0 else {
return
}
// ASSERT COMMENT: We have an index 1 or above
// On tap of a button, we hide the buttons
plusButtonsView.hideButtonsAnimated(true, completionHandler: nil)
// Now, decide what to do based on which button was tapped.
switch index {
case 1:
Intercom.presentMessageComposer()
case 2:
Intercom.presentConversationList()
default:
Intercom.presentConversationList()
}
}
// MARK: - Helpers
enum actionButtonState {
case Open
case Closed
}
func constrainActionButtonContainer(state: actionButtonState) {
// if the view has constraints, clear them prior to resetting
if actionButtonContainerView.constraints.count > 0 {
actionButtonContainerView.removeConstraints(actionButtonContainerView.constraints)
}
// Position the bottom/right edge
actionButtonContainerView.autoPinEdge(.Right, toEdge: .Right, ofView: self.view, withOffset: -3.0)
actionButtonContainerView.autoPinEdge(.Bottom, toEdge: .Bottom, ofView: self.view, withOffset: -65.0)
// Now, constrain for the state we want
switch state {
case .Closed:
// When the action buttons are hidden, we only accommodate the + button, otherwise, our view will take over taps from other items!
actionButtonContainerView.autoSetDimensionsToSize(CGSize(width: 60, height: 60))
case .Open:
// When the action buttons are shown, we make the view larger to accomodate the buttons
actionButtonContainerView.autoSetDimensionsToSize(CGSize(width: 170, height: 150))
}
}
}
I know this is not "exactly" what you're trying to do, but I hope it might give you ideas.
Good luck!
Eric.
@ericwastaken Hi Eric, Thank you, It helped me a lot..:)
Glad I was able to help.
Note that after I did the above, I had a situation where I was still not getting TAPS for the views underneath. I ended up having to place the action button on a view that covered everything.
Well, turns out that a view can implement "pointInside" to let the OS know if a tap should be handled by the view OR passed on to the rest of the responder chain.
So, I moved some things around and below is what I ended up with.
I now have ActionButtonView.swift as a standalone subclass of UIView:
//
// ActionButtonView.swift
// SomeApp
//
// Created by Eric A. Soto on 7/10/16.
//
import UIKit
import LGPlusButtonsView
import Intercom
class ActionButtonView: UIView, LGPlusButtonsViewDelegate {
// Public Properties
var actionButton: LGPlusButtonsView!
var actionButtonContainerView: UIView!
// MARK: - Constructors & Events
override init(frame: CGRect) {
super.init(frame: frame)
setupActionButton()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
fatalError("init(coder:) has not been implemented. This view requires properties to be set, so implementing it via IB is not supported.");
}
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
// We only want to match the point if it's inside our container view's frame
return CGRectContainsPoint(actionButtonContainerView.frame, point)
}
// MARK: - Exposed Methods
func hideButtonsIfShowing() {
actionButton.hideButtonsAnimated(true, completionHandler: nil)
}
// MARK: - LGPlusButtonsView Delegate
/**
When the buttons show, we want to constrain our container view for the Action Button to include the
buttons. Otherwise, we won't be able to tap on them!
*/
func plusButtonsViewWillShowButtons(plusButtonsView: LGPlusButtonsView!) {
constrainActionButtonContainer(.Open)
}
/**
When the buttons hide, we want to constrain our container view for the Action Button to just the
plus button. Otherwise, it will receive taps instead of the view underneath!
*/
func plusButtonsViewWillHideButtons(plusButtonsView: LGPlusButtonsView!) {
constrainActionButtonContainer(.Closed)
}
/**
Handle a button Tap on our Floating Action Button
*/
func plusButtonsView(plusButtonsView: LGPlusButtonsView!, buttonPressedWithTitle title: String!, description: String!, index: UInt) {
// On tap of button zero, we let the library do its thing
guard index > 0 else {
return
}
// ASSERT COMMENT: We have an index 1 or above
// On tap of a button, we hide the buttons
plusButtonsView.hideButtonsAnimated(true, completionHandler: nil)
// Now, decide what to do based on which button was tapped.
switch index {
case 1:
Intercom.presentMessageComposer()
case 2:
Intercom.presentConversationList()
case 3:
// Nada - this is the app version button
break
default:
Intercom.presentConversationList()
}
}
}
private extension ActionButtonView {
// MARK: - Implementation
func setupActionButton() {
// Create a placeholder view for our Action Button
actionButtonContainerView = UIView()
self.addSubview(actionButtonContainerView)
// Constrain it (we start with the action buttons not showing = closed)
actionButtonContainerView.translatesAutoresizingMaskIntoConstraints = false
constrainActionButtonContainer(.Closed)
// Create our button
actionButton = LGPlusButtonsView(numberOfButtons: 4, firstButtonIsPlusButton: true, showAfterInit: true, delegate: self)
actionButton.coverColor = UIColor.blackColor().colorWithAlphaComponent(0.4)
actionButton.setButtonsTitles(["?", "Get Help", "View Conversations", AppUtility.appVersionString], forState: .Normal)
actionButton.setButtonsAdjustsImageWhenHighlighted(false)
actionButton.setButtonsBackgroundColor(AppFormatting.TabBar.backgroundColorWhenSelected, forState: .Normal)
// Set our default buttons (all of them), then we'll customize the PLUS
actionButton.setButtonsSize(CGSize(width: 170.0, height: 38.0), forOrientation: .All)
actionButton.setButtonsLayerCornerRadius(6.0, forOrientation: .All)
actionButton.setButtonsTitleFont(AppFormatting.Fonts.defaultFont(18.0), forOrientation: .All)
// Format the plus icon
actionButton.setButtonAtIndex(0, title: "X", forState: .Selected)
actionButton.setButtonAtIndex(0, size: CGSize(width: 44.0, height: 44.0), forOrientation: .All)
actionButton.setButtonAtIndex(0, layerCornerRadius: 22.00, forOrientation: .All)
actionButton.setButtonAtIndex(0, titleFont: AppFormatting.Fonts.defaultFontBold(24.0), forOrientation: .All)
actionButton.setButtonAtIndex(0, backgroundColor: AppFormatting.TabBar.backgroundColorWhenSelected.colorWithAlphaComponent(0.6), forState: .Normal)
actionButton.setButtonAtIndex(0, backgroundColor: AppFormatting.TabBar.backgroundColorWhenSelected, forState: .Selected)
// Format the App Version button
actionButton.setButtonAtIndex(3, layerCornerRadius: 0.0, forOrientation: .All)
actionButton.setButtonAtIndex(3, backgroundColor: UIColor.whiteColor(), forState: .Normal)
actionButton.setButtonAtIndex(3, titleColor: AppFormatting.Fonts.defaultFontColor, forState: .Normal)
actionButton.setButtonAtIndex(3, titleFont: AppFormatting.Fonts.defaultFont(14.0), forOrientation: .All)
actionButton.setButtonAtIndex(3, size: CGSize(width: 170.0, height: 24.0), forOrientation: .All)
actionButton.setButtonAtIndex(3, layerBorderColor: AppFormatting.Fonts.defaultFontColor)
actionButton.setButtonAtIndex(3, layerBorderWidth: 1.0)
// Now add to our container
actionButtonContainerView.addSubview(actionButton)
// Constrain it
actionButton.translatesAutoresizingMaskIntoConstraints = false
actionButton.autoCenterInSuperview()
}
// MARK: - Helpers
enum actionButtonState {
case Open
case Closed
}
func constrainActionButtonContainer(state: actionButtonState) {
let viewWidth = self.frame.width
let viewHeight = self.frame.height
let bottomOffset:CGFloat = -65.0
let rightOffset:CGFloat = 0 // Offsetting from the right causes misalignment of the COVER!
let closedSize = CGSize(width: 60.0, height: 60.0)
let openSize = CGSize(width: viewWidth+rightOffset, height: viewHeight-bottomOffset)
// if the view has constraints, clear them prior to resetting
if actionButtonContainerView.constraints.count > 0 {
actionButtonContainerView.removeConstraints(actionButtonContainerView.constraints)
}
// Position the bottom/right edge
actionButtonContainerView.autoPinEdge(.Right, toEdge: .Right, ofView: self, withOffset: rightOffset)
actionButtonContainerView.autoPinEdge(.Bottom, toEdge: .Bottom, ofView: self, withOffset: bottomOffset)
// Now, constrain for the state we want
switch state {
case .Closed:
// When the action buttons are hidden, we only accommodate the + button, otherwise, our view will take over taps from other items!
actionButtonContainerView.autoSetDimensionsToSize(closedSize)
case .Open:
// When the action buttons are shown, we make the view larger to accomodate the buttons
actionButtonContainerView.autoSetDimensionsToSize(openSize)
}
}
}
Then, in my UITabBarController subclass:
// Inside viewDidLoad
// Add our ActionButton (it will constrain itself to where it needs to)
actionButtonView = ActionButtonView(frame:self.view.frame)
self.view.addSubview(actionButtonView)
actionButtonView.autoPinEdgesToSuperviewEdges()