12

コンテキスト:

NSTextViewリッチ テキスト入力にを使用する通常のドキュメント ベースの Cocoa Mac OS X アプリケーションがあります。ユーザーは、 内のテキストのフォント ファミリ、ポイント サイズ、および色を編集できますNSTextView

ベース SDK: 10.7
展開ターゲット: 10.6


質問:

NSTextViewユーザーがテキストを編集している間に、プログラムで UI 全体 (を含む) のズームを実装したいと思います。のフレームのスケーリングはNSTextView問題ありません。しかし、テキスト全体のさまざまなサブセクションに複数の異なるポイント サイズが含まれている可能性があるビュー内の編集可能なテキストをスケーリングする方法がわかりません。

に表示されるリッチ テキストに均一な倍率を適用するにはどうすればよいNSTextViewですか?

これは、ユーザーのフォント ファミリ、色、特にポイント サイズ(テキストのランのさまざまなポイントで異なる場合があります) が保持されるように、「リッチ テキスト」とうまく連携する必要がありますが、均一に/相対的にスケーリングされます。

Base SDK と Deployment ターゲットを指定すると、これは可能ですか? 新しい Base SDK または Deployment ターゲットで可能ですか?

4

4 に答える 4

12

ビューをスケーリングすること (実際には文字列の属性を変更することではない) が目的の場合は、適切なスクロール バーの動作のために、scaleUnitSquareToSize: メソッドを ScalingScrollView (TextEdit サンプル コードで使用可能) と共に使用することをお勧めします。

ScalingScrollView のコア部分は次のとおりです。

- (void)setScaleFactor:(CGFloat)newScaleFactor adjustPopup:(BOOL)flag
{
CGFloat oldScaleFactor = scaleFactor;
    if (scaleFactor != newScaleFactor)
    {
        NSSize curDocFrameSize, newDocBoundsSize;
        NSView *clipView = [[self documentView] superview];

        scaleFactor = newScaleFactor;

        // Get the frame.  The frame must stay the same.
        curDocFrameSize = [clipView frame].size;

        // The new bounds will be frame divided by scale factor
        newDocBoundsSize.width = curDocFrameSize.width / scaleFactor;
        newDocBoundsSize.height = curDocFrameSize.height / scaleFactor;
    }
    scaleFactor = newScaleFactor;
    [scale_delegate scaleChanged:oldScaleFactor newScale:newScaleFactor]; 
}

オブジェクトscale_delegateを調整できるデリゲートです。NSTextView

- (void) scaleChanged:(CGFloat)oldScale newScale:(CGFloat)newScale
{
    NSInteger     percent  = lroundf(newScale * 100);

    CGFloat scaler = newScale / oldScale;   
    [textView scaleUnitSquareToSize:NSMakeSize(scaler, scaler)];

    NSLayoutManager* lm = [textView layoutManager];
    NSTextContainer* tc = [textView textContainer];
    [lm ensureLayoutForTextContainer:tc];
}

メソッドはscaleUnitSquareToSize:現在の状態に対して相対的にスケーリングするため、スケーリング係数を追跡してから、絶対スケーリング リクエスト (200%) を相対的スケーリング リクエストに変換します。

于 2013-01-01T20:23:10.137 に答える
1

OPはこちら。

ちょっと機能し、実装がそれほど難しくない解決策を1つ見つけました。ただし、これが最善/理想的な解決策であるかどうかはわかりません。私はまだ他の解決策を見つけることに興味があります。しかし、ここに1つの方法があります:

表示前にソース テキストのフォント ポイント サイズ行の高さの複数のプロパティを手動でスケーリングし、NSAttributedStringソースとして保存する前に表示されたテキストのスケーリングを解除します。

このソリューションの問題点は、システムのフォント パネルが、編集中に、選択したテキストの実際のスケーリングされた表示ポイント サイズ (「実際の」ソース ポイント サイズではなく) を表示することです。それは望ましくありません。


これが私の実装です:

