使用swift 3绘制可调整大小的矩形

时间:2017-03-13 02:17:18

标签: ios swift swift3 drawing

如何在UIView中绘制可调整大小的矩形,我在google和github上进行了很多搜索,我发现这一个Click Here使用swift 2.3并且我将它转换为swift 3 ..但我无法在绘制后调整矩形大小它就是代码

//
//  ResizableRectangleView.swift
//  DrawShapes
//
//  Created by Jordan Focht on 3/9/15.
//  Copyright (c) 2015 Jordan Focht. All rights reserved.
//

import Foundation
import UIKit

private let DefaultTint = UIColor(red: 0, green: 164 / 255.0, blue: 1.0, alpha: 1.0).cgColor
private let DefaultStrokeTint = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0).cgColor
private let ClearColor = UIColor.clear.cgColor
private let DefaultCircleRadius: CGFloat = 8
private let CornerTouchSize: CGFloat = 44

protocol ResizableRectangleViewDelegate : class {
    func didSelectResizableRectangleView(_ view: ResizableRectangleView)
    func didDeselectResizableRectangleView(_ view: ResizableRectangleView)
}

class ResizableRectangleView: UIControl {
    fileprivate var borderLayer: CALayer = CALayer()
    fileprivate var topLeftCircle = CALayer()
    fileprivate var topRightCircle = CALayer()
    fileprivate var bottomLeftCircle = CALayer()
    fileprivate var bottomRightCircle = CALayer()


weak var delegate: ResizableRectangleViewDelegate?
var strokeTintColor: CGColor = DefaultStrokeTint
var circleRadius: CGFloat = DefaultCircleRadius

var nLocation : CGPoint!

override var frame: CGRect {
    get {
        return super.frame
    }
    set {
        super.frame = newValue
        self.updateLayers()
    }
}

override var isSelected: Bool {
    get {
        return super.isSelected
    }
    set {
        let changed = self.isSelected != newValue
        super.isSelected = newValue
        if changed {
            if isSelected {
                self.delegate?.didSelectResizableRectangleView(self)
            } else {
                self.delegate?.didDeselectResizableRectangleView(self)
            }
        }
    }
}

func updateLayers() {
    if self.layer.sublayers == nil {
        self.layer.addSublayer(self.borderLayer)
        self.layer.addSublayer(self.topLeftCircle)
        self.layer.addSublayer(self.topRightCircle)
        self.layer.addSublayer(self.bottomLeftCircle)
        self.layer.addSublayer(self.bottomRightCircle)
        let layers = (self.layer.sublayers ?? []) as [CALayer]
        for layer in layers {
            layer.contentsScale = UIScreen.main.scale
        }
    }

    self.updateBorderLayer()
    let circleFrame = self.borderLayer.frame
    updateCircleLayer(topLeftCircle, center: CGPoint(x: circleFrame.origin.x, y: circleFrame.origin.y))
    updateCircleLayer(topRightCircle, center: CGPoint(x: circleFrame.origin.x, y: circleFrame.maxY))
    updateCircleLayer(bottomLeftCircle, center: CGPoint(x: circleFrame.maxX, y: circleFrame.origin.y))
    updateCircleLayer(bottomRightCircle, center: CGPoint(x: circleFrame.maxX, y: circleFrame.maxY))
}

func borderedFrame() -> CGRect {
    return self.borderLayer.frame
}

//  var trackingFrameTransform: ((CGPoint) -> ())?

func moveFrame(_ originalFrame: CGRect, initialTouchLocation: CGPoint, _ location: CGPoint) {
    let targetX = originalFrame.origin.x + location.x - initialTouchLocation.x
    let targetY = originalFrame.origin.y + location.y - initialTouchLocation.y
    let insetBounds = self.insetBounds()
    self.frame.origin.x = max(insetBounds.origin.x, min(insetBounds.maxX - self.frame.width, targetX))
    self.frame.origin.y = max(insetBounds.origin.y, min(insetBounds.maxY - self.frame.height, targetY))

    nLocation = location
}

fileprivate func insetBounds() -> CGRect {
    let inset = self.inset()
    let contentBounds = (self.superview as? DrawableView)?.contentBounds ?? self.bounds
    return contentBounds.insetBy(dx: -inset, dy: -inset)
}

func updateRect(_ anchor: CGPoint, initialTouchLocation: CGPoint, originalCorner: CGPoint , _ location: CGPoint) {
    let insetBounds = self.insetBounds()
    let locationX = max(insetBounds.origin.x, min(insetBounds.maxX, location.x))
    let locationY = max(insetBounds.origin.y, min(insetBounds.maxY, location.y))
    let targetX = originalCorner.x + locationX - initialTouchLocation.x
    let targetY = originalCorner.y + locationY - initialTouchLocation.y
    let minSize = self.inset() + circleRadius
    if insetBounds.origin.x < targetX && targetX < insetBounds.maxX {
        self.frame.origin.x = min(targetX, anchor.x)
        self.frame.size.width = max(minSize * 2, abs(anchor.x - targetX))
    }
    if insetBounds.origin.y < targetY && targetY < insetBounds.maxY {
        self.frame.origin.y = min(targetY, anchor.y)
        self.frame.size.height = max(minSize * 2, abs(anchor.y - targetY))
    }

    nLocation = location
}

override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
    CATransaction.begin()
    CATransaction.setDisableActions(true)

