На это есть куча ответов. В 2019 году лучший способ сделать это - установить ограничения на панели SplitView, а затем анимировать ограничения.
Предположим, у меня есть SplitView
с тремя панелями: leftPane
, middlePane
, rightPane
. Я хочу не просто сворачивать две панели сбоку, я также хочу динамически изменять размеры ширины различных панелей при входе или выходе определенных видов.
В IB я установил ограничение WIDTH для каждой из трех панелей. leftPane
и rightPane
имеют ширину, установленную на 250
с приоритетом 1000 (required)
.
В коде это выглядит так:
@class MyController: NSViewController
{
@IBOutlet var splitView: NSSplitView!
@IBOutlet var leftPane: NSView!
@IBOutlet var middlePane: NSView!
@IBOutlet var rightPane: NSView!
@IBOutlet var leftWidthConstraint: NSLayoutConstraint!
@IBOutlet var middleWidthConstraint: NSLayoutConstraint!
@IBOutlet var rightWidthConstraint: NSLayoutConstraint!
override func awakeFromNib() {
// We use these in our animation, but want them off normally so the panes
// can be resized as normal via user drags, window changes, etc.
leftWidthConstraint.isActive = false
middleWidthConstraint.isActive = false
rightWidthConstraint.isActive = false
}
func collapseRightPane()
{
NSAnimationContext.runAnimationGroup({ (context) in
context.allowsImplicitAnimation = true
context.duration = 0.15
rightWidthConstraint.constant = 0
rightWidthConstraint.isActive = true
// Critical! Call this in the animation block or you don't get animated changes:
splitView.layoutSubtreeIfNeeded()
}) { [unowned self] in
// We need to tell the splitView to re-layout itself before we can
// remove the constraint, or it jumps back to how it was before animating.
// This process tells the layout engine to recalculate and update
// the frames of everything based on current constraints:
self.splitView.needsLayout = true
self.splitView.needsUpdateConstraints = true
self.splitView.needsDisplay = true
self.splitView.layoutSubtreeIfNeeded()
self.splitView.displayIfNeeded()
// Now, disable the width constraint so we can resize the splitView
// via mouse, etc:
self.middleWidthConstraint.isActive = false
}
}
}
extension MyController: NSSplitViewDelegate
{
final func splitView(_ splitView: NSSplitView, canCollapseSubview subview: NSView) -> Bool
{
// Allow collapsing. You might set an iVar that you can control
// if you don't want the user to be able to drag-collapse. Set the
// ivar to false usually, but set it to TRUE in the animation block
// block, before changing the constraints, then back to false in
// in the animation completion handler.
return true
}
final func splitView(_ splitView: NSSplitView, shouldHideDividerAt dividerIndex: Int) -> Bool {
// Definitely do this. Nobody wants a crappy divider hanging out
// on the side of a collapsed pane.
return true
}
}
Вы можете усложнить этот блок анимации. Например, вы можете решить, что хотите свернуть правую панель, но одновременно увеличить среднюю до 500px.
Преимущество этого подхода перед другими, перечисленными здесь, заключается в том, что он будет автоматически обрабатывать случаи, когда рамка окна в настоящее время недостаточно велика, чтобы приспособиться к "расширению" свернутой панели. Кроме того, вы можете использовать это, чтобы изменять размеры панелей ЛЮБЫМ способом, а не просто расширять и сворачивать их. Вы также можете сделать так, чтобы все эти изменения происходили одновременно в плавной комбинированной анимации.
Примечания:
- Очевидно, что представления, составляющие
leftPane
, middlePane
и rightPane
, никогда не меняются. Это «контейнеры», в которые вы добавляете / удаляете другие представления по мере необходимости. Если вы удалите виды панели из SplitView, вы уничтожите ограничения, установленные в IB.
- При использовании AutoLayout, если вы устанавливаете кадры вручную, вы боретесь с системой. Вы устанавливаете ограничения; механизм автоматического размещения устанавливает кадры.
- Подход
-setPosition:ofDividerAtIndex:
не работает, когда splitView недостаточно велик, чтобы установить делитель там, где вы хотите его видеть. Например, если вы хотите, чтобы ООН свернула правую панель и присвоила ей 500
ширину, но все ваше окно в настоящее время имеет ширину 300
. Это также может привести к путанице, если вам нужно изменить размер нескольких панелей одновременно.
- Вы можете использовать этот подход, чтобы делать больше. Например, может быть, вы хотите установить минимальную и максимальную ширину для различных панелей в splitView. Сделайте это с ограничениями, затем измените константы ограничения ширины
min
и max
по мере необходимости (возможно, когда на каждой панели появляются разные представления и т. Д.).
КРИТИЧЕСКОЕ ПРИМЕЧАНИЕ:
Этот подход потерпит неудачу, если какое-либо подпредставление в одной из панелей имеет ограничение width
или minimumWidth
с приоритетом 1000
. Вы получите уведомление «Не удается удовлетворить ограничения» в журнале. Вам нужно убедиться, что ваши подпредставления (и их дочерние представления, вплоть до иерархии) не имеют ограничения ширины, установленного в приоритет 1000
. Используйте 999 или меньше для таких ограничений, чтобы splitView всегда мог переопределить их, чтобы свернуть представление.