iosswiftiphoneuicollectionviewuitabbar

CollectionView Touch is now working when placed on top of tabBar Swift


I have added a collectionView on top of a UITabBar but its touch is not working.The screeshot for the tabBar and collectionView

The code is attached below, I want the collectionView to be touchable. Here quickAccessView is the UIView that contains the collectionView. For constraints I'm using snapKit

    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        self.tabBar.bringSubviewToFront(quickAccessView)
    }

    private func setupQuickAccessView(){
        print("this is tabBar's height", self.tabBar.frame.size.height)
        self.tabBar.frame.size.height = 150
        print("this is new tabBar's height", self.tabBar.frame.size.height)
        self.tabBar.addSubview(quickAccessView)
        quickAccessView.clipsToBounds = true
    }
    private func addQuickAccessViewConstraints(){
        quickAccessView.snp.makeConstraints { make in
            make.right.left.equalTo(self.tabBar.safeAreaLayoutGuide)
            make.height.equalTo(76)
            make.bottom.equalTo(self.tabBar.snp.bottom).offset(-80)
        }
    }

this is after modification that Aman told The UITabBarController

final class MainTabBarController: UITabBarController {
   private lazy var quickAccessView: QuickAccessView = .fromNib()
    var quickAccessSupportedTabBar: QuickAccessSupportedTabBar {
        self.tabBar as! QuickAccessSupportedTabBar // Even code is crashing here
    }
    // Even code is crashing here
    override func viewDidLoad() {
        super.viewDidLoad()
        self.tabBar.backgroundColor = .white
        
}
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.view.frame = self.quickAccessView.bounds
        setupUI()
    }
}

extension MainTabBarController{
    
    private func setupUI(){
        setupQuickAcessView()
        addQuickAcessViewConstraints()
    }
    
}

// MARK: - Setting Up Quick Access view

extension MainTabBarController {
    private func setupQuickAcessView(){

        self.quickAccessSupportedTabBar.addSubview(quickAccessView)
    }
    
    private func addQuickAcessViewConstraints(){
        quickAccessView.snp.makeConstraints { make in
            make.left.right.equalTo(self.quickAccessSupportedTabBar.safeAreaLayoutGuide)
            make.height.equalTo(66)
            make.bottom.equalTo(self.quickAccessSupportedTabBar.snp.top)
        }
    }
}

the UItabBar and here it is throwing error and I too am confuse that how to access it and convert it to points

class QuickAccessSupportedTabBar: UITabBar {
    override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        // if `quickAccessView` is visible, then convert `point` to its coordinate-system
        // and check if it is within its bounds; if it is, then ask `quickAccessView`
        // to perform the hit-test; you may skip the `isHidden` check, in-case this view
        // is always present in your app; I'm assuming based on your screenshot that
        // the user can dismiss / hide the `quickAccessView` using the cross icon

        if !quickAccessView.isHidden {
            // Convert the point to the target view's coordinate system.
            // The target view isn't necessarily the immediate subview

            let targetPoint = quickAccessView.convert(point, from: self)
            
            if quickAccessView.bounds.contains(targetPoint) {

                // The target view may have its view hierarchy, so call its
                // hitTest method to return the right hit-test view
                return quickAccessView.hitTest(targetPoint, with: event)
            }
        }
        
        // else execute tabbar's default implementation
        return super.hitTest(point, with: event)
    }
}

Solution

  • I think what may be happening here is that since you've added quickAccessView as tab bar's subview, it is not accepting touches. This would be so because the tabbar's hist test will fail in this scenario.

    To get around this, instead of using a UITabBar, subclass UITabBar, let us call it ToastyTabBar for reference. See the code below:

    class ToastyTabBar: UITabBar {
        override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
            // if `quickAccessView` is visible, then convert `point` to its coordinate-system
            // and check if it is within its bounds; if it is, then ask `quickAccessView`
            // to perform the hit-test; you may skip the `isHidden` check, in-case this view
            // is always present in your app; I'm assuming based on your screenshot that
            // the user can dismiss / hide the `quickAccessView` using the cross icon
    
            if !quickAccessView.isHidden {
                // Convert the point to the target view's coordinate system.
                // The target view isn't necessarily the immediate subview
    
                let targetPoint = quickAccessView.convert(point, from: self)
                
                if quickAccessView.bounds.contains(targetPoint) {
    
                    // The target view may have its view hierarchy, so call its
                    // hitTest method to return the right hit-test view
                    return quickAccessView.hitTest(targetPoint, with: event)
                }
            }
            
            // else execute tabbar's default implementation
            return super.hitTest(point, with: event)
        }
    }
    

    Set this as the class of your tabBar (both in the storyboard and the swift file), and then see if that solves it for you. You'll have to figure out a way to make quickAccessView accessible to the tabbar for the hit test check. I haven't advised on that above because I'm not familiar with your class hierarchy, and how and where you set stuff up, but this should be trivial.

    If this solves it for you, please consider marking this as the answer, and if it does not then please share a little more info here about where you're having the problem.

    Edit (for someone using a UITabBarController):

    In response to your comment about "how to access UITabBar class from UITabBarController" here's how I would go about it.

    I'm assuming you have a storyboard with the UITabBarController.

    The first step (ignore this step if you already have a UITabBarController custom subclass) is that you need to subclass UITabBarController. Let us call this class ToastyTabBarController for reference. Set this class on the UITabBarController in your storyboard using the identity inspector pane in xcode.

    The second step is to set the class of the UITabBar in your storyboard as ToastyTabBar (feel free to name it something more 'professional' 😊).

    This is to be done in the same storyboard, in your UITabBarController scene itself. It will show the tabBar under your UITabBarController, and you can set the custom class on it using the identity inspector pane just like earlier.

    The next step is to expose a computed property on your custom UITabBarController class, as shown below.

    var toastyTabBar: ToastyTabBar {
        self.tabBar as! ToastyTabBar 
    }
    

    And that's it. Now you have a property on your UITabBarController subclass which is of ToastyTabBar type and you can use this new property, toastyTabBar, however you require.

    Hope this helps.