    if let superview = self.superview as? DrawableView {
        for view in superview.subviews {
            if let view = view as? ResizableRectangleView {
                if view != self {
                    view.isSelected = false
                    view.updateLayers()
                }
            }
        }
        superview.bringSubview(toFront: self)
    }


    let location = touch.location(in: self.superview)
    nLocation = location
    var anchor: CGPoint?
    var corner: CGPoint?

    switch (location.x, location.y) {
    case (let x, let y) where x < self.frame.origin.x + CornerTouchSize && y < self.frame.origin.y + CornerTouchSize:
        anchor = CGPoint(x: self.frame.maxX, y: self.frame.maxY)
        corner = CGPoint(x: self.frame.minX, y: self.frame.minY)
    case (let x, let y) where x < self.frame.origin.x + CornerTouchSize && y > self.frame.maxY - CornerTouchSize:
        anchor = CGPoint(x: self.frame.maxX, y: self.frame.minY)
        corner = CGPoint(x: self.frame.minX, y: self.frame.maxY)
    case (let x, let y) where x > self.frame.maxX - CornerTouchSize && y < self.frame.origin.y + CornerTouchSize:
        anchor = CGPoint(x: self.frame.minX, y: self.frame.maxY)
        corner = CGPoint(x: self.frame.maxX, y: self.frame.minY)
    case (let x, let y) where x > self.frame.maxX - CornerTouchSize && y > self.frame.maxY - CornerTouchSize:
        anchor = CGPoint(x: self.frame.minX, y: self.frame.minY)
        corner = CGPoint(x: self.frame.maxX, y: self.frame.maxY)
    default:
        self.moveFrame(self.frame, initialTouchLocation: location , nLocation)
    }
    if let anchor = anchor {
        if let corner = corner {
            self.didMove = true
            self.isSelected = true
            self.updateRect(anchor, initialTouchLocation: location, originalCorner: corner, nLocation)
            self.updateLayers()
        }
    }

    CATransaction.commit()
    return true
}

var didMove = false

override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
    CATransaction.begin()
    CATransaction.setDisableActions(true)

    didMove = true
    let location = touch.location(in: self.superview)

    nLocation = location
    //self.trackingFrameTransform?(location)
    self.updateLayers()

    CATransaction.commit()
    return true
}

