带有核心图形的iOS动画

时间:2016-12-02 04:09:01

标签: ios swift animation core-graphics uibezierpath

我目前正在iOS中使用 Swift 创建一个Hangman游戏应用程序。

我已完成所有游戏机制,并希望使用Core Graphics绘制刽子手和绞刑架。我可以使用UIBezierPath绘制绞架和刽子手,并将视图的每个部分(绞架,头部,主体,左臂......)的图形分解为单独的函数,这些函数被调用以创建整个图像。所有这些都在一个自定义的UIView类中。

我似乎无法弄清楚如何在用户做出不正确的猜测时添加Hangman图像的连续部分。我试图避免我在其间循环的一系列图像。相反,我想使用相同的自定义视图使用Core Graphics逐个绘制棒图。

如何实现绘制图中每个连续部分的函数?

类似“self.customView.updateDrawing()”的东西,其中customView中的updateDrawing方法将从模型中获取有关错误猜测数量的信息,并调用适当的方法来绘制棒图所需的部分。

更新

这是我尝试将此帖https://codereview.stackexchange.com/questions/97424/hangman-in-swift中的视图实现到我自己的刽子手游戏中。它们非常适合绘制绞架,但我实际上无法让图像更新并绘制连续的身体部位以添加刽子手。到目前为止,这是我的代码:

GameViewTwo:这是上面帖子中的GameView,我试图将其拆分并将不同的部分作为图层添加到视图中。

 import UIKit

enum BodyPart: Int {
    case Head = 1
    case Body = 2
    case LeftArm = 5
    case RightArm = 6
    case LeftLeg = 3
    case RightLeg = 4
    case LeftEye = 7
    case RightEye = 8
    case Mouth = 9
    case Unknown = 10
}


class GameViewTwo: UIView {

    var path = UIBezierPath()
    var newPartLayer = CAShapeLayer()

    private var bodyStart: CGPoint = CGPoint.zero
    private var bodyEnd: CGPoint = CGPoint.zero
    private var headMiddle: CGPoint = CGPoint.zero

    struct DrawingConstants {
        static let gallowBaseStartScale: CGFloat = 0.15
        static let gallowBaseEndScale: CGFloat = 0.85
        static let gallowBaseHeight: CGFloat = 10
        static let gallowHeight: CGFloat = 0.05        //static let gallowHeight: CGFloat = 0.15
        static let gallowHeightStart: CGFloat = 0.175
        static let gallowHeightWidth: CGFloat = 10
        static let gallowAcrossScale: CGFloat = 0.5
        static let gallowTipHeight: CGFloat = 17.5
        static let headRadius: CGFloat = 16
        static let bodyLength: CGFloat = 25
        static let bodyHeight: CGFloat = 25
        static let legLength: CGFloat = 50
        static let grassHeightScale: CGFloat = 0.68
        static let armBack: CGFloat = 5
    }

    struct ScaleConstants {
        static let bodyLength: CGFloat = 50
        static let limbLength: CGFloat = 25
        static let handHeightScale: CGFloat = 0.4
        static let headRadius: CGFloat = 20
        static let eyeRadius = CGFloat(0.15 * ScaleConstants.headRadius)
        static let eyeOffset = CGFloat(0.3 * ScaleConstants.headRadius)
        static let mouthOffSet = CGFloat(0.3 * ScaleConstants.headRadius)
        static let mouthRadius = CGFloat(0.25 * ScaleConstants.headRadius)
    }

    var part : BodyPart?