- (void)scaleAttributedString:(NSMutableAttributedString *)str by:(CGFloat)scale {
    if (1.0 == scale) return;

    NSRange r = NSMakeRange(0, [str length]);
    [str enumerateAttribute:NSFontAttributeName inRange:r options:0 usingBlock:^(NSFont *oldFont, NSRange range, BOOL *stop) {
        NSFont *newFont = [NSFont fontWithName:[oldFont familyName] size:[oldFont pointSize] * scale];

        NSParagraphStyle *oldParaStyle = [str attribute:NSParagraphStyleAttributeName atIndex:range.location effectiveRange:NULL];
        NSMutableParagraphStyle *newParaStyle = [[oldParaStyle mutableCopy] autorelease];

        CGFloat oldLineHeight = [oldParaStyle lineHeightMultiple];
        CGFloat newLineHeight = scale * oldLineHeight;
        [newParaStyle setLineHeightMultiple:newLineHeight];

        id newAttrs = @{
            NSParagraphStyleAttributeName: newParaStyle,
            NSFontAttributeName: newFont,
        };
        [str addAttributes:newAttrs range:range];
    }];    
}

これには、表示前にソース テキストをスケーリングする必要があります。

// scale text
CGFloat scale = getCurrentScaleFactor();
[self scaleAttributedString:str by:scale];

ソースとして保存する前に、表示されたテキストを逆スケーリングします。

// un-scale text
CGFloat scale = 1.0 / getCurrentScaleFactor();
[self scaleAttributedString:str by:scale];
于 2013-01-01T19:45:20.330 に答える
0

NSScrollView 倍率の狂気と NSLayoutManagers でいっぱいの暗い森の中をさまようことから私を救ってくれたので、彼の答えに対して Mark Munz に感謝したいと思います。

まだ探している人にとっては、これが私のアプローチです。このコードは NSDocument 内にあります。すべてのテキストは、固定幅で中央揃えのコンテナーに挿入されており、ここでズームすると、ワード ラップなどはそのまま維持されます。複雑なレイアウト管理に頼ることなく、素敵な「ページビュー」のような外観を作成します。

この例を機能させるには、クラスに定数を設定する必要がありCGFloat _documentSizeます。NSTextView textView

- (void) initZoom {
    // Call this when the view has loaded and is ready
    // I am storing a separate _scaleFactor and _magnification for my own purposes, mainly to have the initial scale to be higher than 1.0
    _scaleFactor = 1.0;
    _magnification = 1.1;
    [self setScaleFactor:_magnification adjustPopup:false];

    [self updateLayout];
    // NOTE: You might need to call updateLayout after the content is set and we know the window size etc.
}

- (void) zoom: (bool) zoomIn {
    if (!_scaleFactor) _scaleFactor = _magnification;

    // Arbitrary maximum levels of zoom
    if (zoomIn) {
        if (_magnification < 1.6) _magnification += 0.1;
    } else {
        if (_magnification > 0.8) _magnification -= 0.1;
    }

    [self setScaleFactor:_magnification adjustPopup:false];
    [self updateLayout];
}

- (void)setScaleFactor:(CGFloat)newScaleFactor adjustPopup:(BOOL)flag
{
    CGFloat oldScaleFactor = _scaleFactor;
    if (_scaleFactor != newScaleFactor)
    {
        NSSize curDocFrameSize, newDocBoundsSize;
        NSView *clipView = [[self textView] superview];

        _scaleFactor = newScaleFactor;

        // Get the frame.  The frame must stay the same.
        curDocFrameSize = [clipView frame].size;

        // The new bounds will be frame divided by scale factor
        //newDocBoundsSize.width = curDocFrameSize.width / _scaleFactor;
        newDocBoundsSize.width = curDocFrameSize.width;
        newDocBoundsSize.height = curDocFrameSize.height / _scaleFactor;

        NSRect newFrame = NSMakeRect(0, 0, newDocBoundsSize.width, newDocBoundsSize.height);
        clipView.frame = newFrame;
    }
    _scaleFactor = newScaleFactor;
    [self scaleChanged:oldScaleFactor newScale:newScaleFactor];
}
- (void) scaleChanged:(CGFloat)oldScale newScale:(CGFloat)newScale
{
    CGFloat scaler = newScale / oldScale;
    [self.textView scaleUnitSquareToSize:NSMakeSize(scaler, scaler)];

    NSLayoutManager* lm = [self.textView layoutManager];
    NSTextContainer* tc = [self.textView textContainer];
    [lm ensureLayoutForTextContainer:tc];
}

- (void) updateLayout {
    CGFloat width = (self.textView.frame.size.width / 2 - _documentWidth * _magnification / 2) / _magnification;    self.textView.textContainerInset = NSMakeSize(width, TEXT_INSET_TOP);
    self.textView.textContainer.size = NSMakeSize(_documentWidth, self.textView.textContainer.size.height);
}
于 2019-09-06T06:48:21.107 に答える