Подтвердить что ты не робот

Получить NSTextField для роста с текстом в автоматическом макете?

Я пытаюсь заставить свой NSTextField увеличить его высоту (как в iChat или Adium), когда пользователь набирает достаточно текста для переполнения ширины элемента управления (как задано на этом посту)

Я включил принятый ответ, но я не могу заставить его работать. Я загрузил свою попытку http://scottyob.com/pub/autoGrowingExample.zip

В идеале, когда текст растет, содержащее окно должно расти вместе с ним, но я пытаюсь выполнить детские шаги здесь.

4b9b3361

Ответ 1

Решил! (Вдохновленный https://github.com/jerrykrinock/CategoriesObjC/blob/master/NS(Attributed)String%2BGeometrics/NS(Attributed)String%2BGeometrics.m)

Чтение Apple Documentation обычно полезно. Apple разработала все эти материалы для текстового макета, чтобы быть достаточно мощными, чтобы обрабатывать всевозможные сложные граничные случаи, которые иногда чрезвычайно полезны, а иногда и нет.

Во-первых, я устанавливаю текстовое поле для обертывания строк в слове break, поэтому мы фактически получаем несколько строк. (В вашем примере кода даже был оператор if, поэтому он ничего не делал при отключении упаковки).

Трюк к этому заключался в том, чтобы отметить, что при редактировании текста его напечатанный "полевым редактором" - объект с тяжелым весом NSTextView, принадлежащий NSWindow, который повторно используется любым NSTextField в настоящее время "первый ответчик" (выбранный). NSTextView имеет один NSTextContainer (прямоугольник, куда идет текст), который имеет NSLayoutManager для компоновки текста. Мы можем спросить менеджера макета, сколько места он хочет использовать, чтобы получить новую высоту нашего текстового поля.

Другой трюк состоял в том, чтобы переопределить метод NSText delegate - (void)textDidChange:(NSNotification *)notification, чтобы аннулировать размер внутреннего содержимого при изменении текста (так что он просто не ждет обновления, когда вы делаете изменения, нажимая return).

Причина, по которой я не использовал cellSizeForBounds, как вы изначально предполагали, я не смог решить вашу проблему - даже если недействительный внутренний размер содержимого ячейки, cellSizeForBounds: продолжал возвращать старый размер.

Найдите пример проекта GitHub.

@interface TSTTextGrowth()
{
    BOOL _hasLastIntrinsicSize;
    BOOL _isEditing;
    NSSize _lastIntrinsicSize;
}

@end

@implementation TSTTextGrowth

- (void)textDidBeginEditing:(NSNotification *)notification
{
    [super textDidBeginEditing:notification];
    _isEditing = YES;
}

- (void)textDidEndEditing:(NSNotification *)notification
{
    [super textDidEndEditing:notification];
    _isEditing = NO;
}

- (void)textDidChange:(NSNotification *)notification
{
    [super textDidChange:notification];
    [self invalidateIntrinsicContentSize];
}

-(NSSize)intrinsicContentSize
{
    NSSize intrinsicSize = _lastIntrinsicSize;

    // Only update the size if we’re editing the text, or if we’ve not set it yet
    // If we try and update it while another text field is selected, it may shrink back down to only the size of one line (for some reason?)
    if(_isEditing || !_hasLastIntrinsicSize)
    {
        intrinsicSize = [super intrinsicContentSize];

        // If we’re being edited, get the shared NSTextView field editor, so we can get more info
        NSText *fieldEditor = [self.window fieldEditor:NO forObject:self];
        if([fieldEditor isKindOfClass:[NSTextView class]])
        {
            NSTextView *textView = (NSTextView *)fieldEditor;
            NSRect usedRect = [textView.textContainer.layoutManager usedRectForTextContainer:textView.textContainer];

            usedRect.size.height += 5.0; // magic number! (the field editor TextView is offset within the NSTextField. It’s easy to get the space above (it’s origin), but it’s difficult to get the default spacing for the bottom, as we may be changing the height

            intrinsicSize.height = usedRect.size.height;
        }

        _lastIntrinsicSize = intrinsicSize;
        _hasLastIntrinsicSize = YES;
    }

    return intrinsicSize;
}

@end

Как последнее примечание, Ive никогда не использовало автомат самостоятельно - демонстрации выглядят потрясающе, но всякий раз, когда я на самом деле пытаюсь это сделать, я не могу заставить его работать совершенно правильно, и это усложняет ситуацию. Однако в этом случае я думаю, что на самом деле это спасло кучу работы - без нее -intrinsicContentSize не существовало, и вам, возможно, придется самому установить кадр, вычислять новое происхождение, а также новый размер (не слишком сложно, но только больше кода).

Ответ 2

Решение DouglasHeriot работает только для текстовых полей с фиксированной шириной. В моем приложении у меня есть текстовые поля, которые я хочу увеличить как по горизонтали, так и по вертикали. Поэтому я модифицировал решение следующим образом:

AutosizingTextField.h

@interface AutosizingTextField : NSTextField {
    BOOL isEditing;
}
@end

AutosizingTextField.m

@implementation AutosizingTextField

- (void)textDidBeginEditing:(NSNotification *)notification
{
    [super textDidBeginEditing:notification];
    isEditing = YES;
}

- (void)textDidEndEditing:(NSNotification *)notification
{
    [super textDidEndEditing:notification];
    isEditing = NO;
}

- (void)textDidChange:(NSNotification *)notification
{
    [super textDidChange:notification];
    [self invalidateIntrinsicContentSize];
}

-(NSSize)intrinsicContentSize
{
    if(isEditing)
    {
        NSText *fieldEditor = [self.window fieldEditor:NO forObject:self];
        if(fieldEditor)
        {
            NSTextFieldCell *cellCopy = [self.cell copy];
            cellCopy.stringValue = fieldEditor.string;
            return [cellCopy cellSize];
        }
    }
    return [self.cell cellSize];
}
@end

Остается незначительная проблема: при наборе пробелов текст немного перескакивает влево. Однако это не проблема в моем приложении, потому что текстовые поля не должны содержать пробелов в большинстве случаев.

Ответ 3

Решение DouglasHeriot отлично работает для меня.

Вот такой же код на Swift 4

class GrowingTextField: NSTextField {

    var editing = false
    var lastIntrinsicSize = NSSize.zero
    var hasLastIntrinsicSize = false

    override func textDidBeginEditing(_ notification: Notification) {
        super.textDidBeginEditing(notification)
        editing = true
    }

    override func textDidEndEditing(_ notification: Notification) {
        super.textDidEndEditing(notification)
        editing = false
    }

    override func textDidChange(_ notification: Notification) {
        super.textDidChange(notification)
        invalidateIntrinsicContentSize()
    }

    override var intrinsicContentSize: NSSize {
        get {
            var intrinsicSize = lastIntrinsicSize

            if editing || !hasLastIntrinsicSize {

                intrinsicSize = super.intrinsicContentSize

                // If we’re being edited, get the shared NSTextView field editor, so we can get more info
                if let textView = self.window?.fieldEditor(false, for: self) as? NSTextView, let textContainer = textView.textContainer, var usedRect = textView.textContainer?.layoutManager?.usedRect(for: textContainer) {
                    usedRect.size.height += 5.0 // magic number! (the field editor TextView is offset within the NSTextField. It’s easy to get the space above (it’s origin), but it’s difficult to get the default spacing for the bottom, as we may be changing the height
                    intrinsicSize.height = usedRect.size.height
                }

                lastIntrinsicSize = intrinsicSize
                hasLastIntrinsicSize = true
            }

            return intrinsicSize
        }
    }
}

Ответ 4

И если вы хотите ограничить размер TextField (например.):

if (intrinsicSize.height > 100) 
{
    intrinsicSize = _lastIntrinsicSize;
} 
else
{
    _lastIntrinsicSize = intrinsicSize;
    _hasLastIntrinsicSize = YES;
}

(Diff)

Одна вещь, с которой я сталкиваюсь, - это получение NSTextField, встроенного в NSScrollView, и работа с ней должным образом (особенно в NSStackView). Посмотрим, не будет ли проще использовать NSTextView.