12

ユーザーがコントロールの幅をオーバーフローするのに十分なテキストを入力したら(この投稿で尋ねられるように)、NSTextFieldの高さを(iChatやAdiumのように)大きくしようとしています。

私は受け入れられた答えをほのめかしましたが、それを機能させることができないようです。http://scottyob.com/pub/autoGrowingExample.zipに自分の試みをアップロードしました

理想的には、テキストが大きくなると、それとともに含まれるウィンドウも大きくなるはずですが、私はここで赤ちゃんのステップを試しています。

4

5 に答える 5

18

解決しました!( https://github.com/jerrykrinock/CategoriesObjC/blob/master/NS(Attributed)String%2BGeometrics/NS(Attributed)String%2BGeometrics.mに触発されました)

通常、 Appleのドキュメントを読むと役に立ちます。Appleは、これらすべてのテキストレイアウトを、あらゆる種類の複雑なエッジケースを処理するのに十分強力になるように設計しました。これは、非常に役立つ場合とそうでない場合があります。

まず、単語の区切りで行を折り返すようにテキストフィールドを設定したので、実際には複数の行が表示されます。(サンプルコードにはifステートメントも含まれているため、ラッピングがオフになっている場合は何も実行されませんでした)。

NSTextViewこれの秘訣は、テキストが編集されているときに、「フィールドエディタ」によって印刷されることに注意することでした。これは、が所有する重量のあるオブジェクトであり、現在「ファーストレスポンダー」(選択済み)であるNSWindowものによって再利用されます。NSTextFieldには、テキストをレイアウトするためNSTextViewの単一のNSTextContainer(テキストが入る長方形)があります。NSLayoutManagerテキストフィールドの新しい高さを取得するために、レイアウトマネージャーにどのくらいのスペースを使いたいかを尋ねることができます。

もう1つのトリックは、NSTextデリゲートメソッドをオーバーライドし- (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

最後に、私は実際に自動レイアウトを自分で使用したことはありません。デモはすばらしいように見えますが、実際に自分で試してみると、正しく機能させることができず、事態がさら​​に複雑になります。ただし、この場合、実際には多くの作業を節約できたと思います。これがないと-intrinsicContentSize、フレームは存在しません。フレームを自分で設定して、新しい原点と新しいサイズを計算する必要があります(あまりにも多くありません)。難しいですが、コードが増えます)。

于 2013-01-01T14:49:47.253 に答える
7

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

小さな問題が残っています。スペースを入力すると、テキストが少し左にジャンプします。ただし、ほとんどの場合、テキストフィールドにスペースを含めるべきではないため、これは私のアプリでは問題になりません。

于 2014-07-01T06:38:51.687 に答える
5

DouglasHeriotのソリューションは私にとって素晴らしい働きをします。

これがSwift4の同じコードです

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
        }
    }
}
于 2018-02-08T01:01:02.903 に答える
2

このソリューションは、テキストフィールドの文字列値を設定するとき、およびAutoLayoutによってサイズ変更されるときにも機能します。属性付きのテキストプロパティを使用して、必要なときに固有のコンテンツサイズを計算するだけです。

class AutoGrowingTextField: NSTextField {
    var maximumHeight: CGFloat = 100

    override var intrinsicContentSize: NSSize {
        let height = attributedStringValue.boundingRect(
            with: NSSize(width: bounds.width - 8, height: maximumHeight),
            options: [NSString.DrawingOptions.usesLineFragmentOrigin]
            ).height + 5

        return NSSize(width: NSView.noIntrinsicMetric, height: height)
    }

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

    override func layout() {
        super.layout()
        invalidateIntrinsicContentSize()
    }
}
于 2018-11-02T21:23:01.183 に答える
0

また、TextFieldのサイズを制限する場合(例):

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

差分

私が問題を抱えていることの1つは、NSTextFieldをNSScrollViewに埋め込み、正しく機能させることです(特に、NSStackView内で)。代わりにNSTextViewを使用する方が簡単ではないかどうかを確認します。

于 2015-01-04T15:13:15.323 に答える