SwiftUI - 隐藏 ScrollView 的指示器使其停止滚动

Posted

技术标签:

【中文标题】SwiftUI - 隐藏 ScrollView 的指示器使其停止滚动【英文标题】:SwiftUI - Hiding a ScrollView's indicators makes it stop scrolling 【发布时间】:2019-11-30 14:28:16 【问题描述】:

我试图隐藏 ScrollView 的指示符,但当我尝试这样做时,ScrollView 不再滚动。如果这很重要,我正在使用 macOS。

ScrollView(showsIndicators: false) 
    // Everything is in here

【问题讨论】:

看起来只有 macOS 的缺陷。在 ios 和 macCatalyst 上它可以工作。 我遇到了同样的问题,并希望它能在 macOS 10.15.1 和 Xcode 11.2 中得到修复。由于内容不再滚动,我还看到它仍然在顶部具有弹跳效果(有时也是如此)。 我面临同样的问题。在 iOS 上运行良好,但无法在 macOS 和 macCatalyst 上运行。 @Asperi - 你说它正在使用 macCatalyst - 你能展示你的代码吗? @SoOverIt,见答案。 这在 Big Sur 中已修复,但在 10.15.7 中仍未修复 【参考方案1】:

应@SoOverIt 的要求

演示:

没什么特别的,只是启动了一些其他的测试示例。 Xcode 11.2 / macOS 10.15

var body : some View 

    VStack 
        ScrollView([.vertical], showsIndicators: false) 
            Group 
            Text("AAA")
            Text("BBB")
            Text("CCC")
            Text("DDD")
            Text("EEE")
            
            Group 
            Text("AAA")
            Text("BBB")
            Text("CCC")
            Text("DDD")
            Text("EEE")
            
            Group 
            Text("AAA")
            Text("BBB")
            Text("CCC")
            Text("DDD")
            Text("EEE")
            
            Group 
            Text("AAA")
            Text("BBB")
            Text("CCC")
            Text("DDD")
            Text("EEE")
            
        
        .frame(height: 100)
        .border(Color.blue)
    
    .border(Color.red)

【讨论】:

这很奇怪!我将您的代码复制并粘贴到一个新的 macCatalyst 应用程序中 - 仍然出现滚动错误。我正在使用 Xcode 11.3.1 和 macOS 10.15.3。我有以前使用它的记忆 - 也许它在 11.3.1 上很无聊?请参阅下面的视频答案。【参考方案2】:

我解决了这个问题。

extension View 
    func hideIndicators() -> some View 
        return PanelScrollView self 
    


struct PanelScrollView<Content> : View where Content : View 
    let content: () -> Content

    var body: some View 
        PanelScrollViewControllerRepresentable(content: self.content())
    


struct PanelScrollViewControllerRepresentable<Content>: NSViewControllerRepresentable where Content: View
    func makeNSViewController(context: Context) -> PanelScrollViewHostingController<Content> 
        return PanelScrollViewHostingController(rootView: self.content)
    

    func updateNSViewController(_ nsViewController: PanelScrollViewHostingController<Content>, context: Context) 
    

    typealias NSViewControllerType = PanelScrollViewHostingController<Content>

    let content: Content




class PanelScrollViewHostingController<Content>: NSHostingController<Content> where Content : View 

    var scrollView: NSScrollView?

    override func viewDidAppear() 
        self.scrollView = findNSScrollView(view: self.view)
        self.scrollView?.scrollerStyle = .overlay
        self.scrollView?.hasVerticalScroller = false
        self.scrollView?.hasHorizontalScroller = false
        super.viewDidAppear()
    

    func findNSScrollView(view: NSView?) -> NSScrollView? 
        if view?.isKind(of: NSScrollView.self) ?? false 
            return (view as? NSScrollView)
        

        for v in view?.subviews ?? [] 
            if let vc = findNSScrollView(view: v) 
                return vc
            
        

        return nil
    

预览:

struct MyScrollView_Previews: PreviewProvider 
    static var previews: some View 
        ScrollView
            VStack
                Text("hello")
                Text("hello")
                Text("hello")
                Text("hello")
                Text("hello")
            
        .hideIndicators()
    

【讨论】:

希望他们能尽快修复它,但这暂时可行。我不会考虑这个,只是复制意大利面,谢谢! 好吧,这个修复也给我带来了@ObservedObject 重新渲染视图的问题……不知道该怎么做才能防止这种情况发生。 @neptunes 同样在这里...不再更新视图。不能用这个【参考方案3】:

所以...我认为这是目前唯一的方法。

您基本上只是在您的ScrollView 指示器上放置一个View,并使用与您的背景相同的backgroundColor View

注意:这显然只适用于您的背景是静态且后缘没有内容的情况。

想法

struct ContentView: View 
    
    @Environment(\.colorScheme) var colorScheme: ColorScheme
    
    let yourBackgroundColorLight: Color = .white
    let yourBackgroundColorDark: Color = .black

    var yourBackgroundColor: Color  colorScheme == .light ? yourBackgroundColorLight : yourBackgroundColorDark 
    
    
    var body: some View 
        ScrollView 
            VStack 
                ForEach(0..<1000)  i in
                    Text(String(i)).frame(width: 280).foregroundColor(.green)
                
            
        
        .background(yourBackgroundColor) //<-- Same
        .overlay(
            HStack 
                Spacer()
                Rectangle()
                    .frame(width: 10)
                    .foregroundColor(yourBackgroundColor) //<-- Same
            
        )
    

精简版

你可以这样改进,我想你已经在资产中动态设置了你的颜色。

用法:

ScrollView 
    ...

.hideIndicators(with: <#Your Color#>)

实施:

extension View 
    func hideIndicators(with color: Color) -> some View 
        return modifier(HideIndicators(color: color))
    



struct HideIndicators: ViewModifier 
    let color: Color
    
    func body(content: Content) -> some View 
        content
            .overlay(
                HStack 
                    Spacer()
                    Rectangle()
                        .frame(width: 10)
                        .foregroundColor(color)
                
            )
    

【讨论】:

以上是关于SwiftUI - 隐藏 ScrollView 的指示器使其停止滚动的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 淡出 ScrollView

SwiftUI:调整 ScrollView 的边界

SwiftUI:为啥这个 ScrollView 的内容错位了?

ScrollView 位置数据 / SwiftUi

SwiftUI - 如何获取 ScrollView 内容的大小(高度)

SwiftUI:奇怪的行为 ScrollView