移动 NSView 直到它碰到边框

发布于 2024-11-03 03:39:45 字数 596 浏览 1 评论 0原文

在基于 Cocoa 的应用程序中,我有一个用于绘图的画布,继承自 NSView,以及一个矩形,也继承自 NSView。在画布内部拖动矩形是没有问题的:

-(void)mouseDragged:(NSEvent *)theEvent {
  NSPoint myOrigin = self.frame.origin;

  [self setFrameOrigin:NSMakePoint(myOrigin.x + [theEvent deltaX], 
                                   myOrigin.y - [theEvent deltaY])];
}

就像魅力一样。我现在遇到的问题:如何防止矩形移出画布?

因此,首先我想仅针对左边框修复此问题,然后调整其他边缘。我的第一个想法是:“检查矩形的x原点是否为负”。但是:一旦为负数,矩形就不能再移动(自然地)。我通过将 else 分支中的矩形移动到零 x 偏移来解决这个问题。这可行,但它......丑陋。

所以我对这个有点困惑,有什么提示吗?毫无疑问,解决方案非常接近且简单。就这么简单,我无法弄清楚(就像往常一样,有简单的解决方案;)。

问候

Mac

In a Cocoa-based App i'm having a canvas for drawing, inherited from NSView, as well as a rectangle, also inherited from NSView. Dragging the rectangle around inside of the canvas is no problem:

-(void)mouseDragged:(NSEvent *)theEvent {
  NSPoint myOrigin = self.frame.origin;

  [self setFrameOrigin:NSMakePoint(myOrigin.x + [theEvent deltaX], 
                                   myOrigin.y - [theEvent deltaY])];
}

Works like a charm. The issue i'm having now: How can i prevent the rectangle from being moved outside the canvas?

So, first of all i would like to fix this just for the left border, adapting the other edges afterwards. My first idea is: "check whether the x-origin of the rectangle is negative". But: once it is negative the rectangle can't be moved anymore around (naturally). I solved this with moving the rectangle to zero x-offset in the else-branch. This works but it's ... ugly.

So i'm little puzzled with this one, any hints? Definitely the solution is really near and easy. That easy, that i cannot figure it out (as always with easy solutions ;).

Regards

Macs

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

怎会甘心 2024-11-10 03:39:45

我建议不要使用 deltaX 和 deltaY ;尝试使用超级视图中的事件位置。您需要对子视图的引用。

// In the superview
- (void)mouseDragged:(NSEvent *)event {
    NSPoint mousePoint = [self convertPoint:[event locationInWindow] 
                             fromView:nil];

    // Could also add the width of the moving rectangle to this check 
    // to keep any part of it from going outside the superview
    mousePoint.x = MAX(0, MIN(mousePoint.x, self.bounds.size.width));
    mousePoint.y = MAX(0, MIN(mousePoint.y, self.bounds.size.height));

    // position is a custom ivar that indicates the center of the object;
    // you could also use frame.origin, but it looks nicer if objects are 
    // dragged from their centers
    myMovingRectangle.position = mousePoint;
    [self setNeedsDisplay:YES];
}

您将在 mouseUp: 中执行基本相同的边界检查。

更新:您还应该查看视图编程指南,它将引导您创建可拖动视图:创建自定义视图


示例代码应该会有所帮助,但与您原来的问题并不严格相关:

In DotView.m:

- (void)drawRect:(NSRect)dirtyRect {
    // Ignoring dirtyRect for simplicity
    [[NSColor colorWithDeviceRed:0.85 green:0.8 blue:0.8 alpha:1] set];
    NSRectFill([self bounds]);
    // Dot is the custom shape class that can draw itself; see below
    // dots is an NSMutableArray containing the shapes
    for (Dot *dot in dots) {
        [dot draw];
    }
}

- (void)mouseDown:(NSEvent *)event {
    NSPoint mousePoint = [self convertPoint:[event locationInWindow] 
                             fromView:nil];

    currMovingDot = [self clickedDotForPoint:mousePoint];
    // Move the dot to the point to indicate that the user has
    // successfully "grabbed" it    
    if( currMovingDot ) currMovingDot.position = mousePoint;
    [self setNeedsDisplay:YES];
}

// -mouseDragged: already defined earlier in post

- (void)mouseUp:(NSEvent *)event {
    if( !currMovingDot ) return;

    NSPoint mousePoint = [self convertPoint:[event locationInWindow] 
                             fromView:nil];
    spot.x = MAX(0, MIN(mousePoint.x, self.bounds.size.width));
    spot.y = MAX(0, MIN(mousePoint.y, self.bounds.size.height));

    currMovingDot.position = mousePoint;
    currMovingDot = nil;
    [self setNeedsDisplay:YES];
}

- (Dot *)clickedDotForPoint:(NSPoint)point {
    // DOT_NUCLEUS_RADIUS is the size of the 
    // dot's internal "handle"
    for( Dot *dot in dots ){
        if( (abs(dot.position.x - point.x) <= DOT_NUCLEUS_RADIUS) &&
            (abs(dot.position.y - point.y) <= DOT_NUCLEUS_RADIUS)) {
            return dot;
        }
    }
    return nil;
}

Dot.h

#define DOT_NUCLEUS_RADIUS (5)

@interface Dot : NSObject {
    NSPoint position;
}

@property (assign) NSPoint position;

- (void)draw;

@end

Dot.m

#import "Dot.h"

@implementation Dot

@synthesize position;