override func endTracking(_ touch: UITouch?, with event: UIEvent?) {

    CATransaction.begin()
    CATransaction.setDisableActions(true)

    if !didMove {
        self.isSelected = !self.isSelected
    }
    didMove = false
    self.updateLayers()
    // self.trackingFrameTransform = nil
    nLocation = nil
    CATransaction.commit()
}

func updateCircleLayer(_ layer: CALayer, center: CGPoint) {
    layer.isHidden = !self.isSelected
    layer.frame = CGRect(x: center.x - circleRadius, y: center.y - circleRadius, width: 2 * circleRadius, height: 2 * circleRadius)
    layer.backgroundColor = self.tintColor.cgColor
    layer.borderColor = strokeTintColor
    layer.cornerRadius = self.circleRadius
    layer.borderWidth = 1
    layer.setNeedsDisplay()
}

func inset() -> CGFloat {
    let circleInset = (CornerTouchSize - (self.circleRadius * 2)) / 2
    return self.circleRadius + circleInset
}

func updateBorderLayer() {
    self.borderLayer.masksToBounds = false
    self.borderLayer.borderWidth = 1
    self.borderLayer.borderColor = self.tintColor.cgColor
    let inset = self.inset()
    self.borderLayer.frame = self.bounds.insetBy(dx: inset, dy: inset)
    self.borderLayer.setNeedsDisplay()
}

}

DrawableView.swift

import Foundation
import UIKit

struct ColoredRect {
    let color: UIColor
    let origin: CGPoint
    let size: CGSize

    var width: CGFloat {
        get {
            return self.size.width
        }
    }

    var height: CGFloat {
        get {
            return self.size.height
        }
    }

}

class DrawableView: UIControl {
    fileprivate let colorPicker = ColorPicker()
    fileprivate var currentRect: ResizableRectangleView?
    fileprivate var originalLocation: CGPoint?
    fileprivate var rectIsPending = false

    var contentSize: CGSize?
    var contentBounds: CGRect? {
        get {
            if let contentSize = self.contentSize {
                let scale = min(self.bounds.width / contentSize.width, self.bounds.height / contentSize.height)
                let scaledWidth = contentSize.width * scale
                let scaledHeight = contentSize.height * scale
                let x = round(0.5 * (self.bounds.width - scaledWidth))
                let y = round(0.5 * (self.bounds.height - scaledHeight))
                return CGRect(x: x, y: y, width: scaledWidth, height: scaledHeight)
            } else {
                return nil
            }
        }
    }

