8

我有一个占据整个窗口的 NSImageView。图像视图没有边框,它设置为显示在左下方。所以这意味着无论窗口如何调整大小,视图的原点都与实际图像的原点相匹配。

此外,图像比我可以合理地在屏幕上全尺寸显示的要大得多。所以我也将 imageview 设置为按比例缩小图像的大小。但是,我似乎无法在任何地方找到这个比例因子。

我的最终目标是将鼠标按下事件映射到实际的图像坐标。为此,我想我还需要一条信息……显示的 NSImage 实际上有多大。

如果我查看[imageView bounds],我会得到图像视图的边界矩形,它通常会比图像大。

4

4 回答 4

4

我认为这可以满足您的需求:

NSRect imageRect = [imageView.cell drawingRectForBounds: imageView.bounds];

它返回视图中图像原点的偏移量,以及它的大小。

对于您重新映射鼠标坐标的最终目标,您的自定义视图类上的类似内容应该可以工作......

- (void)mouseUp:(NSEvent *)event
{
    NSPoint eventLocation = [event locationInWindow];    
    NSPoint location = [self convertPoint: eventLocation fromView: nil];

    NSRect drawingRect = [self.cell drawingRectForBounds:self.bounds];

    location.x -= drawingRect.origin.x;
    location.y -= drawingRect.origin.y;

    NSSize frameSize = drawingRect.size;
    float frameAspect = frameSize.width/frameSize.height;

    NSSize imageSize = self.image.size;
    float imageAspect = imageSize.width/imageSize.height;

    float scaleFactor = 1.0f;

    if(imageAspect > frameAspect) {

        ///in this case image.width == frame.width
        scaleFactor = imageSize.width / frameSize.width;

        float imageHeightinFrame = imageSize.height / scaleFactor;

        float imageOffsetInFrame = (frameSize.height - imageHeightinFrame)/2;

        location.y -= imageOffsetInFrame;

    } else {
        ///in this case image.height == frame.height
        scaleFactor = imageSize.height / frameSize.height;

        float imageWidthinFrame = imageSize.width / scaleFactor;

        float imageOffsetInFrame = (frameSize.width - imageWidthinFrame)/2;

        location.x -= imageOffsetInFrame;
    }

    location.x *= scaleFactor;
    location.y *= scaleFactor;

    //do something with you newly calculated mouse location    
}
于 2012-08-06T21:17:23.300 回答
3

由于我还没有找到任何解决方案来获取 NSImageView 中的真实图像帧,因此我手动进行了图像计算,尊重其所有属性(缩放、对齐和边框)。这可能不是最有效的代码,并且可能与真实图像有 0.5-1 像素的微小偏差,但它非常接近原始图像(我知道这个问题很老,但解决方案可能对其他人有帮助):

@implementation NSImageView (ImageFrame)

// -------------------------------------------------------------------------
// -imageFrame
// -------------------------------------------------------------------------
- (NSRect)imageFrame
{
    // Find the content frame of the image without any borders first
    NSRect contentFrame = self.bounds;
    NSSize imageSize = self.image.size;
    NSImageFrameStyle imageFrameStyle = self.imageFrameStyle;

    if (imageFrameStyle == NSImageFrameButton ||
        imageFrameStyle == NSImageFrameGroove)
    {
        contentFrame = NSInsetRect(self.bounds, 2, 2);
    }

    else if (imageFrameStyle == NSImageFramePhoto)
    {
        contentFrame = NSMakeRect(contentFrame.origin.x + 1,
                                  contentFrame.origin.y + 2,
                                  contentFrame.size.width - 3,
                                  contentFrame.size.height - 3);
    }

    else if (imageFrameStyle == NSImageFrameGrayBezel)
    {
        contentFrame = NSInsetRect(self.bounds, 8, 8);
    }


    // Now find the right image size for the current imageScaling
    NSImageScaling imageScaling = self.imageScaling;
    NSSize drawingSize = imageSize;

    // Proportionally scaling
    if (imageScaling == NSImageScaleProportionallyDown ||
        imageScaling == NSImageScaleProportionallyUpOrDown)
    {
        NSSize targetScaleSize = contentFrame.size;
        if (imageScaling == NSImageScaleProportionallyDown)
        {
            if (targetScaleSize.width > imageSize.width) targetScaleSize.width = imageSize.width;
            if (targetScaleSize.height > imageSize.height) targetScaleSize.height = imageSize.height;
        }

        NSSize scaledSize = [self sizeByScalingProportionallyToSize:targetScaleSize fromSize:imageSize];
        drawingSize = NSMakeSize(scaledSize.width, scaledSize.height);
    }

    // Axes independent scaling
    else if (imageScaling == NSImageScaleAxesIndependently)
        drawingSize = contentFrame.size;


    // Now get the image position inside the content frame (center is default) from the current imageAlignment
    NSImageAlignment imageAlignment = self.imageAlignment;
    NSPoint drawingPosition = NSMakePoint(contentFrame.origin.x + contentFrame.size.width / 2.0 - drawingSize.width / 2.0,
                                          contentFrame.origin.y + contentFrame.size.height / 2.0 - drawingSize.height / 2.0);

    // NSImageAlignTop / NSImageAlignTopLeft / NSImageAlignTopRight
    if (imageAlignment == NSImageAlignTop ||
        imageAlignment == NSImageAlignTopLeft ||
        imageAlignment == NSImageAlignTopRight)
    {
        drawingPosition.y = contentFrame.origin.y+contentFrame.size.height - drawingSize.height;

        if (imageAlignment == NSImageAlignTopLeft)
            drawingPosition.x = contentFrame.origin.x;
        else if (imageAlignment == NSImageAlignTopRight)
            drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width;
    }

    // NSImageAlignBottom / NSImageAlignBottomLeft / NSImageAlignBottomRight
    else if (imageAlignment == NSImageAlignBottom ||
             imageAlignment == NSImageAlignBottomLeft ||
             imageAlignment == NSImageAlignBottomRight)
    {
        drawingPosition.y = contentFrame.origin.y;

        if (imageAlignment == NSImageAlignBottomLeft)
            drawingPosition.x = contentFrame.origin.x;
        else if (imageAlignment == NSImageAlignBottomRight)
            drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width;
    }

    // NSImageAlignLeft / NSImageAlignRight
    else if (imageAlignment == NSImageAlignLeft)
        drawingPosition.x = contentFrame.origin.x;

    // NSImageAlignRight
    else if (imageAlignment == NSImageAlignRight)
        drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width;


    return NSMakeRect(round(drawingPosition.x),
                      round(drawingPosition.y),
                      ceil(drawingSize.width),
                      ceil(drawingSize.height));
}

