NSView上实现图片(nsview)的旋转拖拽

本文介绍了如何在NSView上实现图片的旋转和拖拽功能。通过创建一个canvasView作为画布,定义一个ImageData对象存储图片信息,并在canvasView上添加一个拖拽框view来实现拖拽和旋转。具体实现包括定义数据结构,添加视图,以及在MediaImageItemView和CanvasShapeView中处理拖拽和旋转逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原理:在实际操作中以一个canvasView 作为画布,然后定义一个关于图片的数据,画在一个一一对应的NSView中,然后在canvasView最上面加一层拖拽框view 作相关拖拽,旋转操作。例子代码为伪代码,只为说明思路,并未完整实现。读者可以自行根据实际需要自行实现。


1.数据结构


图片数据

@interface ImageData : NSObject {

@property (nonatomic, assign) double rotation;             

@property (nonatomic, assign) NSPoint centerPoint; // 在canvasview bounds 上显示 图片的中心点

@property (nonatomic, assign) NSSize mediaSize;a

@property (nonatomic, assign) CGImageRef p_originImageRef_;// 用于画图

@property (nonatomic, assign) CGImageRef p_showImageRef_;         // 各种处理之后画图

@property (nonatomic, strong) MediaImageItemView * itemView;// 自定义类用于画图,作为canvasSuperView的subview

}


画布


先定义一个总的背景视图canvasSuperView,其他的view都作为它的子视图

@property (assign) IBOutlet NSView * canvasSuperView;

canvas视图 自定义CanvasView类,实现画背景图方法即可

@property (nonatomic, strong) CanvasView * p_canvasView;

拖拽框视图 自定义CanvasShapeView类,实现画拖拽点方法即可

@property (nonatomic, strong) CanvasShapeView * p_shapeView;


 self.p_canvasView = [[CanvasView new] autorelease];

 self.p_canvasView.delegate = self;

 [self.canvasSuperView addSubview:self.p_canvasView positioned:NSWindowAbove relativeTo:nil];

 self.p_canvasView.frame = self.canvasSuperView.bounds;

    

 self.p_shapeView = [[CanvasShapeView new] autorelease];

 self.p_shapeView.delegate = self;

 [self.canvasSuperView addSubview:self.p_shapeView positioned:NSWindowAbove relativeTo:nil];

 self.p_shapeView.frame = self.canvasSuperView.bounds;



下面以加入一张图片到canvasview为例


首先根据图片路径创建一个图片数据对象

ImageData * testData = [ImageData createImageDataWithPath:path];


+ (ImageData *)createImageDataWithPath:(NSString *)path {

    ImageData * item = [[ImageData new] autorelease];

   

    if ([[NSFileManager defaultManager] fileExistsAtPath:path] == NO) {

         return nil;

    }


   NSImage * image = [[[NSImage alloc] initWithContentsOfFile:self.path] autorelease];

   self.p_originImageRef_ = createCGImageRefFromNSImage(image);

    if (item.originImageRef == NULL) {

        return nil;

    }

    item.centerPoint = NSMakePoint(50, 50);

    NSSize size = NSMakeSize(CGImageGetWidth(item.originImageRef), CGImageGetHeight(item.originImageRef));

    item.originImageSize = size;

    item.mediaSize = size;

    item.rotation = 0;

    

    return item;

}


- (void)reFlushImageDataItemViewInSuperview:(ImageData *)imageItem {

    if (testData.itemView == nil) {

        MediaImageItemView * view = [[MediaImageItemView new] autorelease];

        view.imageItem = imageItem;

        testData.itemView = view;

    }

    // rotation0 ,设置好frame 在调整回原来的旋转度数

    [testData.itemView setFrameCenterRotation:0];

    NSRect frame = [CommonFunction rectForCenter:testData.centerPoint size:testData.mediaSize];

    [testData.itemView setFrame:frame];

    [testData.itemView setFrameCenterRotation:testData.rotation];

    

    if ([testData.itemView superview] != self.canvasSuperView) {

        [self.canvasSuperView addSubview:testData.itemView positioned:NSWindowAbove relativeTo:nil];

    }

    // 保持拖拽框的view在最上面,好相应鼠标事件

    [self.canvasSuperView addSubview:self.shapeView positioned:NSWindowAbove relativeTo:nil];

    

    [self.canvasSuperView setNeedsDisplay:YES];

}



根据canvassuperview的bounds ,调整第一次导入到canvas,imagedata 的 中心位置和显示大小

testData.originImageSize = NSMakePoint(..)

testData.mediaSize = size * scale; // 可自定义缩放



2.三个view的各自主要实现部分


2.1 MediaImageItemView.m 中只用画图即可

- (void)drawRect:(NSRect)dirtyRect

{

    [super drawRect:dirtyRect];

    

    @autoreleasepool {

        CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];

        NSRect rect = self.bounds;

        CGContextDrawImage(imageContext, rect, self.imageItem.showImageRef);

    }

}


2.2 canvasView 的实现,也是作为画背景图,这里无关痛痒,不画亦可。


