简体   繁体   中英

NScrollView in NSPopover autolayout issue

I have an NSPopover which contains an NSViewController with a containing NSScrollView .

The Popover height has to be either the height of the NSScrollView content or the current window. Once it hits the bounds of the window it should scroll.

Using Snapkit

I have added the NSScrollView to the controller:

view.addSubview(scrollView)
scrollView.snp.makeConstraints { (make) in
   make.edges.equalTo(view)
   make.height.equalTo(mainView.content.snp.height)
}

This works fine until the content is greater than the window, then what happens is the NSScrollView will not scroll to the top of the content because the view has pushed itself upwards out of bounds.

I have gone down the route of removing the height constraint and in the viewDidLayout try to update the height but it doesn't work.

If more code examples are needed let me know.

Finally got to the bottom of the issue and found a sensible solution.

The app I am developing has a few popovers that are required at various stages, to ensure that they closed as required I created a service that manages every popover, here is an example:

class PopoverService: NSObject {

    enum PopoverType {
        case subscription, edit
    }

    //================================================================================
    // MARK: - Properties
    //================================================================================

    private var dismissingPopover = false
    private lazy var currentPopover: NSPopover = {
        let popover = NSPopover()
        popover.delegate = self
        return popover
    }()

    private var nextPopoverType: PopoverType?
    private var currentView: NSView!

    public static var delegate: PopoverServiceDelegate?

    //================================================================================
    // MARK: - Singleton
    //================================================================================

    static let shared = PopoverService()

    //================================================================================
    // MARK: - Helpers
    //================================================================================

    public static func increaseHeight(_ height: CGFloat) {
        shared.currentPopover.contentSize.height = height
    }

    public static func isDisplayingType(_ type: PopoverType) -> Bool {
        switch type {
        case .edit:
            return shared.currentPopover.contentViewController is EditEntryController
        case .language:
            return shared.currentPopover.contentViewController is CodeTypeController
        default:
            return false
        }
    }

    public static func displayPopover(type: PopoverType, fromView view: NSView) {
        shared.nextPopoverType = type
        shared.currentView = view

        switch type {
        case .subscription:
            displaySubscriptionPopoverFrom(view)
        // Create functions to display your popovers
        }
    }

    static func dismissPopover(clearUpcoming: Bool = true) {
        if clearUpcoming {
            shared.nextPopoverType = nil
        }

        shared.currentPopover.performClose(nil)
        if shared.currentPopover.contentViewController == nil {
            shared.dismissingPopover = false; return
        }
    }

}

extension PopoverService: NSPopoverDelegate {

    func popoverDidClose(_ notification: Notification) {
        currentPopover.contentViewController = nil
        dismissingPopover = false

        guard let nextPopoverType = nextPopoverType else { return }
        PopoverService.displayPopover(
            type: nextPopoverType,
            fromView: currentView,
            entry: currentEntry
        )
    }

}

To update the current popover, there is a function increaseHeight which takes and CGFloat and will update the current popovers height.

In the NSViewController override the viewDidLayout() :

    override func viewDidLayout() {
        super.viewDidLayout()

        let windowFrameHeight = view.window?.frame.size.height ?? 0
        let contentHeight = scrollView.content.frame.height
        let adjustment = contentHeight > windowFrameHeight ? windowFrameHeight : contentHeight
        PopoverService.increaseHeight(adjustment)

        if contentHeight > 0 && firstLayout {
            if let documentView = scrollView.documentView {
                documentView.scroll(NSPoint(x: 0, y: documentView.bounds.size.height))
            }
        }
    }

The scrollView will need to be forced to the top so there is a variable firstLayout which you can set to true in the viewDidAppear

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM