问题 如何在NSTextView中统一缩放富文本?


上下文

我有一个普通的基于文档的Cocoa Mac OS X应用程序,它使用了一个 NSTextView 用于富文本输入。用户可以编辑文本的字体系列,点大小和颜色 NSTextView

基础SDK:10.7
部署目标:10.6


题:

我想以编程方式实现整个UI的缩放(包括 NSTextView)当用户正在编辑文本时。缩放框架 NSTextView 没问题。但我不知道如何缩放视图中的可编辑文本,该文本可能在整个文本运行的不同子部分中包含多个不同的点大小。

如何将统一比例因子应用于显示的富文本 NSTextView

这应该与“富文本”很好地配合,这样用户的字体系列,颜色 尤其是点大小 (在文本运行的不同点处可能不同)被保留,但是均匀/相对地缩放。

这可能是我的Base SDK和部署目标吗?是否可以使用较新的Base SDK或部署目标?


7277
2018-01-01 18:43


起源

是否有可能需要10.8及以上并使用10.8的内置文本缩放功能?如果您需要10.8并且文本不必包裹到缩放区域,这可能是一种前进的方式。请参阅TextEdit的视图>缩放...菜单项。 - Joshua Nozzi


答案:


如果目的是缩放视图(而不是实际更改字符串中的属性),我建议使用scaleUnitSquareToSize:method:以及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%)转换为相对比例请求。


10
2018-01-01 20:23



谢谢,这里有很多好消息!完成后我会深入挖掘并更新我的回复。 - Todd Ditchendorf
更新:这正是我需要的信息。我能够使用 -scaleUnitSquareToSize:方法实现我想要的。谢谢马克! - Todd Ditchendorf


适用于iOS和Mac OS

@implementation NSAttributedString (Scale)

- (NSAttributedString *)attributedStringWithScale:(double)scale
{
    if(scale == 1.0)
    {
        return self;
    }

    NSMutableAttributedString *copy = [self mutableCopy];
    [copy beginEditing];

    NSRange fullRange = NSMakeRange(0, copy.length);

    [self enumerateAttribute:NSFontAttributeName inRange:fullRange options:0 usingBlock:^(UIFont *oldFont, NSRange range, BOOL *stop) {
        double currentFontSize = oldFont.pointSize;
        double newFontSize = currentFontSize * scale;

        // don't trust -[UIFont fontWithSize:]
        UIFont *scaledFont = [UIFont fontWithName:oldFont.fontName size:newFontSize];

        [copy removeAttribute:NSFontAttributeName range:range];
        [copy addAttribute:NSFontAttributeName value:scaledFont range:range];
    }];

    [self enumerateAttribute:NSParagraphStyleAttributeName inRange:fullRange options:0 usingBlock:^(NSParagraphStyle *oldParagraphStyle, NSRange range, BOOL *stop) {

        NSMutableParagraphStyle *newParagraphStyle = [oldParagraphStyle mutableCopy];
        newParagraphStyle.lineSpacing *= scale;
        newParagraphStyle.paragraphSpacing *= scale;
        newParagraphStyle.firstLineHeadIndent *= scale;
        newParagraphStyle.headIndent *= scale;
        newParagraphStyle.tailIndent *= scale;
        newParagraphStyle.minimumLineHeight *= scale;
        newParagraphStyle.maximumLineHeight *= scale;
        newParagraphStyle.paragraphSpacing *= scale;
        newParagraphStyle.paragraphSpacingBefore *= scale;

        [copy removeAttribute:NSParagraphStyleAttributeName range:range];
        [copy addAttribute:NSParagraphStyleAttributeName value:newParagraphStyle range:range];
    }];

    [copy endEditing];
    return copy;
}

@end

5
2017-11-07 20:06





OP在这里。

我找到了一种有效的解决方案,并且实施起来并不十分困难。我不确定这是最好/理想的解决方案。我仍然有兴趣寻找其他解决方案。但这是一种方式:

手动缩放 字体磅值 和 线高度倍数 的属性 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];

1
2018-01-01 19:45