Установить позицию nspopover

У меня есть контроллер представления (A), который покажет другой контроллер представления (B) в качестве всплывающего окна.

В моем VC (A) есть NSButton с этим IBAction:

self.presentViewController(vcPopover, asPopoverRelativeTo: myButton.bounds, of: myButton, preferredEdge: .maxX, behavior: .semitransient)

Результат:

введите здесь описание изображения

теперь я хотел бы изменить положение моего всплывающего окна - я хотел бы переместить его вверх.

Я пробовал это:

let position = NSRect(origin: CGPoint(x: 100.0, y: 120.0), size: CGSize(width: 0.0, height: 0.0))
self.presentViewController(vcPopover, asPopoverRelativeTo: position, of: myButton, preferredEdge: .maxX, behavior: .semitransient)

Но положение не меняется

ДРУГОЙ ПРИМЕР

введите здесь описание изображения

У меня есть сегментированный контроль. Если вы нажмете на сегмент «1», появится всплывающее окно (тот же код, что и выше). Но стрелка указывала на сегмент «2», а не на сегмент «1».


person Ghost108    schedule 03.09.2017    source источник


Ответы (1)


Во-первых, убедитесь, что ваш поповер действительно NSPopover, а не просто NSViewController. Предполагая, что контроллер представления, который вы хотите обернуть во всплывающее окно, имеет идентификатор раскадровки «vcPopover», получение содержимого vc будет выглядеть так:

let popoverContentController = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "vcPopover")) as! NSViewController

Затем оберните его в поповер:

let popover = NSPopover()
popover.contentSize = NSSize(width: 200, height: 200) // Or whatever size you want, perhaps based on the size of the content controller
popover.behavior = .semitransient
popover.animates = true
popover.contentViewController = popoverContentController    

Затем для презентации вызовите show(relativeTo:of:preferredEdge:):

vcPopover.show(relativeTo: myButton.bounds, of: myButton, preferredEdge: .maxX)

Это должно обновить позицию всплывающего окна.

Обновление: вы, вероятно, используете NSSegmentedControl, а это значит, что вам нужно обратить особое внимание на прямоугольник, который вы передаете в show. Вам необходимо передать прямоугольник границ в системе координат сегментированного элемента управления, который описывает площадь сегмента. Вот подробный пример:

// The view controller doing the presenting
class ViewController: NSViewController {

    ...

    var presentedPopover: NSPopover?

    @IBAction func selectionChanged(_ sender: NSSegmentedControl) {
        let segment = sender.selectedSegment

        if let storyboard = storyboard {
            let contentVC = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("vcPopover")) as! NSViewController

            presentedPopover = NSPopover()
            presentedPopover?.contentSize = NSSize(width: 200, height: 200)
            presentedPopover?.behavior = .semitransient
            presentedPopover?.animates = true
            presentedPopover?.contentViewController = contentVC
        }

        presentedPopover?.show(relativeTo: sender.relativeBounds(forSegment: segment), of: sender, preferredEdge: .minY)
    }

}

extension NSSegmentedControl {

    func relativeBounds(forSegment index: Int) -> NSRect {
        // Assuming equal widths
        let segmentWidth = bounds.width / CGFloat(segmentCount)

        var rect = bounds
        rect.size.width = segmentWidth
        rect.origin.x = rect.origin.x + segmentWidth * CGFloat(index)

        return rect
    }

}

Обратите внимание, что расширение NSSegmentedControl вычисляет приблизительный прямоугольник для сегмента, используя ширину. Этот метод предполагает равную ширину и не учитывает границы. Вы можете изменить этот метод, чтобы учесть то, что вам нужно. Информацию о получении кадра сегмента для iOS (что аналогично) можно найти здесь< /а>.

Этот пример проверен на правильность работы, если контроллер представления существует в той же раскадровке с идентификатором раскадровки "vcPopover".

person Matthew Seaman    schedule 02.10.2017
comment
Я объявил свой vcPopover следующим образом: let vcPopover = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil).instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "vcPopover")) as! NSViewController. Я получаю сообщение об ошибке: значение типа «NSViewController» не имеет члена «show» - person Ghost108; 02.10.2017
comment
@ Ghost108 Я обновил свой ответ инструкциями по созданию файла NSPopover. - person Matthew Seaman; 02.10.2017
comment
Я попробовал ваш код, но результат такой же, как на картинке выше. стрелка всплывающего окна будет указывать на второй сегмент вместо первого сегмента. я играл со значениями CGPoint(x: 100.0, y: 120.0), но ничего не изменилось - person Ghost108; 02.10.2017
comment
Вы просто используете горизонтальные NSButtons или NSSegmentedControl? В случае последнего, откуда вы берёте myButton? - person Matthew Seaman; 02.10.2017
comment
@Ghost108 Приношу свои извинения. Я создал пример проекта и смог заставить мою версию работать. Я добавлю свой код в свой ответ. - person Matthew Seaman; 03.10.2017
comment
Если я попробую вашу последнюю версию кода, всплывающее окно не появится, если я нажму кнопку - person Ghost108; 03.10.2017
comment
@ Ghost108 Вы подключили IBAction к элементу управления? - person Matthew Seaman; 03.10.2017
comment
конечно ... я также получаю печать (привет), если нажимаю кнопку, но не всплывающее окно - person Ghost108; 03.10.2017
comment
@ Ghost108 Я убедился, что код работает в моем собственном проекте, так что, вероятно, это небольшая разница в том, как вы настроили свой проект. Контроллер представления выполняет представление, созданное из той же раскадровки? Если вы зарегистрируетесь в отладчике, вы можете обнаружить, что «self.storyboard» возвращает «nil», и вы можете использовать свой оригинальный метод получения раскадровки. - person Matthew Seaman; 03.10.2017
comment
Я использую только одну раскадровку. Вы можете прислать мне свой пример проекта, чтобы я мог сравнить? - person Ghost108; 03.10.2017