Динамическое добавление ячеек в NSMatrix, выложенное с помощью Auto Layout, имеет странные эффекты; Зачем?

Я хочу создать группу переключателей, используя метод NSMatrix, который использует интерфейсный конструктор, но в коде. Матрица выложена с помощью Auto Layout. У меня это в основном работает, за исключением случаев, когда я добавляю новые параметры во время выполнения.

В следующем примере нажатие кнопки «Добавить элемент» несколько раз будет работать нормально, после чего матрица начнет выходить за пределы окна в верхней части (по крайней мере, я думаю, что она обрезана вверху). Если вы развернете это окно после добавления множества элементов, окно останется той же высоты, и все элементы будут обрезаны примерно до высоты пикселя каждый, что очень нежелательно :)

В моей реальной программе (а не в этом тесте ниже) она работает в основном нормально, но если я добавляю параметр динамически, после определенного количества элементов (первоначально 5), параметры будут очень незначительно обрезаться, кажутся слегка сжатыми или сплющенными. Добавление другой опции отменяет это до тех пор, пока не будет достигнуто следующее магическое число.

Что происходит? Я тестирую это на OS X Yosemite. Спасибо.

// 17 august 2015
import Cocoa

var keepAliveMainwin: NSWindow? = nil
var matrix: NSMatrix? = nil

class ButtonHandler : NSObject {
    @IBAction func onClicked(sender: AnyObject) {
        var lastRow = matrix!.numberOfRows
        matrix!.renewRows(lastRow + 1, columns: 1)
        var cell = matrix!.cellAtRow(lastRow, column: 0) as! NSButtonCell
        cell.title = "New Item"
        matrix!.sizeToCells()
    }
}

var buttonHandler: ButtonHandler = ButtonHandler()

func appLaunched() {
    var mainwin = NSWindow(
        contentRect: NSMakeRect(0, 0, 320, 240),
        styleMask: (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask),
        backing: NSBackingStoreType.Buffered,
        defer: true)
    var contentView = mainwin.contentView as! NSView

    var prototype = NSButtonCell()
    prototype.setButtonType(NSButtonType.RadioButton)
    prototype.font = NSFont.systemFontOfSize(NSFont.systemFontSizeForControlSize(NSControlSize.RegularControlSize))

    matrix = NSMatrix(frame: NSZeroRect,
        mode: NSMatrixMode.RadioModeMatrix,
        prototype: prototype,
        numberOfRows: 0,
        numberOfColumns: 0)
    matrix!.allowsEmptySelection = false
    matrix!.selectionByRect = true
    matrix!.intercellSpacing = NSMakeSize(4, 2)
    matrix!.autorecalculatesCellSize = true
    matrix!.drawsBackground = false
    matrix!.drawsCellBackground = false
    matrix!.autosizesCells = true
    matrix!.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(matrix!)

    var button = NSButton(frame: NSZeroRect)
    button.title = "Append Item"
    button.setButtonType(NSButtonType.MomentaryPushInButton)
    button.bordered = true
    button.bezelStyle = NSBezelStyle.RoundedBezelStyle
    button.font = NSFont.systemFontOfSize(NSFont.systemFontSizeForControlSize(NSControlSize.RegularControlSize))
    button.translatesAutoresizingMaskIntoConstraints = false
    contentView.addSubview(button)

    button.target = buttonHandler
    button.action = "onClicked:"

    var views: [String: NSView]
    views = [
        "button":   button,
        "matrix":   matrix!,
    ]
    addConstraints(contentView, "V:|-[matrix]-[button]-|", views)
    addConstraints(contentView, "H:|-[matrix]-|", views)
    addConstraints(contentView, "H:|-[button]-|", views)

    mainwin.cascadeTopLeftFromPoint(NSMakePoint(20, 20))
    mainwin.makeKeyAndOrderFront(mainwin)
    keepAliveMainwin = mainwin
}