    var shapes: [ColoredRect] {
        get {
            var shapes = [ColoredRect]()
            for view in self.subviews {
                if let view = view as? ResizableRectangleView {
                    let f = view.convert(view.borderedFrame(), to: self)
                    let relX = min(1.0, max(0.0, f.origin.x / self.bounds.width))
                    let relY = min(1.0, max(0.0, f.origin.y / self.bounds.height))
                    let relWidth = min(1.0, max(0.0, f.width / self.bounds.width))
                    let relHeight = min(1.0, max(0.0, f.height / self.bounds.height))
                    let relOrigin = CGPoint(x: relX, y: relY)
                    let relSize = CGSize(width: relWidth, height: relHeight)
                    let rect = ColoredRect(color: view.tintColor, origin: relOrigin, size: relSize)
                    shapes.append(rect)
                }
            }
            return shapes
        }
        set {
            let shapes = newValue
            for view in self.subviews {
                if let view = view as? ResizableRectangleView {
                    view.removeFromSuperview()
                }
            }
            self.colorPicker.alpha = 0
            for shape in shapes {
                let x = shape.origin.x * self.bounds.width
                let y = shape.origin.y * self.bounds.height
                let width = shape.width * self.bounds.width
                let height = shape.height * self.bounds.height
                let rectFrame = CGRect(x: x, y: y, width: width, height: height)
                let view = ResizableRectangleView()
                let inset = view.inset()
                view.tintColor = shape.color
                view.frame = rectFrame.insetBy(dx: -inset, dy: -inset)
                view.delegate = self
                self.addSubview(view)
            }
            self.bringSubview(toFront: self.colorPicker)
        }
    }

//    override init() {
//        super.init()
//        self.addColorPicker()
//    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.addColorPicker()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addColorPicker()
    }

    override func awakeFromNib() {
        super.awakeFromNib()

        self.addColorPicker()
    }

    fileprivate func addColorPicker() {
        colorPicker.delegate = self
        colorPicker.alpha = 0
        self.addSubview(colorPicker)
        self.bringSubview(toFront: self.colorPicker)
        colorPicker.frame = CGRect(x: self.bounds.width - 44, y: 0, width: 44, height: self.bounds.height)

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        colorPicker.frame = CGRect(x: self.bounds.width - 44, y: 0, width: 44, height: self.bounds.height)
    }

    override var canBecomeFirstResponder : Bool {
        return true
    }

    override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) {
        if (motion == UIEventSubtype.motionShake) {
            self.shapes = []
        }
    }

    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        let location = touch.location(in: self)
        if let contentBounds = self.contentBounds {
            if (!contentBounds.contains(location)) {
                return false
            }
        }
        rectIsPending = true
        let newRect = ResizableRectangleView()
        newRect.frame = CGRect(x: location.x, y: location.y, width: 1, height: 1)
        newRect.tintColor = UIColor(cgColor: self.colorPicker.color)
        self.currentRect = newRect
        self.originalLocation = location

        CATransaction.begin()
        CATransaction.setDisableActions(true)
        for view in self.subviews {
            if let view = view as? ResizableRectangleView {
                view.isSelected = false
                view.updateLayers()
            }
        }
        CATransaction.commit()

        return true
    }

    override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        if let currentRect = self.currentRect {
            if rectIsPending {
                currentRect.delegate = self
                self.addSubview(currentRect)
                self.bringSubview(toFront: self.colorPicker)
            }
            CATransaction.begin()
            CATransaction.setDisableActions(true)
            if let originalLocation = self.originalLocation {
                let location = touch.location(in: self)
                currentRect.updateRect(originalLocation, initialTouchLocation: originalLocation, originalCorner: originalLocation, location)

              //  currentRect.updateRect(originalLocation, initialTouchLocation: originalLocation, originalCorner: originalLocation ,location: location)
            }
            CATransaction.commit()
        }
        return super.continueTracking(touch, with: event)
    }

    override func endTracking(_ touch: UITouch?, with event: UIEvent?) {

        self.currentRect = nil
        self.rectIsPending = false
    }

}


extension DrawableView: ColorPickerDelegate {

    func colorPicker(_ picker: ColorPicker, didChangeColor color: CGColor) {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
        for view in self.subviews {
            if let view = view as? ResizableRectangleView {
                if view.isSelected {
                    view.tintColor = UIColor(cgColor: color)
                    view.updateLayers()
                }
            }
        }
        CATransaction.commit()
    }

}

extension DrawableView: ResizableRectangleViewDelegate {

    func didSelectResizableRectangleView(_ view: ResizableRectangleView) {
        self.bringSubview(toFront: self.colorPicker)
        if self.colorPicker.alpha == 0 {
            UIView.animate(withDuration: 0.15, animations: {
                self.colorPicker.alpha = 1
            }) 
        }
    }

    func didDeselectResizableRectangleView(_ view: ResizableRectangleView) {
        self.bringSubview(toFront: self.colorPicker)
        if colorPicker.alpha == 1 {
            let selectionCount = self.subviews.reduce(0) {
                acc, view in

                if let view = view as? ResizableRectangleView {
                    return acc + (view.isSelected ? 1 : 0)
                }
                return acc
            }
            if selectionCount == 0 {
                UIView.animate(withDuration: 0.15, animations: {
                    self.colorPicker.alpha = 0
                }) 
            }
        }
    }
}

0 个答案:

没有答案