// -------------------------------------------------------------------------
// -sizeByScalingProportionallyToSize:fromSize:
// -------------------------------------------------------------------------
- (NSSize)sizeByScalingProportionallyToSize:(NSSize)newSize fromSize:(NSSize)oldSize
{
    CGFloat widthHeightDivision = oldSize.width / oldSize.height;
    CGFloat heightWidthDivision = oldSize.height / oldSize.width;

    NSSize scaledSize = NSZeroSize;
    if (oldSize.width > oldSize.height)
    {
        if ((widthHeightDivision * newSize.height) >= newSize.width)
        {
            scaledSize = NSMakeSize(newSize.width, heightWidthDivision * newSize.width);
        }  else {
            scaledSize = NSMakeSize(widthHeightDivision * newSize.height, newSize.height);
        }

    } else {

        if ((heightWidthDivision * newSize.width) >= newSize.height)
        {
            scaledSize = NSMakeSize(widthHeightDivision * newSize.height, newSize.height);
        } else {
            scaledSize = NSMakeSize(newSize.width, heightWidthDivision * newSize.width);
        }
    }

    return scaledSize;
}

@end
于 2015-01-19T20:09:40.183 回答
2

@Toby 解决方案的Swift 5实现

extension NSImageView {

    /** Returns an `NSRect` of the drawn image in the view. */
    func imageRect() -> NSRect {
        // Find the content frame of the image without any borders first
        var contentFrame = self.bounds
        guard let imageSize = image?.size else { return .zero }
        let imageFrameStyle = self.imageFrameStyle

        if imageFrameStyle == .button || imageFrameStyle == .groove {
            contentFrame = NSInsetRect(self.bounds, 2, 2)
        } else if imageFrameStyle == .photo {
            contentFrame = NSRect(x: contentFrame.origin.x + 1, y: contentFrame.origin.x + 2, width: contentFrame.size.width - 3, height: contentFrame.size.height - 3)
        } else if imageFrameStyle == .grayBezel {
            contentFrame = NSInsetRect(self.bounds, 8, 8)
        }


        // Now find the right image size for the current imageScaling
        let imageScaling = self.imageScaling
        var drawingSize = imageSize

        // Proportionally scaling
        if imageScaling == .scaleProportionallyDown || imageScaling == .scaleProportionallyUpOrDown {
            var targetScaleSize = contentFrame.size
            if imageScaling == .scaleProportionallyDown {
                if targetScaleSize.width > imageSize.width { targetScaleSize.width = imageSize.width }
                if targetScaleSize.height > imageSize.height { targetScaleSize.height = imageSize.height }
            }

            let scaledSize = self.sizeByScalingProportianlly(toSize: targetScaleSize, fromSize: imageSize)
            drawingSize = NSSize(width: scaledSize.width, height: scaledSize.height)
        }

        // Axes independent scaling
        else if imageScaling == .scaleAxesIndependently {
            drawingSize = contentFrame.size
        }


        // Now get the image position inside the content frame (center is default) from the current imageAlignment
        let imageAlignment = self.imageAlignment
        var drawingPosition = NSPoint(x: contentFrame.origin.x + contentFrame.size.width / 2 - drawingSize.width / 2,
                                      y: contentFrame.origin.y + contentFrame.size.height / 2 - drawingSize.height / 2)

        // Top Alignments
        if imageAlignment == .alignTop || imageAlignment == .alignTopLeft || imageAlignment == .alignTopRight {
            drawingPosition.y = contentFrame.origin.y + contentFrame.size.height - drawingSize.height

            if imageAlignment == .alignTopLeft {
                drawingPosition.x = contentFrame.origin.x
            } else if imageAlignment == .alignTopRight {
                drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width
            }
        }

        // Bottom Alignments
        else if imageAlignment == .alignBottom || imageAlignment == .alignBottomLeft || imageAlignment == .alignBottomRight {
            drawingPosition.y = contentFrame.origin.y

            if imageAlignment == .alignBottomLeft {
                drawingPosition.x = contentFrame.origin.x
            } else if imageAlignment == .alignBottomRight {
                drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width
            }
        }

        // Left Alignment
        else if imageAlignment == .alignLeft {
            drawingPosition.x = contentFrame.origin.x
        }

        // Right Alginment
        else if imageAlignment == .alignRight {
            drawingPosition.x = contentFrame.origin.x + contentFrame.size.width - drawingSize.width
        }

        return NSRect(x: round(drawingPosition.x), y: round(drawingPosition.y), width: ceil(drawingSize.width), height: ceil(drawingSize.height))
    }