    func setPart(part: BodyPart){
        self.part = part
    }


    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
        drawGallow()

    }


    func add(part: BodyPart){

        let partPath = path(forPart: part)
        newPartLayer.frame = bounds
        newPartLayer.path = partPath.cgPath
        newPartLayer.strokeColor = UIColor.black.cgColor
        let strokeAnim = CABasicAnimation(keyPath: "strokeEnd")
        strokeAnim.fromValue = 0
        strokeAnim.toValue = 1
        strokeAnim.duration = 1
        layer.addSublayer(newPartLayer)
        newPartLayer.add(strokeAnim, forKey: "path")

    }

    func path(forPart: BodyPart)-> UIBezierPath {

        switch forPart {

        case BodyPart.Head :
            let centerX = CGFloat(bounds.size.width * DrawingConstants.gallowAcrossScale - (DrawingConstants.gallowHeightWidth / 2))
            let centerY = CGFloat(bounds.size.height * DrawingConstants.gallowHeight + DrawingConstants.gallowBaseHeight + DrawingConstants.gallowTipHeight + ScaleConstants.headRadius)
            let center = CGPoint(x: centerX, y: centerY)
            headMiddle = center
            path = UIBezierPath(arcCenter: center, radius: ScaleConstants.headRadius, startAngle: CGFloat(0), endAngle: CGFloat(2 * M_PI), clockwise: true)
            path.lineWidth = CGFloat(2)

            return path

        case BodyPart.Body :
            let add = CGFloat(DrawingConstants.gallowBaseHeight + DrawingConstants.gallowTipHeight + 2 * ScaleConstants.headRadius)
            let startPointY = CGFloat(bounds.size.height * DrawingConstants.gallowHeight + add)
            let startPointX = CGFloat(bounds.size.width * DrawingConstants.gallowAcrossScale - (DrawingConstants.gallowHeightWidth / 2))
            let startPoint = CGPoint(x: startPointX, y: startPointY)
            let endPoint = CGPoint(x: startPoint.x, y: startPoint.y + ScaleConstants.bodyLength)
            bodyStart = startPoint
            bodyEnd = endPoint
            path.lineWidth = CGFloat(2)
            path.move(to: startPoint)
            path.addLine(to: endPoint)
            return path

        case BodyPart.LeftLeg :
            let startPoint = CGPoint(x: bodyEnd.x, y: bodyEnd.y)
            let endPoint = CGPoint(x: startPoint.x - ScaleConstants.limbLength, y: startPoint.y + ScaleConstants.limbLength)
            path.lineWidth = CGFloat(2)
            path.move(to: startPoint)
            path.addLine(to: endPoint)
            return path

        case BodyPart.RightLeg :
            let startPoint = CGPoint(x: bodyEnd.x, y: bodyEnd.y)
            let endPoint = CGPoint(x: startPoint.x + ScaleConstants.limbLength, y: startPoint.y + ScaleConstants.limbLength)
            path.lineWidth = CGFloat(2)
            path.move(to: startPoint)
            path.addLine(to: endPoint)
            return path

        case BodyPart.LeftArm :
            let startPoint = CGPoint(x: bodyStart.x, y: bodyStart.y + ScaleConstants.handHeightScale * ScaleConstants.bodyLength)
            let endPoint = CGPoint(x: startPoint.x - ScaleConstants.limbLength, y: startPoint.y - ScaleConstants.limbLength * ScaleConstants.handHeightScale)
            path.lineWidth = CGFloat(2)
            path.move(to: startPoint)
            path.addLine(to: endPoint)
            return path

        case BodyPart.RightArm :
            let startPoint = CGPoint(x: bodyStart.x, y: bodyStart.y + ScaleConstants.handHeightScale * ScaleConstants.bodyLength)
            let endPoint = CGPoint(x: startPoint.x + ScaleConstants.limbLength, y: startPoint.y - ScaleConstants.limbLength * ScaleConstants.handHeightScale)
            path.lineWidth = CGFloat(2)
            path.move(to: startPoint)
            path.addLine(to: endPoint)
            return path

        case BodyPart.LeftEye :
            UIColor.black.set()
            let eyeMiddle = CGPoint(x: headMiddle.x - ScaleConstants.eyeOffset, y: headMiddle.y - ScaleConstants.eyeOffset)

            let path = UIBezierPath(arcCenter: eyeMiddle, radius: ScaleConstants.eyeRadius, startAngle: 0, endAngle: CGFloat(2 * M_PI), clockwise: true)
            path.lineWidth = CGFloat(1)
            return path

        case BodyPart.RightEye :
            UIColor.black.set()
            let eyeMiddle = CGPoint(x: headMiddle.x + ScaleConstants.eyeOffset, y: headMiddle.y - ScaleConstants.eyeOffset)

            let path = UIBezierPath(arcCenter: eyeMiddle, radius: ScaleConstants.eyeRadius, startAngle: 0, endAngle: CGFloat(2 * M_PI), clockwise: true)
            path.lineWidth = CGFloat(1)
            return path

        default:
            return path
        }
    }

    /******************************************************************************************/

    func connectPoints(bottomLeftPoint: CGPoint, bottomRightPoint: CGPoint, topLeftPoint: CGPoint, topRightPoint: CGPoint, color: UIColor) {
        color.set()

        let path = UIBezierPath()
        path.move(to: bottomLeftPoint)
        path.addLine(to: topLeftPoint)
        path.addLine(to: topRightPoint)
        path.addLine(to: bottomRightPoint)
        path.close()
        path.fill()
        path.stroke()

    }

    func calculateMidPoint(point1: CGPoint, point2: CGPoint) -> CGPoint {
        return CGPoint(x: (point1.x + point2.x) / 2, y: (point1.y + point2.y) / 2)

    }

    /*****************************************************************************************/

    func drawGrass() {
        let topStartPoint = CGPoint(x: CGFloat(0), y: CGFloat(bounds.size.height * DrawingConstants.grassHeightScale))
        let topRightPoint = CGPoint(x: CGFloat(bounds.size.width), y: topStartPoint.y)
        let bottomRightPoint = CGPoint(x: topRightPoint.x, y: CGFloat(bounds.size.height))
        let bottomLeftPoint = CGPoint(x: CGFloat(0), y: bottomRightPoint.y)

        connectPoints(bottomLeftPoint: bottomLeftPoint, bottomRightPoint: bottomRightPoint, topLeftPoint: topStartPoint, topRightPoint: topRightPoint, color: UIColor.green)
    }

    func drawSky() {
        let bottomLeftPoint = CGPoint(x: CGFloat(0), y: CGFloat(bounds.size.height * DrawingConstants.grassHeightScale))
        let topLeftPoint = CGPoint(x: CGFloat(0), y: CGFloat(0))
        let topRightPoint = CGPoint(x: CGFloat(bounds.size.width), y: CGFloat(0))
        let bottomRightPoint = CGPoint(x: CGFloat(bounds.size.width), y: CGFloat(bounds.size.height * DrawingConstants.grassHeightScale))

        connectPoints(bottomLeftPoint: bottomLeftPoint, bottomRightPoint: bottomRightPoint, topLeftPoint: topLeftPoint, topRightPoint: topRightPoint, color: UIColor.cyan)
    }

    func drawGallow() {
        drawGallowBase()
        drawGallowHeight()
        drawGallowAcross()
        drawGallowTip()
    }

    func drawGallowBase() {
        let bottomLeftPoint = CGPoint(x: CGFloat(bounds.size.width * DrawingConstants.gallowBaseStartScale), y: CGFloat(bounds.size.height * DrawingConstants.grassHeightScale))
        let topLeftPoint = CGPoint(x: bottomLeftPoint.x, y: bottomLeftPoint.y - DrawingConstants.gallowBaseHeight)
        let topRightPoint = CGPoint(x: CGFloat(bounds.size.width * DrawingConstants.gallowBaseEndScale), y: topLeftPoint.y)
        let bottomRightPoint = CGPoint(x: topRightPoint.x, y: bottomLeftPoint.y)

        connectPoints(bottomLeftPoint: bottomLeftPoint, bottomRightPoint: bottomRightPoint, topLeftPoint: topLeftPoint, topRightPoint: topRightPoint, color: UIColor.brown)
    }

    func drawGallowHeight() {
        let bottomLeftPoint = CGPoint(x: CGFloat(bounds.size.width * DrawingConstants.gallowHeightStart), y: CGFloat(bounds.size.height * DrawingConstants.grassHeightScale - DrawingConstants.gallowBaseHeight))
        let bottomRightPoint = CGPoint(x: bottomLeftPoint.x + DrawingConstants.gallowHeightWidth, y: bottomLeftPoint.y)
        let topLeftPoint = CGPoint(x: bottomLeftPoint.x, y: bounds.size.height * DrawingConstants.gallowHeight)
        let topRightPoint = CGPoint(x: bottomRightPoint.x, y: topLeftPoint.y)

        connectPoints(bottomLeftPoint: bottomLeftPoint, bottomRightPoint: bottomRightPoint, topLeftPoint: topLeftPoint, topRightPoint: topRightPoint, color: UIColor.brown)
    }

    func drawGallowAcross() {
        let bottomLeftPoint = CGPoint(x: CGFloat(bounds.size.width * DrawingConstants.gallowHeightStart) + DrawingConstants.gallowHeightWidth, y: CGFloat(bounds.size.height * DrawingConstants.gallowHeight + DrawingConstants.gallowBaseHeight))
        let bottomRightPoint = CGPoint(x: CGFloat(bounds.size.width * DrawingConstants.gallowAcrossScale), y: bottomLeftPoint.y)
        let topLeftPoint = CGPoint(x: bottomLeftPoint.x, y: CGFloat(bounds.size.height * DrawingConstants.gallowHeight))
        let topRightPoint = CGPoint(x: CGFloat(bottomRightPoint.x), y: topLeftPoint.y)
        connectPoints(bottomLeftPoint: bottomLeftPoint, bottomRightPoint: bottomRightPoint, topLeftPoint: topLeftPoint, topRightPoint: topRightPoint, color: UIColor.brown)
    }

    func drawGallowTip() {
        let topLeftPoint = CGPoint(x: CGFloat(bounds.size.width * DrawingConstants.gallowAcrossScale - DrawingConstants.gallowHeightWidth), y: CGFloat(bounds.size.height * DrawingConstants.gallowHeight + DrawingConstants.gallowBaseHeight))
        let topRightPoint = CGPoint(x: CGFloat(bounds.size.width * DrawingConstants.gallowAcrossScale), y: topLeftPoint.y)
        let bottomLeftPoint = CGPoint(x: topLeftPoint.x, y: topLeftPoint.y + DrawingConstants.gallowTipHeight)
        let bottomRightPoint = CGPoint(x: topRightPoint.x, y: bottomLeftPoint.y)

        connectPoints(bottomLeftPoint: bottomLeftPoint, bottomRightPoint: bottomRightPoint, topLeftPoint: topLeftPoint, topRightPoint: topRightPoint, color: UIColor.brown)
    }

    /*******************************************************************************************/




}

