Я новичок в программировании и пытаюсь изучить Cocoa для Mac OS, используя Xcode 9.3 и Swift 4.1. Я работал над книгами на C ++ и Objective-C. Сейчас я работаю над книгой «Big Nerd Ranch», «Программирование какао для Mac OS», 5-е издание, в котором используется Swift 2.0 (самостоятельно). Были проблемы, пытаясь преодолеть различия от Swift 2.0 до 4.1, и я не знаю, является ли эта проблема частью этой проблемы.
Чтобы подвести итоги упражнения, я создал кубик. Часть упражнения состоит в том, чтобы заставить матрицу изменить свой номер, дважды щелкнув по матрице. Я сделал это, и это сработало. Теперь мне нужно дублировать кубик дважды, чтобы получить три кубика.
Я выполнил шаги, чтобы скопировать кристалл (выделите dieView и команду-D), и теперь у меня есть три кубика. Каждое представление принимает Первый ответчик, и каждое представление впоследствии принимает ввод с клавиатуры, чтобы изменить число, отображаемое на матрице. Я могу выбрать ключевое окно с помощью мыши, и выделение показывает, какое окно активно. Однако двойной щелчок, чтобы «бросить» кости, работает только на оригинальном кубике, а не на двух других. Как это может быть, когда они являются точными копиями? И так как они были скопированы, как я могу заставить двойной щелчок работать на двух других кубиках, поскольку они должны иметь одинаковый код?
Я также попытался переместить оригинальный кубик в третью позицию, переместив два новых кубика, и теперь он остается в первой позиции, которая работает с двойным щелчком, а не с двумя последними в строке. Когда я перемещал кубики в сложенную позицию, один над другим, над другим, сначала у меня они были в левой части окна, и двойной щелчок не работал ни на одном из них. Я изменил размеры окна, чтобы они все были сложены на левой стороне, и двойной щелчок работает только на нижней матрице.
Я знаю, что это как-то связано с окном настраиваемого вида, в котором находятся кости, но разве щелчки мыши не должны регистрироваться где-либо в этом окне? Очевидно, что щелчки регистрируются, потому что я могу изменить ключевое окно для каждой кости. Это просто функция двойного щелчка, которая работает неправильно.
Вот мой код 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)
}
}