UITabBarController中UISwipeGestureRecognizer引发滑动卡顿问题求助
Hey there! Let's work through that swipe lag issue you're facing. I took a look at your code, and the main culprit here is probably the repeated recursive call to UIApplication.topViewController() every time the swipe gesture fires—especially if your view controller hierarchy gets deep, that's a lot of unnecessary work happening on every swipe, leading to that laggy feel. Plus, right now even when the top controller isn't HomeVC, the gesture is still triggering the handler (even if it does nothing), which adds extra overhead.
Here's a step-by-step fix to smooth things out:
1. Control Gesture Enablement Dynamically
Instead of checking the top controller every time the swipe happens, we'll enable/disable the gesture recognizers entirely when the tab or view controller hierarchy changes. This way, the gesture doesn't even fire when it's not needed.
Update your TabBarController code like this:
class TabBarController: UITabBarController { // Store gestures as properties to easily toggle their state private var leftSwipe: UISwipeGestureRecognizer! private var rightSwipe: UISwipeGestureRecognizer! override func viewDidLoad() { super.viewDidLoad() self.selectedIndex = Values.menuSelectedIndex setupSwipeGestures() // Set initial gesture state updateGestureEnabledStatus() } private func setupSwipeGestures() { leftSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipes(_:))) rightSwipe = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipes(_:))) leftSwipe.direction = .left rightSwipe.direction = .right view.addGestureRecognizer(leftSwipe) view.addGestureRecognizer(rightSwipe) } @objc func handleSwipes(_ sender: UISwipeGestureRecognizer) { // Add boundary checks to prevent index out of bounds switch sender.direction { case .left: if selectedIndex < (viewControllers?.count ?? 0) - 1 { selectedIndex += 1 } case .right: if selectedIndex > 0 { selectedIndex -= 1 } default: break } } // Update gesture state when the selected tab changes override var selectedViewController: UIViewController? { didSet { updateGestureEnabledStatus() } } override var selectedIndex: Int { didSet { updateGestureEnabledStatus() } } private func updateGestureEnabledStatus() { // Only enable gestures if the top controller is HomeVC let shouldEnableGestures = UIApplication.topViewController() is HomeVC leftSwipe.isEnabled = shouldEnableGestures rightSwipe.isEnabled = shouldEnableGestures } }
2. Optimize the Top Controller Lookup
Your existing topViewController() extension works, but it recursively traverses the hierarchy every time it's called. Adding a simple cache can reduce redundant work:
extension UIApplication { private static var cachedTopViewController: UIViewController? class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? { // Return cached value if it's still valid (no presented controller on top) if let cached = cachedTopViewController, cached.presentedViewController == nil { return cached } var currentController = controller while let nextController = currentController?.presentedViewController ?? (currentController as? UINavigationController)?.visibleViewController ?? (currentController as? UITabBarController)?.selectedViewController { currentController = nextController } cachedTopViewController = currentController return currentController } // Call this when dismissing a presented controller to invalidate the cache class func invalidateTopViewControllerCache() { cachedTopViewController = nil } }
Just remember to call UIApplication.invalidateTopViewControllerCache() in places like viewWillDisappear of presented view controllers, or when dismissing modals, to keep the cache accurate.
Why This Works
- By disabling gestures when they're not needed, we eliminate the overhead of running the handler and hierarchy lookup on every swipe.
- The cache in the extension cuts down on repeated recursive traversals of your view controller hierarchy, which was likely a big contributor to the lag.
- Adding boundary checks to the swipe handler prevents invalid index changes, making the code more robust.
内容的提问来源于stack exchange,提问作者Dan Bray