3.3 shapeview 的实现,这里就是主要的拖拽实现


定义一个拖拽的枚举类型,通过鼠标点击point判断选择的是那个点z作为拖拽点

typedef NS_ENUM(NSInteger, CanvasShapeViewDragType) {

    CanvasShapeViewDrag_None,

    

    CanvasShapeViewDrag_TopLeft,

    CanvasShapeViewDrag_TopRight,

    CanvasShapeViewDrag_BottomRigth,

    CanvasShapeViewDrag_BottomLeft,

    

    CanvasShapeViewDrag_MiddleLeft,

    CanvasShapeViewDrag_MiddleRight,

    CanvasShapeViewDrag_MiddleTop,

    CanvasShapeViewDrag_MiddleBottom,

    

    CanvasShapeViewDrag_MiddleCenter,

};

    

通过testdata的itemview


// 画的时候用,这里可以选择画在itemview上 ,也可以选择画在shapeview上,这里为了实际好操作,把9个拖拽点画在了itemview上,然后通过origin = [self convertPoint:origin fromView:self.p_mediaItemView];转换到shapeview上,在画。


- (NSRect)contentRect {                 

    NSRect rect = self.p_mediaItemView.bounds;

    return rect;

}


// 移动的时候用 比如拖的是中间部分,那个就要改变itemview的frame

- (NSRect)contentFrame {                

    NSRect rect = self.p_mediaItemView.frame;

    return rect;

}


拖拽核心部分在于判断mousedown点到哪个点以及之后的处理,各个function未实现的如字面意思

- (void)mouseDown:(NSEvent *)theEvent {

    NSPoint mouse = [self convertPoint:theEvent.locationInWindow fromView:nil];

    [self checkIfMouseDownOnMedia:mouse];

    if (self.currentMedia == nil) {

        return;

    }

    

    CanvasShapeViewDragType dragType = [self checkDragTypeWithPointOnShape:mouse];

        

    [self setNeedsDisplay:YES];

    

    NSRect oldFrame = self.contentFrame;

    

    if (dragType != CanvasShapeViewDrag_None) {

        while ( (theEvent = [self.window nextEventMatchingMask:NSLeftMouseDraggedMask|NSLeftMouseUpMask]) && ([theEvent type] == NSLeftMouseDragged)) {

            @autoreleasepool {

                NSPoint currentPoint, lastPoint = NSZeroPoint;

                if (dragType == CanvasShapeViewDrag_MiddleCenter) {

                    currentPoint = [self.superview convertPoint:theEvent.locationInWindow fromView:nil];

                    lastPoint = [self.superview convertPoint:mouse fromView:self];

                } else {

                    currentPoint = [self.p_mediaItemView convertPoint:theEvent.locationInWindow fromView:nil];

                    lastPoint = [self.p_mediaItemView convertPoint:mouse fromView:self];

                }

                [self startDragAction:dragType from:lastPoint to:currentPoint oldFrame:oldFrame];

            }

        }

    }

    if ([theEvent type] == NSLeftMouseUp) {

        if ([theEvent clickCount]==1) {

            [self.nextResponder mouseUp:theEvent];

        }

        [self setNeedsDisplay:YES];

    }

    

}


- (void)startDragAction:(CanvasShapeViewDragType)dragType from:(NSPoint)last to:(NSPoint)mouse oldFrame:(NSRect)oldCropRect {

    NSSize offset = NSMakeSize(mouse.x - last.x, mouse.y - last.y);

    NSRect rect = NSZeroRect;

    if (dragType==CanvasShapeViewDrag_MiddleCenter){

        

        rect = oldCropRect;

        rect.origin.x += offset.width;

        rect.origin.y += offset.height;

    

    } else {

         // 实现其他点时,怎么改变当前itemviewframe

rect …;

    }

    

    [self  updateCurrentMediaViewFrameWhenShapeDraged:rect];

    [self setNeedsDisplay:YES];

}


要特别注意的就是这里得到rect之后在有旋转的情况下,怎么把得到的rect改动到itemview上去

self.selectedMediaItemView 即为testdata的itemview

- (void)updateCurrentMediaViewFrameWhenShapeDraged:(NSRect)rect {

    

    self.selectedMediaItemView.frame = rect;

    [self.selectedMediaItemView setFrameCenterRotation:0];

    rect = self.selectedMediaItemView.frame;


    NSPoint centerOnRect = NSMakePoint(NSMidX(rect), NSMidY(rect));

    

    [self mediaItem:self.selectedMedia centerPoint:centerOnRect];  // self.selectedMedia.centerPoint = centerOnRect;

    [self mediaItem:self.selectedMedia mediaSize:rect.size];  // self.selectedMedia.mediaSize = rect.size;


    [self.selectedMediaItemView setFrameCenterRotation:self.selectedMedia.rotation];

}




旋转的实现 改变rotate值即可。关键在于之后刷新图的位置调用

reFlushImageDataItemViewInSuperview: (上面有)注意先把view的frameCenterRotation值0,设置好之后还原。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值