Swift 4.0으로 팝업 된 SwiftColorPicker (Matthias Schlemm)의 문제를 해결하려고합니다. 전체 PickerImage 클래스를 포함 할 것이므로 전체 컨텍스트를 볼 수 있습니다.Swift 4에서 null을 반환하는 CGDataProvider
let provider = CGDataProvider(data: mutableData)
createImageFromData
에서
import UIKit
import ImageIO
open class PickerImage {
var provider:CGDataProvider!
var imageSource:CGImageSource?
var image:UIImage?
var mutableData:CFMutableData
var width:Int
var height:Int
fileprivate func createImageFromData(_ width:Int, height:Int) {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
let provider = CGDataProvider(data: mutableData)
imageSource = CGImageSourceCreateWithDataProvider(provider!, nil)
let cgimg = CGImage(width: Int(width), height: Int(height), bitsPerComponent: Int(8), bitsPerPixel: Int(32), bytesPerRow: Int(width) * Int(4),
space: colorSpace, bitmapInfo: bitmapInfo, provider: provider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
image = UIImage(cgImage: cgimg!)
}
func changeSize(_ width:Int, height:Int) {
self.width = width
self.height = height
let size:Int = width * height * 4
CFDataSetLength(mutableData, size)
createImageFromData(width, height: height)
}
init(width:Int, height:Int) {
self.width = width
self.height = height
let size:Int = width * height * 4
mutableData = CFDataCreateMutable(kCFAllocatorDefault, size)
createImageFromData(width, height: height)
}
open func writeColorData(_ h:CGFloat, a:CGFloat) {
let d = CFDataGetMutableBytePtr(self.mutableData)
if width == 0 || height == 0 {
return
}
var i:Int = 0
let h360:CGFloat = ((h == 1 ? 0 : h) * 360)/60.0
let sector:Int = Int(floor(h360))
let f:CGFloat = h360 - CGFloat(sector)
let f1:CGFloat = 1.0 - f
var p:CGFloat = 0.0
var q:CGFloat = 0.0
var t:CGFloat = 0.0
let sd:CGFloat = 1.0/CGFloat(width)
let vd:CGFloat = 1/CGFloat(height)
var double_s:CGFloat = 0
var pf:CGFloat = 0
let v_range = 0..<height
let s_range = 0..<width
for v in v_range {
pf = 255 * CGFloat(v) * vd
for s in s_range {
i = (v * width + s) * 4
d?[i] = UInt8(255)
if s == 0 {
q = pf
d?[i+1] = UInt8(q)
d?[i+2] = UInt8(q)
d?[i+3] = UInt8(q)
continue
}
double_s = CGFloat(s) * sd
p = pf * (1.0 - double_s)
q = pf * (1.0 - double_s * f)
t = pf * (1.0 - double_s * f1)
switch(sector) {
case 0:
d?[i+1] = UInt8(pf)
d?[i+2] = UInt8(t)
d?[i+3] = UInt8(p)
case 1:
d?[i+1] = UInt8(q)
d?[i+2] = UInt8(pf)
d?[i+3] = UInt8(p)
case 2:
d?[i+1] = UInt8(p)
d?[i+2] = UInt8(pf)
d?[i+3] = UInt8(t)
case 3:
d?[i+1] = UInt8(p)
d?[i+2] = UInt8(q)
d?[i+3] = UInt8(pf)
case 4:
d?[i+1] = UInt8(t)
d?[i+2] = UInt8(p)
d?[i+3] = UInt8(pf)
default:
d?[i+1] = UInt8(pf)
d?[i+2] = UInt8(p)
d?[i+3] = UInt8(q)
}
}
}
}
}
라인은 물론, 다음 라인 충돌을 야기하는 무 값을 반환한다. 이 스위프트에 잘 작동하고 여기에 3
은 디버거의 값은 다음과 같습니다
내 현재 스킬 셋 이상 조금이다, 그래서 나는 실제로 무슨 일이 일어나고 있는지와 사투를 벌인거야 이리. Swift 4.0에서 이와 관련된 변경 사항이있어 CGDataProvider
호출이 무효 값을 반환하게합니까?
편집 : 다음은 PickerImage 개체를 초기화하는 ColorPicker 클래스입니다.
import UIKit
import ImageIO
open class ColorPicker: UIView {
fileprivate var pickerImage1:PickerImage?
fileprivate var pickerImage2:PickerImage?
fileprivate var image:UIImage?
fileprivate var data1Shown = false
fileprivate lazy var opQueue:OperationQueue = {return OperationQueue()}()
fileprivate var lock:NSLock = NSLock()
fileprivate var rerender = false
open var onColorChange:((_ color:UIColor, _ finished:Bool)->Void)? = nil
open var a:CGFloat = 1 {
didSet {
if a < 0 || a > 1 {
a = max(0, min(1, a))
}
}
}
open var h:CGFloat = 0 { // // [0,1]
didSet {
if h > 1 || h < 0 {
h = max(0, min(1, h))
}
renderBitmap()
setNeedsDisplay()
}
}
fileprivate var currentPoint:CGPoint = CGPoint.zero
open func saturationFromCurrentPoint() -> CGFloat {
return (1/bounds.width) * currentPoint.x
}
open func brigthnessFromCurrentPoint() -> CGFloat {
return (1/bounds.height) * currentPoint.y
}
open var color:UIColor {
set(value) {
var hue:CGFloat = 1
var saturation:CGFloat = 1
var brightness:CGFloat = 1
var alpha:CGFloat = 1
value.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
a = alpha
if hue != h || pickerImage1 === nil {
self.h = hue
}
currentPoint = CGPoint(x: saturation * bounds.width, y: brightness * bounds.height)
self.setNeedsDisplay()
}
get {
return UIColor(hue: h, saturation: saturationFromCurrentPoint(), brightness: brigthnessFromCurrentPoint(), alpha: a)
}
}
public override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
isUserInteractionEnabled = true
clipsToBounds = false
self.addObserver(self, forKeyPath: "bounds", options: [NSKeyValueObservingOptions.new, NSKeyValueObservingOptions.initial], context: nil)
}
deinit {
self.removeObserver(self, forKeyPath: "bounds")
}
open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "bounds" {
if let pImage1 = pickerImage1 {
pImage1.changeSize(Int(self.bounds.width), height: Int(self.bounds.height))
}
if let pImage2 = pickerImage2 {
pImage2.changeSize(Int(self.bounds.width), height: Int(self.bounds.height))
}
renderBitmap()
self.setNeedsDisplay()
} else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first! as UITouch
handleTouche(touch, ended: false)
}
open override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first! as UITouch
handleTouche(touch, ended: false)
}
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first! as UITouch
handleTouche(touch, ended: true)
}
fileprivate func handleColorChange(_ color:UIColor, changing:Bool) {
if color !== self.color {
if let handler = onColorChange {
handler(color, !changing)
}
setNeedsDisplay()
}
}
fileprivate func handleTouche(_ touch:UITouch, ended:Bool) {
// set current point
let point = touch.location(in: self)
if self.bounds.contains(point) {
currentPoint = point
} else {
let x:CGFloat = min(bounds.width, max(0, point.x))
let y:CGFloat = min(bounds.width, max(0, point.y))
currentPoint = CGPoint(x: x, y: y)
}
handleColorChange(pointToColor(point), changing: !ended)
}
fileprivate func pointToColor(_ point:CGPoint) ->UIColor {
let s:CGFloat = min(1, max(0, (1.0/bounds.width) * point.x))
let b:CGFloat = min(1, max(0, (1.0/bounds.height) * point.y))
return UIColor(hue: h, saturation: s, brightness: b, alpha:a)
}
fileprivate func renderBitmap() {
if self.bounds.isEmpty {
return
}
if !lock.try() {
rerender = true
return
}
rerender = false
if pickerImage1 == nil {
self.pickerImage1 = PickerImage(width: Int(bounds.width), height: Int(bounds.height))
self.pickerImage2 = PickerImage(width: Int(bounds.width), height: Int(bounds.height))
}
opQueue.addOperation {() -> Void in
// Write colors to data array
if self.data1Shown { self.pickerImage2!.writeColorData(self.h, a:self.a) }
else { self.pickerImage1!.writeColorData(self.h, a:self.a)}
// flip images
// self.image = self.data1Shown ? self.pickerImage2!.image! : self.pickerImage1!.image!
self.data1Shown = !self.data1Shown
// make changes visible
OperationQueue.main.addOperation({() -> Void in
self.setNeedsDisplay()
self.lock.unlock()
if self.rerender {
self.renderBitmap()
}
})
}
}
open override func draw(_ rect: CGRect) {
if let img = image {
img.draw(in: rect)
}
//// Oval Drawing
let ovalPath = UIBezierPath(ovalIn: CGRect(x: currentPoint.x - 5, y: currentPoint.y - 5, width: 10, height: 10))
UIColor.white.setStroke()
ovalPath.lineWidth = 1
ovalPath.stroke()
//// Oval 2 Drawing
let oval2Path = UIBezierPath(ovalIn: CGRect(x: currentPoint.x - 4, y: currentPoint.y - 4, width: 8, height: 8))
UIColor.black.setStroke()
oval2Path.lineWidth = 1
oval2Path.stroke()
}
}
그게 다야! 데이터의 길이를 설정하면 iOS 11 (심지어 어쨌든) 용도로 배포 할 때도 수정됩니다. 많은 감사합니다. – Lastmboy
당신은 영웅입니다! –