- (void)draw {
    //!!!: Demo only: assume that focus is locked on a view.
    NSColor *clr = [NSColor colorWithDeviceRed:0.3 
                                         green:0.2 
                                          blue:0.8 
                                         alpha:1];
    // Draw a nice border
    NSBezierPath *outerCirc;
    outerCirc = [NSBezierPath bezierPathWithOvalInRect:
                         NSMakeRect(position.x - 23, position.y - 23, 46, 46)];
    [clr set];
    [outerCirc stroke];
    [[clr colorWithAlphaComponent:0.7] set];
    [outerCirc fill];
    [clr set];
    // Draw the "handle"
    NSRect nucleusRect = NSMakeRect(position.x - DOT_NUCLEUS_RADIUS,
                                    position.y - DOT_NUCLEUS_RADIUS,
                                    DOT_NUCLEUS_RADIUS * 2,
                                    DOT_NUCLEUS_RADIUS * 2);
    [[NSBezierPath bezierPathWithOvalInRect:nucleusRect] fill];
}

@end

如您所见, Dot 类非常轻量级,并使用贝塞尔曲线路径画画。超级视图可以处理用户交互。

I'd suggest not using the deltaX and deltaY; try using the event's location in the superview. You'll need a reference to the subview.

// In the superview
- (void)mouseDragged:(NSEvent *)event {
    NSPoint mousePoint = [self convertPoint:[event locationInWindow] 
                             fromView:nil];

    // Could also add the width of the moving rectangle to this check 
    // to keep any part of it from going outside the superview
    mousePoint.x = MAX(0, MIN(mousePoint.x, self.bounds.size.width));
    mousePoint.y = MAX(0, MIN(mousePoint.y, self.bounds.size.height));

    // position is a custom ivar that indicates the center of the object;
    // you could also use frame.origin, but it looks nicer if objects are 
    // dragged from their centers
    myMovingRectangle.position = mousePoint;
    [self setNeedsDisplay:YES];
}

You'd do essentially the same bounds checking in mouseUp:.

UPDATE: You should also have a look at the View Programming Guide, which walks you through creating a draggable view: Creating a Custom View.


Sample code that should be helpful, though not strictly relevant to your original question:

In DotView.m:

- (void)drawRect:(NSRect)dirtyRect {
    // Ignoring dirtyRect for simplicity
    [[NSColor colorWithDeviceRed:0.85 green:0.8 blue:0.8 alpha:1] set];
    NSRectFill([self bounds]);
    // Dot is the custom shape class that can draw itself; see below
    // dots is an NSMutableArray containing the shapes
    for (Dot *dot in dots) {
        [dot draw];
    }
}

- (void)mouseDown:(NSEvent *)event {
    NSPoint mousePoint = [self convertPoint:[event locationInWindow] 
                             fromView:nil];

    currMovingDot = [self clickedDotForPoint:mousePoint];
    // Move the dot to the point to indicate that the user has
    // successfully "grabbed" it    
    if( currMovingDot ) currMovingDot.position = mousePoint;
    [self setNeedsDisplay:YES];
}

// -mouseDragged: already defined earlier in post

- (void)mouseUp:(NSEvent *)event {
    if( !currMovingDot ) return;

    NSPoint mousePoint = [self convertPoint:[event locationInWindow] 
                             fromView:nil];
    spot.x = MAX(0, MIN(mousePoint.x, self.bounds.size.width));
    spot.y = MAX(0, MIN(mousePoint.y, self.bounds.size.height));

    currMovingDot.position = mousePoint;
    currMovingDot = nil;
    [self setNeedsDisplay:YES];
}

- (Dot *)clickedDotForPoint:(NSPoint)point {
    // DOT_NUCLEUS_RADIUS is the size of the 
    // dot's internal "handle"
    for( Dot *dot in dots ){
        if( (abs(dot.position.x - point.x) <= DOT_NUCLEUS_RADIUS) &&
            (abs(dot.position.y - point.y) <= DOT_NUCLEUS_RADIUS)) {
            return dot;
        }
    }
    return nil;
}

Dot.h

#define DOT_NUCLEUS_RADIUS (5)

@interface Dot : NSObject {
    NSPoint position;
}

@property (assign) NSPoint position;

- (void)draw;

@end

Dot.m

#import "Dot.h"

@implementation Dot

@synthesize position;

- (void)draw {
    //!!!: Demo only: assume that focus is locked on a view.
    NSColor *clr = [NSColor colorWithDeviceRed:0.3 
                                         green:0.2 
                                          blue:0.8 
                                         alpha:1];
    // Draw a nice border
    NSBezierPath *outerCirc;
    outerCirc = [NSBezierPath bezierPathWithOvalInRect:
                         NSMakeRect(position.x - 23, position.y - 23, 46, 46)];
    [clr set];
    [outerCirc stroke];
    [[clr colorWithAlphaComponent:0.7] set];
    [outerCirc fill];
    [clr set];
    // Draw the "handle"
    NSRect nucleusRect = NSMakeRect(position.x - DOT_NUCLEUS_RADIUS,
                                    position.y - DOT_NUCLEUS_RADIUS,
                                    DOT_NUCLEUS_RADIUS * 2,
                                    DOT_NUCLEUS_RADIUS * 2);
    [[NSBezierPath bezierPathWithOvalInRect:nucleusRect] fill];
}

@end

As you can see, the Dot class is very lightweight, and uses bezier paths to draw. The superview can handle the user interaction.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文