然后从我的GameViewController中调用它,就像这样

let newPart = BodyPart(rawValue: (model?.attempts)!)

            self.hangmanView.add(part: newPart!)

其中hangmanView是GameViewTwo

2 个答案:

答案 0 :(得分:2)

为每个身体部位创建一个枚举。使它成为一个整数,以便您可以使用错误答案的数量对其进行实例化。

enum BodyPart: Int {
    case head = 0
    case leftArm
    case rightArm
...
}

制作新的身体部位

let newPart = BodyPart(rawValue: incorrectGuesses - 1)

为您的观点提供一些功能......

func add(part: BodyPart){
    // first get a drawing path according to that part
    let partPath = path(for: part)

    // lets add a new layer so we can animate it seperately
    let newPartLayer = CAShapeLayer()
    newPartLayer.frame = bounds
    newPartLayer.path = partPath
    // linewidth, linecap you might want to play with

   let strokeAnim = CABasicAnimation(keyPath: "strokeEnd")
   strokeAnim.fromValue = 0
   strokeAnim.toValue = 1
   strokeAnim.duration = 0.5 // play with it, see what you like

   layer.addSublayer(layer: newPartLayer)
   newPartLayer.add(anim: strokeAnim)
}


func path(for: BodyPart) -> CGPath {

    // draw your part path, return it as a CGPath
}

然后从您的控制器中实例化一个新零件并将其添加到您的hangmanview中,它将在您在绘图代码中绘制时绘制图层。 UIBezierPath有一个.cgPath变量,可以作为核心图形路径进行转换。

let newPart = BodyPart(rawValue: incorrectGuesses - 1)
hangmanView.add(part: newPart)

当你想要一个新游戏时,只需从hangmanView中删除所有子图层。 CAShapeLayers对于简单的小形状非常有用,您可以设置动画更改路径(keyPath:"路径")或沿任一方向抚摸路径。它看起来不错。如果您保留对所有零件图层的引用,您甚至可以独立移动零件或使用它们执行其他有趣的操作。这样,您就可以遵守模型 - 视图 - 控制器范例。您可以将游戏状态远离视图,所有视图都会添加到身体部位,控制器会提供部件来执行此操作。对于简单的事情来说,这并不是什么大不了的事情,但是当你变得更好并且事情变得更复杂时,要记住它是好的。

快速编辑:您可能想尝试使用keyPath:"路径",给它一个起始路径(可能是身体路径)和一个结束路径,看起来这个部分会长出身体。祝你好运!

***编辑回答。

我在这里清理了你的代码。我不完全确定你为什么会有某些变量或者小类私有结构,但没关系,我把它们留在了里面。我移动了一些东西,但大多数都保持不变,除非很明显你在画画一个矩形而不是其他形状。我没有尝试运行它。 Lemme知道它是怎么回事。

enum BodyPart: Int {
case noose = 0
case head
case body
case leftLeg
case rightLeg
case leftArm
case rightArm
case leftEye
case rightEye
case mouth
case unknown
}




class HangmanView: UIView {


var partLayers = [CAShapeLayer]()
private var bodyStart: CGPoint = CGPoint.zero
private var bodyEnd: CGPoint = CGPoint.zero
private var headMiddle: CGPoint = CGPoint.zero

var currentPart: BodyPart?


func clear() {

    for i in partLayers {
        i.removeFromSuperlayer()
    }

    partLayers.removeAll()
}

func add(part: BodyPart){

    currentPart = part


    let newPartLayer = CAShapeLayer()

    let partPath = path(forPart: part)
    newPartLayer.frame = bounds
    newPartLayer.path = partPath.cgPath
    newPartLayer.strokeColor = UIColor.black.cgColor
    newPartLayer.fillColor = UIColor.clear.cgColor

    newPartLayer.lineCap = kCALineCapRound
    newPartLayer.lineWidth = part == .rightEye || part == .leftEye ? 1 : 2

    let strokeAnim = CABasicAnimation(keyPath: "strokeEnd")
    strokeAnim.fromValue = 0
    strokeAnim.toValue = 1
    strokeAnim.duration = 1
    layer.addSublayer(newPartLayer)
    newPartLayer.add(strokeAnim, forKey: "path")
    partLayers.append(newPartLayer)

}

func path(forPart: BodyPart) -> UIBezierPath {

    switch forPart {
    case .noose:
        return UIBezierPath()


    case .head :
        let centerX = CGFloat(bounds.size.width * DrawingConstants.gallowAcrossScale - (DrawingConstants.gallowHeightWidth / 2))
        let centerY = CGFloat(bounds.size.height * DrawingConstants.gallowHeight + DrawingConstants.gallowBaseHeight + DrawingConstants.gallowTipHeight + ScaleConstants.headRadius)
        let center = CGPoint(x: centerX, y: centerY)
        headMiddle = center
        let path = UIBezierPath(arcCenter: center, radius: ScaleConstants.headRadius, startAngle: CGFloat(0), endAngle: CGFloat(2 * M_PI), clockwise: true)

        return path

    case .body :
        let add = CGFloat(DrawingConstants.gallowBaseHeight + DrawingConstants.gallowTipHeight + 2 * ScaleConstants.headRadius)
        let startPointY = CGFloat(bounds.size.height * DrawingConstants.gallowHeight + add)
        let startPointX = CGFloat(bounds.size.width * DrawingConstants.gallowAcrossScale - (DrawingConstants.gallowHeightWidth / 2))
        let startPoint = CGPoint(x: startPointX, y: startPointY)
        let endPoint = CGPoint(x: startPoint.x, y: startPoint.y + ScaleConstants.bodyLength)
        bodyStart = startPoint
        bodyEnd = endPoint
        let path = UIBezierPath()
        path.move(to: startPoint)
        path.addLine(to: endPoint)
        return path

    case .leftLeg :
        let startPoint = CGPoint(x: bodyEnd.x, y: bodyEnd.y)
        let endPoint = CGPoint(x: startPoint.x - ScaleConstants.limbLength, y: startPoint.y + ScaleConstants.limbLength)
        let path = UIBezierPath()
        path.move(to: startPoint)
        path.addLine(to: endPoint)
        return path

    case .rightLeg :
        let startPoint = CGPoint(x: bodyEnd.x, y: bodyEnd.y)
        let endPoint = CGPoint(x: startPoint.x + ScaleConstants.limbLength, y: startPoint.y + ScaleConstants.limbLength)
        let path = UIBezierPath()
        path.move(to: startPoint)
        path.addLine(to: endPoint)
        return path

    case .leftArm :
        let startPoint = CGPoint(x: bodyStart.x, y: bodyStart.y + ScaleConstants.handHeightScale * ScaleConstants.bodyLength)
        let endPoint = CGPoint(x: startPoint.x - ScaleConstants.limbLength, y: startPoint.y - ScaleConstants.limbLength * ScaleConstants.handHeightScale)
        let path = UIBezierPath()
        path.move(to: startPoint)
        path.addLine(to: endPoint)
        return path

    case .rightArm :
        let startPoint = CGPoint(x: bodyStart.x, y: bodyStart.y + ScaleConstants.handHeightScale * ScaleConstants.bodyLength)
        let endPoint = CGPoint(x: startPoint.x + ScaleConstants.limbLength, y: startPoint.y - ScaleConstants.limbLength * ScaleConstants.handHeightScale)
        let path = UIBezierPath()
        path.move(to: startPoint)
        path.addLine(to: endPoint)
        return path

    case .leftEye :
        let eyeMiddle = CGPoint(x: headMiddle.x - ScaleConstants.eyeOffset, y: headMiddle.y - ScaleConstants.eyeOffset)

        let path = UIBezierPath(arcCenter: eyeMiddle, radius: ScaleConstants.eyeRadius, startAngle: 0, endAngle: CGFloat(2 * M_PI), clockwise: true)
        path.lineWidth = CGFloat(1)
        return path

    case .rightEye :
        let eyeMiddle = CGPoint(x: headMiddle.x + ScaleConstants.eyeOffset, y: headMiddle.y - ScaleConstants.eyeOffset)

        let path = UIBezierPath(arcCenter: eyeMiddle, radius: ScaleConstants.eyeRadius, startAngle: 0, endAngle: CGFloat(2 * M_PI), clockwise: true)
        path.lineWidth = CGFloat(1)
        return path

    default:
        return UIBezierPath()
    }
}




override func draw(_ rect: CGRect) {

    guard let context = UIGraphicsGetCurrentContext() else { return }
    // get the drawing context and save the ground state
    context.saveGState()

    // add sky and grass, now they are just rectangles
    context.setFillColor(UIColor.cyan.cgColor)
    context.fill(sky(rect))

    context.setFillColor(UIColor.green.cgColor)
    context.fill(grass(rect))

    context.setFillColor(UIColor.brown.cgColor)

    context.addPath(gallowBase(rect))
    context.addPath(gallowHeight(rect))
    context.addPath(gallowAcross(rect))
    context.addPath(gallowTip(rect))

    context.fillPath()
    context.restoreGState()

}



func gallowBase(_ rect: CGRect) -> CGPath {

    let bottomLeftPoint = CGPoint(x: CGFloat(rect.width * DrawingConstants.gallowBaseStartScale), y: CGFloat(rect.height * DrawingConstants.grassHeightScale))
    let topLeftPoint = CGPoint(x: bottomLeftPoint.x, y: bottomLeftPoint.y - DrawingConstants.gallowBaseHeight)
    let topRightPoint = CGPoint(x: CGFloat(rect.width * DrawingConstants.gallowBaseEndScale), y: topLeftPoint.y)
    let bottomRightPoint = CGPoint(x: topRightPoint.x, y: bottomLeftPoint.y)

    let path = CGMutablePath()
    path.addLines(between: [bottomLeftPoint,topLeftPoint, topRightPoint,bottomRightPoint])
    path.closeSubpath()
    return path
}

func gallowHeight(_ rect: CGRect)  -> CGPath {
    let bottomLeftPoint = CGPoint(x: CGFloat(rect.width * DrawingConstants.gallowHeightStart), y: CGFloat(rect.height * DrawingConstants.grassHeightScale - DrawingConstants.gallowBaseHeight))
    let bottomRightPoint = CGPoint(x: bottomLeftPoint.x + DrawingConstants.gallowHeightWidth, y: bottomLeftPoint.y)
    let topLeftPoint = CGPoint(x: bottomLeftPoint.x, y: rect.height * DrawingConstants.gallowHeight)
    let topRightPoint = CGPoint(x: bottomRightPoint.x, y: topLeftPoint.y)

    let path = CGMutablePath()
    path.addLines(between: [bottomLeftPoint,topLeftPoint, topRightPoint,bottomRightPoint])
    path.closeSubpath()
    return path
}

func gallowAcross(_ rect: CGRect)  -> CGPath {
    let bottomLeftPoint = CGPoint(x: CGFloat(rect.width * DrawingConstants.gallowHeightStart) + DrawingConstants.gallowHeightWidth, y: CGFloat(rect.height * DrawingConstants.gallowHeight + DrawingConstants.gallowBaseHeight))


    let bottomRightPoint = CGPoint(x: CGFloat(rect.width * DrawingConstants.gallowAcrossScale), y: bottomLeftPoint.y)
    let topLeftPoint = CGPoint(x: bottomLeftPoint.x, y: CGFloat(rect.height * DrawingConstants.gallowHeight))
    let topRightPoint = CGPoint(x: CGFloat(bottomRightPoint.x), y: topLeftPoint.y)

    let path = CGMutablePath()
    path.addLines(between: [bottomLeftPoint,topLeftPoint, topRightPoint,bottomRightPoint])
    path.closeSubpath()
    return path
}

func gallowTip(_ rect: CGRect)  -> CGPath {
    let topLeftPoint = CGPoint(x: CGFloat(rect.width * DrawingConstants.gallowAcrossScale - DrawingConstants.gallowHeightWidth), y: CGFloat(rect.height * DrawingConstants.gallowHeight + DrawingConstants.gallowBaseHeight))
    let topRightPoint = CGPoint(x: CGFloat(rect.width * DrawingConstants.gallowAcrossScale), y: topLeftPoint.y)
    let bottomLeftPoint = CGPoint(x: topLeftPoint.x, y: topLeftPoint.y + DrawingConstants.gallowTipHeight)
    let bottomRightPoint = CGPoint(x: topRightPoint.x, y: bottomLeftPoint.y)

    let path = CGMutablePath()
    path.addLines(between: [bottomLeftPoint,topLeftPoint, topRightPoint,bottomRightPoint])
    path.closeSubpath()
    return path
}



func grass(_ rect: CGRect)  -> CGRect {


    let grassRect = CGRect(x: 0, y: rect.height * DrawingConstants.grassHeightScale, width: rect.width, height: (1 - DrawingConstants.grassHeightScale) * rect.height)

    return grassRect
}

func sky(_ rect: CGRect)  -> CGRect {

    let skyRect = CGRect(x: 0, y: 0, width: rect.width, height: DrawingConstants.grassHeightScale * rect.height)

    return skyRect

}


func calculateMidPoint(point1: CGPoint, point2: CGPoint) -> CGPoint {
    return CGPoint(x: (point1.x + point2.x) / 2, y: (point1.y + point2.y) / 2)

}


struct DrawingConstants {
    static let gallowBaseStartScale: CGFloat = 0.15
    static let gallowBaseEndScale: CGFloat = 0.85
    static let gallowBaseHeight: CGFloat = 10
    static let gallowHeight: CGFloat = 0.05        //static let gallowHeight: CGFloat = 0.15
    static let gallowHeightStart: CGFloat = 0.175
    static let gallowHeightWidth: CGFloat = 10
    static let gallowAcrossScale: CGFloat = 0.5
    static let gallowTipHeight: CGFloat = 17.5
    static let headRadius: CGFloat = 16
    static let bodyLength: CGFloat = 25
    static let bodyHeight: CGFloat = 25
    static let legLength: CGFloat = 50
    static let grassHeightScale: CGFloat = 0.68
    static let armBack: CGFloat = 5
}

struct ScaleConstants {
    static let bodyLength: CGFloat = 50
    static let limbLength: CGFloat = 25
    static let handHeightScale: CGFloat = 0.4
    static let headRadius: CGFloat = 20
    static let eyeRadius = 0.15 * headRadius
    static let eyeOffset = 0.3 * headRadius
    static let mouthOffSet = 0.3 * headRadius
    static let mouthRadius = 0.25 * headRadius
}
}

