macos - Mountain Lion 中用两根手指拖动 IKImageView 和 NSScrollView

标签 macos cocoa osx-mountain-lion nsscrollview ikimageview

我有一个 Mac 应用程序,它已经在应用程序商店中存在一年左右了。它首次与目标 SDK 10.7 Lion 一起发布。更新到 Mountain Lion 后,它不再起作用。

应用程序在嵌入 NSScrollView 中的 IKImageView 中显示大图像。将其放入 ScrollView 的目的是让两根手指拖动工作,而不是用户必须单击才能拖动。使用 Nicholas Riley 的 ScrollViewWorkaround,我能够在用户放大后使用两根手指滚动来显示剪辑的内容。就像您在预览应用程序中看到的那样。

尼古拉斯·莱利的解决方案: IKImageView and scroll bars

现在在 Mountain Lion 中这不起作用。放大、捏合或缩放按钮后,图像被锁定在图像的左下部分。它不会滚动。

所以问题是,在 IKImageView 中显示大图像并用两根手指拖动缩放图像的适当方法是什么?

谢谢,
有状态

最佳答案

好吧,尼古拉斯莱利的解决方案是一个丑陋的黑客,因为它解决了错误的类;问题不在于 NSClipView (他对其进行了子类化,但按原样工作得很好),而是在于 IKImageView

IKImageView 的问题其实很简单(天知道苹果为什么没有解决这个问题?……7年了……):它的大小没有根据图像的大小进行调整它显示。现在,当您在 NSScrollView 中嵌入 IKImageView 时, ScrollView 显然只能相对于嵌入的 IKImageView 的大小调整其滚动条,而不是它包含的图像。由于 IKImageView 的大小始终保持不变,因此滚动条将无法按预期工作。

以下代码是 IKImageView 的子类并修复了此行为。唉,它无法解决 IKImageView 在 Mountain Lion 中一旦缩放就容易崩溃的事实......

///////////////////// HEADER FILE - FixedIKImageView.h

#import <Quartz/Quartz.h>

@interface FixedIKImageView : IKImageView
@end






///////////////////// IMPLEMENTATION FILE - FixedIKImageView.m

#import "FixedIKImageView.h"


@implementation FixedIKImageView

- (void)awakeFromNib
    {
        [self setTranslatesAutoresizingMaskIntoConstraints:NO]; // compatibility with Auto Layout; without this, there could be Auto Layout error messages when we are resized (delete this line if your app does not use Auto Layout)
    }


// FixedIKImageView must *only* be used embedded within an NSScrollView. This means that setFrame: should never be called explicitly from outside the scroll view. Instead, this method is overwritten here to provide the correct behavior within a scroll view. The new implementation ignores the frameRect parameter.
- (void)setFrame:(NSRect)frameRect
    {
        NSSize  imageSize = [self imageSize];
        CGFloat zoomFactor = [self zoomFactor];
        NSSize  clipViewSize = [[self superview] frame].size;

        // The content of our scroll view (which is ourselves) should stay at least as large as the scroll clip view, so we make ourselves as large as the clip view in case our (zoomed) image is smaller. However, if our image is larger than the clip view, we make ourselves as large as the image, to make the scrollbars appear and scale appropriately.
        CGFloat newWidth = (imageSize.width * zoomFactor < clipViewSize.width)?  clipViewSize.width : imageSize.width * zoomFactor;
        CGFloat newHeight = (imageSize.height * zoomFactor < clipViewSize.height)?  clipViewSize.height : imageSize.height * zoomFactor;

        [super setFrame:NSMakeRect(0, 0, newWidth - 2, newHeight - 2)]; // actually, the clip view is 1 pixel larger than the content view on each side, so we must take that into account
    }


//// We forward size affecting messages to our superclass, but add [self setFrame:NSZeroRect] to update the scroll bars. We also add [self setAutoresizes:NO]. Since IKImageView, instead of using [self setAutoresizes:NO], seems to set the autoresizes instance variable to NO directly, the scrollers would not be activated again without invoking [self setAutoresizes:NO] ourselves when these methods are invoked.

- (void)setZoomFactor:(CGFloat)zoomFactor
    {
        [super setZoomFactor:zoomFactor];
        [self setFrame:NSZeroRect];
        [self setAutoresizes:NO];
    }


- (void)zoomImageToRect:(NSRect)rect
    {
        [super zoomImageToRect:rect];
        [self setFrame:NSZeroRect];
        [self setAutoresizes:NO];
    }


- (void)zoomIn:(id)sender
    {
        [super zoomIn:self];
        [self setFrame:NSZeroRect];
        [self setAutoresizes:NO];
    }


- (void)zoomOut:(id)sender
    {
        [super zoomOut:self];
        [self setFrame:NSZeroRect];
        [self setAutoresizes:NO];
    }


- (void)zoomImageToActualSize:(id)sender
    {
        [super zoomImageToActualSize:sender];
        [self setFrame:NSZeroRect];
        [self setAutoresizes:NO];
    }


- (void)zoomImageToFit:(id)sender
    {
        [self setAutoresizes:YES];  // instead of invoking super's zoomImageToFit: method, which has problems of its own, we invoke setAutoresizes:YES, which does the same thing, but also makes sure the image stays zoomed to fit even if the scroll view is resized, which is the most intuitive behavior, anyway. Since there are no scroll bars in autoresize mode, we need not add [self setFrame:NSZeroRect].
    }


- (void)setAutoresizes:(BOOL)autoresizes    // As long as we autoresize, make sure that no scrollers flicker up occasionally during live update.
    {
        [self setHasHorizontalScroller:!autoresizes];
        [self setHasVerticalScroller:!autoresizes];
        [super setAutoresizes:autoresizes];
    }


@end

关于macos - Mountain Lion 中用两根手指拖动 IKImageView 和 NSScrollView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12231573/

相关文章:

Cocoa 应用程序 - 我可以将亚马逊商品搜索集成到我的 cocoa 应用程序中吗

macos - 如何为 OSX 注册自定义非文档 UTI/文件类型以识别它?

xcode - 如何在 Xcode 9 的 Project Navigator 中显示所有文件?

iOS/Cocoa - 数据模型 <-> Controller 的设计模式

objective-c - OS X 上的 SceneKit 有数千个对象

javascript - 在 javascript 中检测 Mountain Lion (OS X 10.8)?

python - 使用 savefig() 将图形导出为 pdf 会弄乱 matplotlib 中的轴背景

objective-c - AppDelegate (OSX) 中主 NSWindow 的引用导出

macos - (NSWindowButton) NSWindow.standardWindowButton 按钮未突出显示

java - 是否可以在应用程序中捆绑 kext?