【问题标题】:Move NSView around until it hits a border移动 NSView 直到碰到边框
【发布时间】:2011-04-21 13:35:23
【问题描述】:

在一个基于 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

【问题讨论】:

    标签: objective-c cocoa draggable nsview


    【解决方案1】:

    我建议不要使用deltaXdeltaY;尝试在 superview 中使用事件的位置。您需要对子视图的引用。

    // 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: 中进行基本相同的边界检查。

    更新:您还应该查看 View Programming Guide,它会引导您创建可拖动的视图:Creating a Custom View


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

    在 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;
    }
    

    点.h

    #define DOT_NUCLEUS_RADIUS (5)
    
    @interface Dot : NSObject {
        NSPoint position;
    }
    
    @property (assign) NSPoint position;
    
    - (void)draw;
    
    @end
    

    点.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 类非常轻量级,并且使用贝塞尔路径进行绘制。 superview 可以处理用户交互。

    【讨论】:

    • 嗯,好的......还有两个问题:(1)我为什么不应该使用增量? (2)使用超级视图时,我总是要检查我是否正在拖动矩形,即。 e.如果我的鼠标在矩形上方。如果我有多个矩形,这对我来说似乎效率很低,还是我误解了什么?
    • @macs:您不应该使用增量的原因是这样做会导致您面临的问题!至于第二个问题,我实际上几乎建议您可能要重新考虑将矩形作为NSView 子类。如果你只有一个,superview 做图就很简单了。如果您打算拥有很多,那么性能就会成为问题。见Optimizing View Drawing
    • 感谢您的补充说明。我会落实你的建议。
    • @macs:如果你打算有很多矩形,那么你或 AppKit 将不得不进行命中测试。保留NSRects 的数组可以很容易地确定用户想要移动哪个。
    • @Josh:最终应用程序将有多个形状。因为我是 Objective-C 的新手,所以我想弄清楚的第一件事是绘制形状的最佳方法以及如何拖动它们等等。正如编程指南所建议的那样:拥有大量视图并不是很有效,因此我将在之后用轻量级自定义类替换它们。
    猜你喜欢
    • 2011-02-21
    • 1970-01-01
    • 2018-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多