You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
How to add LGPlusButton view as subview to tab bar #31
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.
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.
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()
Activity
ericwastaken commentedon Jul 10, 2016
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:
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".
I know this is not "exactly" what you're trying to do, but I hope it might give you ideas.
Good luck!
Eric.
punithbm commentedon Jul 11, 2016
@ericwastaken Hi Eric,
Thank you, It helped me a lot..:)
ericwastaken commentedon Jul 11, 2016
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:
Then, in my UITabBarController subclass: