Drawing selection box (rubberbanding, marching ants) in Cocoa, ObjectiveC

后端 未结 4 1114
没有蜡笔的小新
没有蜡笔的小新 2020-12-13 07:28

I\'ve currently implemented a simple selection box using mouse events and redrawing a rectangle on mouse drag. Here\'s my code:

-(void)drawRect:(NSRect)dirty         


        
相关标签:
4条回答
  • 2020-12-13 08:13

    You can use QuartzCore to animate the "marching ants" for you, if you want. This gets you completely out of the world of manually drawing the rubber-banded selection box, too. You just define the path that defines that box, and let Core Animation take care of drawing the box, as well as animating it.

    In the XIB's "View Effects" Inspector, turn on "Core Animation", and then you can do something like:

    #import <QuartzCore/QuartzCore.h>
    #import "CustomView.h"
    
    @interface CustomView ()
    
    @property (nonatomic) NSPoint startPoint;
    @property (nonatomic, strong) CAShapeLayer *shapeLayer;
    
    @end
    
    @implementation CustomView
    
    #pragma mark Mouse Events
    
    - (void)mouseDown:(NSEvent *)theEvent
    {
        self.startPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    
        // create and configure shape layer
    
        self.shapeLayer = [CAShapeLayer layer];
        self.shapeLayer.lineWidth = 1.0;
        self.shapeLayer.strokeColor = [[NSColor blackColor] CGColor];
        self.shapeLayer.fillColor = [[NSColor clearColor] CGColor];
        self.shapeLayer.lineDashPattern = @[@10, @5];
        [self.layer addSublayer:self.shapeLayer];
    
        // create animation for the layer
    
        CABasicAnimation *dashAnimation;
        dashAnimation = [CABasicAnimation animationWithKeyPath:@"lineDashPhase"];
        [dashAnimation setFromValue:@0.0f];
        [dashAnimation setToValue:@15.0f];
        [dashAnimation setDuration:0.75f];
        [dashAnimation setRepeatCount:HUGE_VALF];
        [self.shapeLayer addAnimation:dashAnimation forKey:@"linePhase"];
    }
    
    - (void)mouseDragged:(NSEvent *)theEvent
    {
        NSPoint point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
    
        // create path for the shape layer
    
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathMoveToPoint(path, NULL, self.startPoint.x, self.startPoint.y);
        CGPathAddLineToPoint(path, NULL, self.startPoint.x, point.y);
        CGPathAddLineToPoint(path, NULL, point.x, point.y);
        CGPathAddLineToPoint(path, NULL, point.x, self.startPoint.y);
        CGPathCloseSubpath(path);
    
        // set the shape layer's path
    
        self.shapeLayer.path = path;
    
        CGPathRelease(path);
    }
    
    - (void)mouseUp:(NSEvent *)theEvent
    {
        [self.shapeLayer removeFromSuperlayer];
        self.shapeLayer = nil;
    }
    
    @end
    

    This way, Core Animation takes care of the marching ants for you.

    marching ants demo

    0 讨论(0)
  • 2020-12-13 08:14

    swift 3 version:

    import Cocoa
    import QuartzCore
    
    class myView: NSView {
    
        //MARK:Properties
        var startPoint : NSPoint!
        var shapeLayer : CAShapeLayer!
    
        override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
    
            // Drawing code here.
        }
    
    
        override func mouseDown(with event: NSEvent) {
    
            self.startPoint = self.convert(event.locationInWindow, from: nil)
    
            shapeLayer = CAShapeLayer()
            shapeLayer.lineWidth = 1.0
            shapeLayer.fillColor = NSColor.clear.cgColor
            shapeLayer.strokeColor = NSColor.black.cgColor
            shapeLayer.lineDashPattern = [10,5]
            self.layer?.addSublayer(shapeLayer)
    
            var dashAnimation = CABasicAnimation()
            dashAnimation = CABasicAnimation(keyPath: "lineDashPhase")
            dashAnimation.duration = 0.75
            dashAnimation.fromValue = 0.0
            dashAnimation.toValue = 15.0
            dashAnimation.repeatCount = .infinity
            shapeLayer.add(dashAnimation, forKey: "linePhase")
    
    
    
    
        }
    
        override func mouseDragged(with event: NSEvent) {
    
            let point : NSPoint = self.convert(event.locationInWindow, from: nil)
            let path = CGMutablePath()
            path.move(to: self.startPoint)
            path.addLine(to: NSPoint(x: self.startPoint.x, y: point.y))
            path.addLine(to: point)
            path.addLine(to: NSPoint(x:point.x,y:self.startPoint.y))
            path.closeSubpath()
            self.shapeLayer.path = path
        }
    
        override func mouseUp(with event: NSEvent) {
            self.shapeLayer.removeFromSuperlayer()
            self.shapeLayer = nil
        }
    
    
    }
    
    0 讨论(0)
  • 2020-12-13 08:28

    This post is a little older but I still found no good solution (Framework) on the web. But I guess this is a task (content selection) everyone have to master someday and there is no API from Apple for it. However I made my own framework it and put on Github: https://github.com/ckteebe/MultiSelectionSuiteFramework.

    0 讨论(0)
  • 2020-12-13 08:28

    If anybody needs a Swift 2.0 version, here it is:

    import Cocoa
    import QuartzCore
    
    
    class myView: NSView {
    
    var startPoint : NSPoint!
    var shapeLayer : CAShapeLayer!
    
    
    
    override func drawRect(dirtyRect: NSRect) {
        super.drawRect(dirtyRect)
    
        // Drawing code here.
    }
    
    
    override func mouseDown(theEvent: NSEvent) {
    
        self.startPoint = self.convertPoint(theEvent.locationInWindow, fromView: nil)
    
        shapeLayer = CAShapeLayer()
        shapeLayer.lineWidth = 1.0
        shapeLayer.fillColor = NSColor.clearColor().CGColor
        shapeLayer.strokeColor = NSColor.blackColor().CGColor
        shapeLayer.lineDashPattern = [10,5]
        self.layer?.addSublayer(shapeLayer)
    
        var dashAnimation = CABasicAnimation()
        dashAnimation = CABasicAnimation(keyPath: "lineDashPhase")
        dashAnimation.duration = 0.75
        dashAnimation.fromValue = 0.0
        dashAnimation.toValue = 15.0
        dashAnimation.repeatCount = .infinity
        shapeLayer.addAnimation(dashAnimation, forKey: "linePhase")
    
    
    
    
    }
    
    override func mouseDragged(theEvent: NSEvent) {
    
        let point : NSPoint = self.convertPoint(theEvent.locationInWindow, fromView: nil)
        let path = CGPathCreateMutable()
        CGPathMoveToPoint(path, nil, self.startPoint.x, self.startPoint.y)
        CGPathAddLineToPoint(path, nil, self.startPoint.x, point.y)
        CGPathAddLineToPoint(path, nil, point.x, point.y)
        CGPathAddLineToPoint(path, nil, point.x, self.startPoint.y)
        CGPathCloseSubpath(path)
        self.shapeLayer.path = path
    }
    
    override func mouseUp(theEvent: NSEvent) {
        self.shapeLayer.removeFromSuperlayer()
        self.shapeLayer = nil
    }
    
    
    
    }
    
    0 讨论(0)
提交回复
热议问题