Дублирующая проблема представления, используя Какао для OS X, Xcode 9.3, Swift 4.1 - PullRequest
0 голосов
/ 04 мая 2018

Я новичок в программировании и пытаюсь изучить 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)
    }
}

1 Ответ

0 голосов
/ 04 мая 2018

После быстрого взгляда, я думаю, проблема в if dieShape.contains(event.locationInWindow).

diaShape в локальной (просмотр) координате. Но event.locationInWindow находится в координатах окна. Вам нужно сначала преобразовать координаты окна в локальные координаты, прежде чем вы сможете проверить точку попадания.

См. Документацию для locationInWindow:

let eventLocation = event.locationInWindow
let localPoint = self.convert(eventLocation, from: nil)
if dieShape.contains(localPoint)
   ...

Если ваш вид находится близко к началу окна, разница между координатами окна и вида достаточно мала, чтобы она могла работать, но те, которые находятся дальше, не будут.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...