2017-12-09 3 views
3

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に

ここ

を罰金働いていたデバッガでの値です:

メモリ割り当ての対処

enter image description here

は少し私の現在のスキルを超えているので、私は実際に何が起こっているのに苦労していますここに。 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() 
    } 

} 

答えて

2

それはスウィフト4の問題ではありませんが、iOSの11の問題は、あなたは、iOSシミュレータ上で10の作品あなたのコードを見つけることができます。

元のコードは、単なる運に応じて、iOS 10で動作するようです。コードのこの部分で

size、及びempty (that is, content-less)

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) 
} 
mutableData

性、容量のCFMutableDataで初期化されます。

iOS 11では、イニシャライザCGDataProvider.init(data:)は、データプロバイダとして空ではないので、空のCFDataを拒否します。

クイックフィックスは、このようなものになるだろう:

init(width:Int, height:Int) { 
    self.width = width 
    self.height = height 
    let size:Int = width * height * 4 
    mutableData = CFDataCreateMutable(kCFAllocatorDefault, size) 
    CFDataSetLength(mutableData, size) //<-set the length of the data 
    createImageFromData(width, height: height) 
} 

しかし、私はiOSの11

+1

それだけです!データの長さを設定すると、iOS 11として展開されても(私の目的では、とにかく)展開されます。どうもありがとう。 – Lastmboy

+0

あなたは英雄です! –

1

問題は、あなたがその行にletを追加され、それが機能createImageFromData内の新しい変数呼び出しproviderを作成します。こうすることで、クラスのパラメータproviderに渡されることはなく、常にnullになります。 letを削除するだけで正常に動作します。

fileprivate func createImageFromData(_ width:Int, height:Int) { 
    let colorSpace = CGColorSpaceCreateDeviceRGB() 
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue) 
    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!) 
} 
+0

おかげで期待通りのコードの他の部分がうまくいくか分かりません。私は完全にそれを逃した。しかし、私はまだ 'スレッド1:致命的なエラー: 'imageSource = CGImageSourceCreateWithDataProvider(プロバイダ!, nil)'でエラーが発生しました。 – Lastmboy

+0

それは私のために働いています。 'ImagePicker'クラスが作成されたときにどのような値を与えていますか? – barbarity

+0

PickerImageはwidth:240の高さで初期化されています。128 – Lastmboy