如何在 Cocoa 中将 NSImageView 移动到 NSView 中?

How to move NSImageView inside NSView in Cocoa?

在我的 Mac OS 应用程序中,我需要一个功能,以便能够在此 NSImageView 中发生 mouseDown 事件(由用户触发)后在 NSView 内部移动 NSImageView。当用户触发鼠标 Up 事件时,此视图必须移动到最后一个 mouseDrag 事件方向并停留在那里。在移动过程中,我希望 NSImageView 在屏幕上可见(它应该与鼠标光标一起移动)。

我读过 Apple 的处理鼠标事件指南,https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/HandlingMouseEvents.html 我还下载了这个示例代码:https://developer.apple.com/library/content/samplecode/DragItemAround/Introduction/Intro.html

两个链接都包含 Objective C 中的代码。 DragItemAround 的代码已过时。我试图在 GitHub 和其他 Whosebug 线程上搜索解决方案,但没有找到任何有效的解决方案。

很高兴听到这个问题的答案。我正在使用 Swift 3.

我创建了一个自定义的 MovableImageView,它是 NSImageView 的子class,代码如下:

import Cocoa

class MovableImageView: NSImageView {

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        // Drawing code here.
        // setup the starting location of the
        // draggable item

    }

    override func mouseDown(with event: NSEvent) {
        Swift.print("mouseDown")

        //let window = self.window!
        //let startingPoint = event.locationInWindow


    }


    override func mouseDragged(with event: NSEvent) {
        Swift.print("mouseDragged")

        self.backgroundColor = NSColor.black

    }

    override func mouseUp(with event: NSEvent) {
        Swift.print("mouseUp")
    }

}

之后,我在 Interface Builder 中将此 class 设置为 NSImageView。我还在 Interface Builder 中为此 NSImageView 设置了约束。

现在我想知道如何在 NSView 中移动 NSImageView? (Swift 3, XCode 8)

您需要保存mouseDown 点,稍后用于偏移。请检查以下代码:

class MovableImageView: NSImageView {

var firstMouseDownPoint: NSPoint = NSZeroPoint

init() {
    super.init(frame: NSZeroRect)
    self.wantsLayer = true
    self.layer?.backgroundColor = NSColor.red.cgColor
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

override func draw(_ dirtyRect: NSRect) {
    super.draw(dirtyRect)

    // Drawing code here.
}

override func mouseDown(with event: NSEvent) {
    Swift.print("mouseDown")
    firstMouseDownPoint = (self.window?.contentView?.convert(event.locationInWindow, to: self))!
}

override func mouseDragged(with event: NSEvent) {
    Swift.print("mouseDragged")
    let newPoint = (self.window?.contentView?.convert(event.locationInWindow, to: self))!
    let offset = NSPoint(x: newPoint.x - firstMouseDownPoint.x, y: newPoint.y - firstMouseDownPoint.y)
    let origin = self.frame.origin
    let size = self.frame.size
    self.frame = NSRect(x: origin.x + offset.x, y: origin.y + offset.y, width: size.width, height: size.height)
}

override func mouseUp(with event: NSEvent) {
    Swift.print("mouseUp")

    }
}

在父视图中,只需将此 MovableImageView 添加为子视图,如下所示:

let view = MovableImageView()
view.frame = NSRect(x: 0, y: 0, width: 100, height: 100)
self.view.addSubview(view) //Self is your parent view

@brianLikeApple 回答的限制版本:

class MovableImageView: NSImageView {

var firstMouseDownPoint: NSPoint = NSZeroPoint
var xConstraint: NSLayoutConstraint!
var yContstraint: NSLayoutConstraint!

required init?(coder aDecoder: NSCoder) {
   super.init(coder: aDecoder)
    self.wantsLayer = true
    self.layer?.backgroundColor = NSColor.red.cgColor
}
override func draw(_ dirtyRect: NSRect) {
    super.draw(dirtyRect)
    
    // Drawing code here.
}

override func mouseDown(with event: NSEvent) {
    Swift.print("mouseDown")
    firstMouseDownPoint = (self.window?.contentView?.convert(event.locationInWindow, to: self))!
}

override func mouseDragged(with event: NSEvent) {
    Swift.print("mouseDragged")
    let newPoint = (self.window?.contentView?.convert(event.locationInWindow, to: self))!
    let offset = NSPoint(x: newPoint.x - firstMouseDownPoint.x, y: newPoint.y - firstMouseDownPoint.y)
    let origin = self.frame.origin
    let size = self.frame.size
    yContstraint.constant = (self.superview?.frame.height)! - origin.y - offset.y - size.height
    xConstraint.constant = origin.x + offset.x
}

override func mouseUp(with event: NSEvent) {
    Swift.print("mouseUp")
    
}

这是一个可以拖动的 NSView。它使用与@ExeRhythm 答案完全相同的约束。

import AppKit
class DraggableNSView: NSView {
    var down: NSPoint = NSZeroPoint
    @IBOutlet var across: NSLayoutConstraint!
    @IBOutlet var up: NSLayoutConstraint!

    override func mouseDown(with e: NSEvent) {
        down = window?.contentView?.convert(e.locationInWindow, to: self) ?? NSZeroPoint
    }

    override func mouseDragged(with e: NSEvent) {
        guard let m = window?.contentView?.convert(e.locationInWindow, to: self) else { return }
        let p = frame.origin + (m - down)
        across.constant = p.x
        up.constant = p.y
    }
}

别忘了必须在故事板中连接 acrossup 约束!

注意。如果您更喜欢“向下”约束而不是“向上”约束,则为:

        // if you prefer a "down" constraint:
        down.constant = (superview?.frame.height)! - p.y - frame.size.height

一如既往,在你的项目中添加这两个函数,

func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
    return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
}

func +(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
    return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}

请注意,您当然可以对 NSView 的任何子类(例如 NSImageView)执行此操作:

class DraggableNSImageView: NSView {
     ... same