我是编程并尝试使用Xcode 9.3和Swift 4.1学习Cocoa for Mac OS的新手。我已经完成了有关C ++和Objective-C的书籍。现在我正在阅读Big Nerd Ranch的书,Mac OS的Cocoa Programming,第5版,它使用Swift 2.0(我自己)。尝试解决从Swift 2.0到4.1的差异一直存在挑战,我不知道这个问题是否是其中的一部分。
总结一下这个练习,我创造了一个模具。部分练习是通过双击模具使模具更改其数量。我这样做了,而且很有效。现在我需要复制骰子两次,总共三个骰子。
我按照步骤复制骰子(突出显示dieView和命令-D),现在我有三个骰子。每个视图都接受First Responder,每个视图随后接受键盘输入以更改模具上显示的数字。我可以用鼠标选择关键窗口,高亮显示哪个窗口处于活动状态。但是,双击“滚动”骰子只适用于原始骰子,而不是其他两个骰子。当它们完全重复时怎么会这样?因为它们被复制了,我怎么能在其他两个骰子中双击才能使用相同的代码呢?
我也尝试将原始模具移动到第三个位置,移动两个新骰子,现在它仍然是第一个位置的模具,双击而不是最后两个。当我将骰子移动到一个堆叠的位置,一个在另一个之上,在另一个之上时,我首先将它们放在窗口的左侧,并且双击对它们中的任何一个都不起作用。我调整了窗口的大小,使它们全部堆叠在左侧,双击仅适用于底部模具。
我知道这与骰子所在的自定义视图窗口有关,但是不应该鼠标点击在该窗口的任何位置注册?显然点击正在注册,因为我可以更改每个骰子的关键窗口。这只是双击功能无法正常工作。
这是我的dieView代码:
import Cocoa
@IBDesignable class DieView: NSView {
var intValue: Int? = 1 {
didSet {
needsDisplay = true
}
}
var pressed: Bool = false {
didSet {
needsDisplay = true
}
}
var dieShape = NSBezierPath()
override var intrinsicContentSize: NSSize {
return NSSize(width: 20, height: 20)
}
override func draw(_ dirtyRect: NSRect) {
let backgroundColor = NSColor.lightGray
backgroundColor.set()
NSBezierPath.fill(bounds)
drawDieWithSize(size: bounds.size)
}
func metricsForSize(size: CGSize) -> (edgeLength: CGFloat, dieFrame: CGRect) {
let edgeLength = min(size.width, size.height)
let padding = edgeLength/10.0
let drawingBounds = CGRect(x: 0, y: 0, width: edgeLength, height: edgeLength)
var dieFrame = drawingBounds.insetBy(dx: padding, dy: padding)
if pressed {
dieFrame = dieFrame.offsetBy(dx: 0, dy: -edgeLength/40)
}
return (edgeLength, dieFrame)
}
func drawDieWithSize(size: CGSize) {
if let intValue = intValue {
let (edgeLength, dieFrame) = metricsForSize(size: size)
let cornerRadius: CGFloat = edgeLength/5.0
let dotRadius = edgeLength/12.0
let dotFrame = dieFrame.insetBy(dx: dotRadius * 2.5, dy: dotRadius * 2.5)
// The glint must be within the dot.
let glintFrame = dotFrame
NSGraphicsContext.saveGraphicsState()
let shadow = NSShadow()
shadow.shadowOffset = NSSize(width: 0, height: -1)
//shadow.shadowBlurRadius = edgeLength/20
shadow.shadowBlurRadius = (pressed ? edgeLength/100 : edgeLength/20)
shadow.set()
// Draw the rounded shape of the die profile:
// Challenge use color Gradient - commented portions are used to make white die and were removed to make code more readable in this post
let gradient = NSGradient(starting: NSColor.red, ending: NSColor.blue)
dieShape =
NSBezierPath(roundedRect: dieFrame, xRadius: cornerRadius, yRadius: cornerRadius)
gradient?.draw(in: dieShape, angle: 1.0)
// Challlenge - use stroke() to add a border the die
NSColor.black.set()
dieShape.lineWidth = 4
dieShape.stroke()
NSGraphicsContext.restoreGraphicsState()
// Shadow will not apply to subequent drawing commands
// ready to draw the dots.
// Nested Function to make drawing dots cleaner:
func drawDot(u: CGFloat, v: CGFloat) {
let dotOrigin = CGPoint(x: dotFrame.minX + dotFrame.width * u,
y: dotFrame.minY + dotFrame.height * v)
let dotRect =
CGRect(origin: dotOrigin, size: CGSize.zero).insetBy(dx: -dotRadius, dy: -dotRadius)
// The dots will be black:
NSColor.black.set()
NSBezierPath(ovalIn: dotRect).fill()
}
// nested function to draw a glint in each dot
func drawGlint(u: CGFloat, v: CGFloat) {
let glintOrigin = CGPoint(x: glintFrame.minX + glintFrame.width * u,
y: glintFrame.minY + glintFrame.height * v)
let glintRect =
CGRect(origin: glintOrigin,
size: CGSize(width: 3.5, height: 3.5)).insetBy(dx: -0.5, dy: -0.5)
// Glints will be white
NSColor.white.set()
NSBezierPath(rect: glintRect).fill()
}
// If intVlaue is in range...
if intValue >= 1 && intValue <= 6 {
// Draw the dots:
if intValue == 1 || intValue == 3 || intValue == 5 {
drawDot(u: 0.5, v: 0.5) // Center dot
drawGlint(u: 0.55, v: 0.55)
}
if intValue >= 2 && intValue <= 6 {
drawDot(u: 0, v: 1) // upper left
drawGlint(u: 0.05, v: 1.05)
drawDot(u: 1, v: 0) // Lower right
drawGlint(u: 1.05, v: 0.05)
}
if intValue >= 4 && intValue <= 6 {
drawDot(u: 1, v: 1) // Upper right
drawGlint(u: 1.05, v: 1.05)
drawDot(u: 0, v: 0) // lower left
drawGlint(u: 0.05, v: 0.05)
}
if intValue == 6 {
drawDot(u: 0, v: 0.5) // Mid left/right
drawGlint(u: 0.05, v: 0.55)
drawDot(u: 1, v: 0.5)
drawGlint(u: 1.05, v: 0.55)
}
} else {
let paraStyle = NSParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
paraStyle.alignment = .center
let font = NSFont.systemFont(ofSize: edgeLength * 0.5)
let attrs = [NSAttributedStringKey.foregroundColor: NSColor.black,
NSAttributedStringKey.font : font,
NSAttributedStringKey.paragraphStyle: paraStyle ]
let string = "\(intValue)" as NSString
string.drawCentered(in: dieFrame, attributes: attrs)
}
}
}
func randomize() {
intValue = Int(arc4random_uniform(5)) + 1
}
// MARK: - Mouse Events
override func mouseDown(with event: NSEvent) {
if dieShape.contains(event.locationInWindow) {
Swift.print("mouseDown CLICKCOUNT: \(event.clickCount)")
let dieFrame = metricsForSize(size: bounds.size).dieFrame
let pointInView = convert(event.locationInWindow, from: nil)
pressed = dieFrame.contains(pointInView)
}
}
override func mouseDragged(with event: NSEvent) {
Swift.print("mouseDragged")
}
override func mouseUp(with event: NSEvent) {
if dieShape.contains(event.locationInWindow) {
Swift.print("mouseUp clickCount: \(event.clickCount)")
if event.clickCount == 2 {
randomize()
}
pressed = false
}
}
// MARK: - First Responder
override func drawFocusRingMask() {
NSBezierPath.fill(bounds)
}
override var focusRingMaskBounds: NSRect {
return bounds
}
override var acceptsFirstResponder: Bool { return true }
override func becomeFirstResponder() -> Bool {
return true
}
override func resignFirstResponder() -> Bool {
return true
}
// MARK: Ketboard Events
override func keyDown(with event: NSEvent) {
interpretKeyEvents([event])
}
override func insertText(_ insertString: Any) {
let text = insertString as! String
if let number = Int(text) {
intValue = number
}
}
override func insertTab(_ sender: Any?) {
window?.selectNextKeyView(sender)
}
override func insertBacktab(_ sender: Any?) {
window?.selectPreviousKeyView(sender)
}
}
答案 0 :(得分:0)
快速浏览后,我的猜测是if dieShape.contains(event.locationInWindow)
中的问题。
diaShape
位于本地(视图)坐标中。但是event.locationInWindow
在窗口坐标中。在测试生命值之前,您需要先将窗口坐标转换为本地坐标。
请参阅locationInWindow
的文档:
let eventLocation = event.locationInWindow
let localPoint = self.convert(eventLocation, from: nil)
if dieShape.contains(localPoint)
...
如果您的视图接近窗口的原点,则窗口和视图坐标之间的差异足够小,可能会起作用,但距离较远的区域不会。