Skip to content

How to add LGPlusButton view as subview to tab bar  #31

Open

Description

@punithbm

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.

screen shot 2016-06-27 at 3 45 22 pm

Activity

  1. ericwastaken commented on Jul 10, 2016

    @ericwastaken

    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:

    1. 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".
    2. 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).
    3. 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.
    4. Now, finally, create your LGPlusButtonView and add it to your actionButtonContainer view.
    5. 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.

  2. punithbm commented on Jul 11, 2016

    @punithbm
    Author

    @ericwastaken Hi Eric,
    Thank you, It helped me a lot..:)

  3. ericwastaken commented on Jul 11, 2016

    @ericwastaken

    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()
    
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions