如何通过编程实现NSScrollView滚动?含平滑滚动实现问询
解决NSScrollView编程式平滑滚动的问题
我来帮你搞定这个NSScrollView的平滑滚动需求!你遇到的问题其实很多Mac开发新手都会碰到——visibleRect只读、scrollLineDown()不符合平滑预期,下面给你两种靠谱的实现方案:
一、逐帧平滑滚动(推荐,和屏幕刷新率同步)
用CADisplayLink是最理想的选择,它会和屏幕的刷新频率(通常60Hz)保持同步,每帧滚动1像素,视觉上完全平滑。
代码实现(Swift)
首先在你的视图控制器或持有NSScrollView的类里声明一个属性:
private var scrollDisplayLink: CADisplayLink?
然后实现启动、停止滚动的方法:
// 启动平滑滚动 func startSmoothScroll() { // 先停止之前的滚动(防止重复启动) stopSmoothScroll() scrollDisplayLink = CADisplayLink(target: self, selector: #selector(updateScrollPosition)) scrollDisplayLink?.add(to: .current, forMode: .common) } // 每帧更新滚动位置 @objc private func updateScrollPosition() { guard let scrollView = yourScrollView else { return } // 获取当前滚动偏移量(contentView的bounds.origin就是滚动的核心位置) var currentOffset = scrollView.contentView.bounds.origin // 每帧向下滚动1像素,你可以根据需求调整这个数值 currentOffset.y += 1 // 计算最大可滚动偏移量,避免滚出内容底部 let maxScrollY = (scrollView.documentView?.frame.height ?? 0) - scrollView.contentView.bounds.height if currentOffset.y >= maxScrollY { // 滚动到底部后自动停止 currentOffset.y = maxScrollY stopSmoothScroll() } // 应用新的偏移量 scrollView.contentView.bounds.origin = currentOffset } // 停止滚动 func stopSmoothScroll() { scrollDisplayLink?.invalidate() scrollDisplayLink = nil }
二、Timer定时滚动(退而求其次的方案)
如果觉得CADisplayLink有点复杂,用Timer定时更新滚动位置也可以,比如每0.1秒滚动一定距离:
代码实现(Swift)
同样先声明属性:
private var scrollTimer: Timer?
然后实现启动停止方法:
// 启动定时滚动 func startTimerScroll() { stopTimerScroll() scrollTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in guard let self = self, let scrollView = self.yourScrollView else { return } var currentOffset = scrollView.contentView.bounds.origin // 每0.1秒滚动6像素,和60帧每秒的1像素/帧速度差不多 currentOffset.y += 6 let maxScrollY = (scrollView.documentView?.frame.height ?? 0) - scrollView.contentView.bounds.height if currentOffset.y >= maxScrollY { currentOffset.y = maxScrollY self.stopTimerScroll() } scrollView.contentView.bounds.origin = currentOffset } } // 停止定时滚动 func stopTimerScroll() { scrollTimer?.invalidate() scrollTimer = nil }
补充说明
- 为什么
scrollLineDown()不好用?因为这个方法是按照系统默认的行高来滚动的,而且是瞬间完成,没有平滑过渡,完全达不到逐像素滚动的效果。 visibleRect确实是只读属性,它只是用来获取当前可见区域的范围,不能用来设置滚动位置,正确的做法是操作contentView的bounds.origin。- 如果只是想一次性平滑滚动到某个固定位置,还可以用系统的动画方法:
// 滚动到y=1000的位置,带系统平滑动画 scrollView.animator().scroll(to: CGRect(x: 0, y: 1000, width: scrollView.contentView.bounds.width, height: scrollView.contentView.bounds.height))
内容的提问来源于stack exchange,提问作者Zac
相关产品推荐
相关产品推荐

