子视图控制器滚动时隐藏父视图导航栏与侧滑菜单方案
实现滚动时隐藏父视图控制器的导航栏与侧边菜单
这个需求其实很常见,核心就是让子视图控制器把滚动状态传递给父VC,由父VC来控制导航栏和侧边菜单的显隐。下面给你一步步拆解实现方案:
1. 子VC监听滚动事件
首先要在子VC里捕捉滚动行为——因为你的层级是scrollView -> view -> textView,其实直接监听UITextView的滚动就行(它本身是UIScrollView的子类),或者监听外层的scrollView都可以。
Swift 实现示例
在子VC的viewDidLoad里设置监听:
override func viewDidLoad() { super.viewDidLoad() // 用UIScrollViewDelegate监听滚动 textView.delegate = self // 或者给外层scrollView设置delegate // scrollView.delegate = self } // 记录上一次的滚动偏移量,用来判断滚动方向 private var lastScrollOffset: CGFloat = 0 extension YourChildViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { let currentOffset = scrollView.contentOffset.y // 判断是否向下滚动 let isScrollingDown = currentOffset > lastScrollOffset // 加个20pt的阈值,避免轻微滚动就触发UI变化,提升体验 if abs(currentOffset - lastScrollOffset) > 20 { // 把滚动状态传递给父VC if let parentVC = parent as? YourParentViewController { parentVC.toggleNavAndMenu(hide: isScrollingDown) } } // 更新上一次的偏移量 lastScrollOffset = currentOffset } }
Objective-C 实现示例
- (void)viewDidLoad { [super viewDidLoad]; self.textView.delegate = self; // 或者 self.scrollView.delegate = self; } static CGFloat lastScrollOffset = 0; - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat currentOffset = scrollView.contentOffset.y; BOOL isScrollingDown = currentOffset > lastScrollOffset; if (abs(currentOffset - lastScrollOffset) > 20) { YourParentViewController *parentVC = (YourParentViewController *)self.parentViewController; [parentVC toggleNavAndMenuHide:isScrollingDown]; } lastScrollOffset = currentOffset; }
2. 父VC实现UI显隐逻辑
在父VC里定义一个公开方法,用来处理导航栏和侧边菜单的显隐,加上动画让过渡更自然:
Swift 实现示例
// 记录当前状态,避免重复触发 private var isNavAndMenuHidden = false func toggleNavAndMenu(hide: Bool) { // 如果当前状态和要设置的状态一致,直接返回,避免重复动画 guard hide != isNavAndMenuHidden else { return } isNavAndMenuHidden = hide UIView.animate(withDuration: 0.3) { // 隐藏/显示导航栏 self.navigationController?.setNavigationBarHidden(hide, animated: true) // 处理侧边菜单:假设你的侧边菜单是一个UIView实例 self.sliderMenuView.isHidden = hide // 如果是自定义的抽屉式菜单,调用它的隐藏方法即可,比如: // self.sliderMenu.hide(animated: true) } }
Objective-C 实现示例
static BOOL isNavAndMenuHidden = NO; - (void)toggleNavAndMenuHide:(BOOL)hide { if (hide == isNavAndMenuHidden) return; isNavAndMenuHidden = hide; [UIView animateWithDuration:0.3 animations:^{ [self.navigationController setNavigationBarHidden:hide animated:YES]; self.sliderMenuView.hidden = hide; // 自定义菜单的隐藏逻辑,比如: // [self.sliderMenu hideAnimated:YES]; }]; }
3. 进阶优化:用协议解耦(更优雅)
上面的写法需要子VC强转父VC类型,耦合性有点高。推荐用协议的方式来解耦,让代码更灵活:
Swift 协议实现
// 定义协议 protocol ScrollNavigationDelegate: AnyObject { func shouldHideNavigationAndMenu(_ hide: Bool) } // 子VC里添加协议属性 class YourChildViewController: UIViewController { weak var navigationDelegate: ScrollNavigationDelegate? // ...其他代码 } // 滚动时调用协议方法 extension YourChildViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { // ...判断滚动方向的逻辑不变 navigationDelegate?.shouldHideNavigationAndMenu(isScrollingDown) } } // 父VC遵守协议 extension YourParentViewController: ScrollNavigationDelegate { func shouldHideNavigationAndMenu(_ hide: Bool) { toggleNavAndMenu(hide: hide) } } // 父VC添加子VC时设置delegate func addChildViewController() { let childVC = YourChildViewController() childVC.navigationDelegate = self // ...添加子VC到container view的代码 }
注意事项
- 动态加载内容时,确保滚动监听不会失效:每次从API加载完textView内容后,检查delegate是否还正确设置。
- 调整阈值:20pt只是参考值,你可以根据实际产品体验调整,比如改成15pt或者30pt。
- 边界情况处理:比如滚动到顶部时,应该自动显示导航栏和菜单,你可以在
scrollViewDidScroll里判断contentOffset.y <= 0时强制设置为显示状态。
内容的提问来源于stack exchange,提问作者Hasnain Kanji
相关产品推荐
相关产品推荐

