NSScrollView封装NSCollectionView在调整大小时总是向后滚动
NSScrollView encapsulating NSCollectionView always scrolling back when resizing
程序打开一个 window,其中包含一个包含 1000 个项目的集合视图。如果我向下滚动一点,然后向任何方向调整 window 的大小,滚动视图将立即跳回顶部。
应用程序是在没有 Storyboard 或 XIB 的情况下构建的。 通过 Interface Builder 构建类似的应用程序时不会出现此问题。 似乎缺少某些东西,Interface Builder 默认配置的东西。我正在 macOS 11.2.3 上使用 Xcode 12.4 进行测试。有什么想法吗?
为了便于重现,我将所有内容打包到一个文件中 main.swift
.:
import Cocoa
let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
class AppDelegate: NSObject, NSApplicationDelegate, NSSplitViewDelegate {
var window: NSWindow?
var dataSource: CollectionViewDataSource?
var collectionView: NSCollectionView?
func applicationDidFinishLaunching(_ aNotification: Notification) {
let screenSize = NSScreen.main?.frame.size ?? NSSize(width: 1920, height: 1080)
window = NSWindow(contentRect: NSMakeRect(screenSize.width/4, screenSize.height/4, screenSize.width/2, screenSize.height/2),
styleMask: [.miniaturizable, .closable, .resizable, .titled],
backing: .buffered,
defer: false)
window?.makeKeyAndOrderFront(nil)
if let view = window?.contentView {
collectionView = NSCollectionView(frame: NSZeroRect)
dataSource = CollectionViewDataSource()
let scrollView = NSScrollView(frame: NSZeroRect)
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
scrollView.documentView = collectionView
collectionView?.collectionViewLayout = NSCollectionViewFlowLayout()
collectionView?.register(CollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CollectionViewItem"))
collectionView?.dataSource = dataSource
}
}
}
class CollectionViewDataSource: NSObject, NSCollectionViewDataSource {
func numberOfSections(in collectionView: NSCollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 1000
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let i = collectionView.makeItem(withIdentifier:NSUserInterfaceItemIdentifier(rawValue: "CollectionViewItem"), for:indexPath) as! CollectionViewItem
i.view.wantsLayer = true
i.view.layer?.backgroundColor = NSColor.init(colorSpace: NSColorSpace.deviceRGB, hue: CGFloat(Float.random(in: 0..<1)), saturation: CGFloat(Float.random(in: 0.4...1)), brightness: CGFloat(Float.random(in: 0.5...1)), alpha: 1).cgColor
return i
}
func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {
fatalError("Not implemented")
}
}
class CollectionViewItem : NSCollectionViewItem {
override func loadView() {
self.view = NSView(frame: NSZeroRect)
}
}
故事板中集合视图的源代码:
<collectionView id="yh4-in-fLt">
<rect key="frame" x="0.0" y="0.0" width="480" height="158"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="10" minimumLineSpacing="10" id="TGK-mX-O4r">
<size key="itemSize" width="50" height="50"/>
</collectionViewFlowLayout>
<color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</collectionView>
但是 autoresizingMask
在 IB 中不可见。设置 autoresizingMask
解决了这个问题。
collectionView.autoresizingMask = [.width]
忽略帧更改通知似乎可以解决问题。
添加自定义 NSClipView
作为 NSScrollView
的内容视图,如下所示:
scrollView.contentView = ClipViewIgnoringFrameChange()
其中 ClipViewIgnoringFrameChange()
定义如下:
class ClipViewIgnoringFrameChange : NSClipView {
override func viewFrameChanged(_ notification: Notification) {}
}
程序打开一个 window,其中包含一个包含 1000 个项目的集合视图。如果我向下滚动一点,然后向任何方向调整 window 的大小,滚动视图将立即跳回顶部。
应用程序是在没有 Storyboard 或 XIB 的情况下构建的。 通过 Interface Builder 构建类似的应用程序时不会出现此问题。 似乎缺少某些东西,Interface Builder 默认配置的东西。我正在 macOS 11.2.3 上使用 Xcode 12.4 进行测试。有什么想法吗?
为了便于重现,我将所有内容打包到一个文件中 main.swift
.:
import Cocoa
let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
class AppDelegate: NSObject, NSApplicationDelegate, NSSplitViewDelegate {
var window: NSWindow?
var dataSource: CollectionViewDataSource?
var collectionView: NSCollectionView?
func applicationDidFinishLaunching(_ aNotification: Notification) {
let screenSize = NSScreen.main?.frame.size ?? NSSize(width: 1920, height: 1080)
window = NSWindow(contentRect: NSMakeRect(screenSize.width/4, screenSize.height/4, screenSize.width/2, screenSize.height/2),
styleMask: [.miniaturizable, .closable, .resizable, .titled],
backing: .buffered,
defer: false)
window?.makeKeyAndOrderFront(nil)
if let view = window?.contentView {
collectionView = NSCollectionView(frame: NSZeroRect)
dataSource = CollectionViewDataSource()
let scrollView = NSScrollView(frame: NSZeroRect)
view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
scrollView.documentView = collectionView
collectionView?.collectionViewLayout = NSCollectionViewFlowLayout()
collectionView?.register(CollectionViewItem.self, forItemWithIdentifier: NSUserInterfaceItemIdentifier(rawValue: "CollectionViewItem"))
collectionView?.dataSource = dataSource
}
}
}
class CollectionViewDataSource: NSObject, NSCollectionViewDataSource {
func numberOfSections(in collectionView: NSCollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
return 1000
}
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
let i = collectionView.makeItem(withIdentifier:NSUserInterfaceItemIdentifier(rawValue: "CollectionViewItem"), for:indexPath) as! CollectionViewItem
i.view.wantsLayer = true
i.view.layer?.backgroundColor = NSColor.init(colorSpace: NSColorSpace.deviceRGB, hue: CGFloat(Float.random(in: 0..<1)), saturation: CGFloat(Float.random(in: 0.4...1)), brightness: CGFloat(Float.random(in: 0.5...1)), alpha: 1).cgColor
return i
}
func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView {
fatalError("Not implemented")
}
}
class CollectionViewItem : NSCollectionViewItem {
override func loadView() {
self.view = NSView(frame: NSZeroRect)
}
}
故事板中集合视图的源代码:
<collectionView id="yh4-in-fLt">
<rect key="frame" x="0.0" y="0.0" width="480" height="158"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumInteritemSpacing="10" minimumLineSpacing="10" id="TGK-mX-O4r">
<size key="itemSize" width="50" height="50"/>
</collectionViewFlowLayout>
<color key="primaryBackgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</collectionView>
但是 autoresizingMask
在 IB 中不可见。设置 autoresizingMask
解决了这个问题。
collectionView.autoresizingMask = [.width]
忽略帧更改通知似乎可以解决问题。
添加自定义 NSClipView
作为 NSScrollView
的内容视图,如下所示:
scrollView.contentView = ClipViewIgnoringFrameChange()
其中 ClipViewIgnoringFrameChange()
定义如下:
class ClipViewIgnoringFrameChange : NSClipView {
override func viewFrameChanged(_ notification: Notification) {}
}