答案 1 :(得分:1)

An approach like this would work. Have a property of your custom HangmanView class called incorrectGuesses. Changing that property will trigger a redraw of the view by calling setNeedsDisplay on itself.

The use of the switch statement and fallthough will allow more of the drawing to appear as the incorrectGuesses increases.

class HangmanView: UIView {
    var incorrectGuesses = 0 {
        didSet {
            self.setNeedsDisplay()
        }
    }

    override func draw(_ rect: CGRect) {
        switch incorrectGuesses {
        case let x where x > 5:
            drawRightLeg()
            fallthrough
        case 5:
            drawLeftLeg()
            fallthrough
        case 4:
            drawRightArm()
            fallthrough
        case 3:
            drawLeftArm()
            fallthrough
        case 2:
            drawBody()
            fallthrough
        case 1:
            drawHead()
            fallthrough
        case 0:
            drawGallows()
        default:
            break
        }
    }

    func drawGallows() {
        // code to draw gallows
    }

    func drawHead() {
        // code to draw head
    }

    func drawBody() {
        // code to draw body
    }

    func drawLeftArm() {
        // code to draw left arm
    }

    func drawRightArm() {
        // code to draw right arm
    }

    func drawLeftLeg() {
        // code to draw left leg
    }

    func drawRightLeg() {
        // code to draw right leg
    }

}

To use it, you could have an outlet for the HangmanView in your ViewController, and you'd just set the incorrectGuesses to update the view:

class ViewController: UIViewController {
    @IBOutlet weak var hangmanView: HangManView!
    var incorrectGuesses = 0

    func gameplay() {
        ...

        if letter not in word {
            incorrectGuesses += 1

            // Update the HangmanView
            hangmanView.incorrectGuesses = incorrectGuesses
        }
    }
}

In your Storyboard, add a UIView and set the Class to HangmanView in the Identity Inspector and then connect it to the outlet.


If you want to animate the drawing of the HangmanView, you could add an animationCount property to the HangmanView. When that is set to a value, such as 3, it will animate the drawing bit by bit every 0.5 seconds. To use it, you'd set hangmanView.animationCount = 3.

var animationCount = 0 {
    didSet {
        incorrectGuesses = 0
        if animationCount > incorrectGuesses {
            _ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
                self.incorrectGuesses += 1
                if self.incorrectGuesses >= self.animationCount {
                    timer.invalidate()
                }
            }
        }
    }
}