func addConstraints(view: NSView, constraint: String, views: [String: NSView]) {
    var constraints = NSLayoutConstraint.constraintsWithVisualFormat(
        constraint,
        options: NSLayoutFormatOptions(0),
        metrics: nil,
        views: views)
    view.addConstraints(constraints)
}

class appDelegate : NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(note: NSNotification) {
        appLaunched()
    }

    func applicationShouldTerminateAfterLastWindowClosed(app: NSApplication) -> Bool {
        return true
    }
}

func main() {
    var app = NSApplication.sharedApplication()
    app.setActivationPolicy(NSApplicationActivationPolicy.Regular)
    // NSApplication.delegate is weak; if we don't use the temporary variable, the delegate will die before it's used
    var delegate = appDelegate()
    app.delegate = delegate
    app.run()
}

main()

person andlabs    schedule 23.08.2015    source источник
comment
Что вы ожидаете/хотите сделать, если матрица слишком высока, чтобы поместиться в окне (за вычетом места для кнопки)? Вы пытались установить autosizesCells в false? Вы хотите, чтобы внутренний размер матрицы отражал размер ее ячеек; вы не хотите, чтобы матрица изменяла размер ячеек в зависимости от ее текущего размера. Также приоритеты сопротивления сжатию матрицы (горизонтальные и вертикальные) выставлены как хотите? В частности, если вы хотите, чтобы окно увеличивалось, когда матрица становится слишком высокой, сопротивление сжатию должно быть больше 500 (NSLayoutPriorityWindowSizeStayPut).   -  person Ken Thomases    schedule 23.08.2015
comment
Хорошие вопросы. Я хочу, чтобы NSMatrix рос, чтобы вместить новую ячейку, но без сжатия других ячеек. Я попробовал autosizesCells из false в реальной программе, и это не дало желаемого эффекта, хотя я не помню, какой. Я также попробую материал сопротивления сжатию и сообщу обо всем, а затем отредактирую вопрос, чтобы он был немного более ясным. Спасибо.   -  person andlabs    schedule 23.08.2015
comment
Верно, но когда матрица становится выше окна, чего вы ожидаете? Окно должно увеличиваться? Как насчет того, чтобы окно было настолько высоким, насколько может поместиться на экране? Вы хотите, чтобы матрица прокручивалась? В этом случае он должен быть в режиме прокрутки.   -  person Ken Thomases    schedule 23.08.2015
comment
Когда матрица становится выше окна, я хочу, чтобы окно росло само, даже если это сделало бы окно слишком высоким, чтобы поместиться на экране. При автоматическом изменении размера ячеек в false в приведенной выше программе матрица увеличивается вверх от верхней части окна каждый раз, когда я добавляю кнопку. Если в моей реальной программе установлено значение false, после добавления элемента матрица вообще перестает расти, поэтому отображается только первый новый элемент. Для обеих программ приоритет сопротивления сжатию в обоих направлениях равен 750 (NSLayoutPriorityDefaultHigh), так что у меня ощущение, что это не так...   -  person andlabs    schedule 24.08.2015
comment
В своем методе действия попробуйте исключить вызов sizeToCells(). Если это ничего не меняет, попробуйте добавить вызов invalidateIntrinsicContentSize().   -  person Ken Thomases    schedule 24.08.2015
comment
Да, удаление sizeToCells() сделало это не только в программе выше, но и в моей реальной программе. Я предполагаю, что он несовместим с Auto Layout, даже если я воссоздаю свои ограничения после его вызова. Спасибо!   -  person andlabs    schedule 24.08.2015


Ответы (1)


Очевидно, вам нужно опустить вызов sizeToCells() после вызова renewRows(_:columns:). Я предполагаю, что он устанавливает размер кадра, который в основном бесполезен при использовании автоматической компоновки, но также очищает где-то «грязный» флаг, который сообщает матрице, что ей нужно сделать недействительным ее внутренний размер. Другими словами, матрица считает, что уже выполнила необходимые действия по изменению макета.

person Ken Thomases    schedule 23.08.2015