    func sizeByScalingProportianlly(toSize newSize: NSSize, fromSize oldSize: NSSize) -> NSSize {
        let widthHeightDivision = oldSize.width / oldSize.height
        let heightWidthDivision = oldSize.height / oldSize.width

        var scaledSize = NSSize.zero

        if oldSize.width > oldSize.height {
            if (widthHeightDivision * newSize.height) >= newSize.width {
                scaledSize = NSSize(width: newSize.width, height: heightWidthDivision * newSize.width)
            } else {
                scaledSize = NSSize(width: widthHeightDivision * newSize.height, height: newSize.height)
            }
        } else {
            if (heightWidthDivision * newSize.width) >= newSize.height {
                scaledSize = NSSize(width: widthHeightDivision * newSize.height, height: newSize.height)
            } else {
                scaledSize = NSSize(width: newSize.width, height: heightWidthDivision * newSize.width)
            }
        }

        return scaledSize
    }
}
于 2020-03-25T09:19:19.607 回答
0

正如我在上面的评论中指出的那样,这是我采取的方法:

// the view that mouseUp: is part of doesnt draw anything. I'm layering it
// in the window hierarchy to intercept mouse events. I suppose I could have
// subclassed NSImageView instead, but I went this route.  isDragging is
// an ivar...its cleared in mouseDown: and set in mouseDragged:
// this view has no idea what the original unscaled image size is, so
// rescaling is done by caller
- (void)mouseUp:(NSEvent *)theEvent
{

    if (!isDragging)
    {
        NSPoint rawPoint = [theEvent locationInWindow];
        NSImageView *view = self.subviews.lastObject;

        point = [self convertPoint:rawPoint fromView:view];
        point.x /= view.bounds.size.width;
        point.y /= view.bounds.size.height;

        [owner mouseClick:point];

    }
}

在我的 NSWindowController 中,这是我的鼠标视图窗口委托,我有:

static int resizeMode=-1;

- (void)windowDidEndLiveResize:(NSNotification *)notification
{
    if ([notification object]==frameWindow)
        self.resizeFrameSelection=0;
    resizeMode = -1;
}

- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)frameSize
{

    if (sender==frameWindow)
    {
        float imageAspectRatio = (float)movie.movieSize.width / (float)movie.movieSize.height;
        float newH = frameSize.height;
        float newW = frameSize.width;
        float currH = sender.frame.size.height;
        float currW = sender.frame.size.width;
        float deltaH = abs(newH-currH);
        float deltaW = abs(newW-currW);

        // lock onto one dimension to key off of, per drag.
        if  ( resizeMode==1 || (resizeMode==-1 && deltaW<deltaH ))
        {
            // adjust width to match aspect ratio
            frameSize.width = frameSize.height * imageAspectRatio;
            resizeMode=1;
        }
        else
        {
            // adjust height to match aspect ratio
            frameSize.height = frameSize.width / imageAspectRatio;
            resizeMode=2;
        }
    }

    return frameSize;
}
于 2012-08-07T03:23:58.693 回答