0

I'm iterating through an array of UIImages that all need to be rotated by 90 degrees. This works...sometimes.

I'll randomly get a case where 2 or 3 of the images failed to rotate but I can't consistently reproduce so debugging has been troublesome.

Here is how I'm looping through my array:

func processPhotosForRotation(completion:() -> Void) {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {

        for (index,image) in self.frameImages.enumerate() {

            let flippedImage = image.imageRotatedByDegrees(90, flip: false)
            self.frameImages[index] = flippedImage
        }

        //I need the images forwards, then backwards
        self.frameImages.appendContentsOf(self.frameImages.reverse())

        dispatch_async(dispatch_get_main_queue()) {
            completion()
        }
    }
}

Here is how I'm rotating the images:

extension UIImage {

    public func imageRotatedByDegrees(degrees: CGFloat, flip: Bool) -> UIImage {

        let degreesToRadians: (CGFloat) -> CGFloat = {
            return $0 / 180.0 * CGFloat(M_PI)
        }

        // calculate the size of the rotated view's containing box for our drawing space
        let rotatedViewBox = UIView(frame: CGRect(origin: CGPointZero, size: size))
        let t = CGAffineTransformMakeRotation(degreesToRadians(degrees));
        rotatedViewBox.transform = t
        let rotatedSize = rotatedViewBox.frame.size

        // Create the bitmap context
        UIGraphicsBeginImageContext(rotatedSize)
        let bitmap = UIGraphicsGetCurrentContext()

        // Move the origin to the middle of the image so we will rotate and scale around the center.
        CGContextTranslateCTM(bitmap, rotatedSize.width / 2.0, rotatedSize.height / 2.0);

        //   // Rotate the image context
        CGContextRotateCTM(bitmap, degreesToRadians(degrees));

        // Now, draw the rotated/scaled image into the context
        var yFlip: CGFloat

        if(flip){
            yFlip = CGFloat(-1.0)
        } else {
            yFlip = CGFloat(1.0)
        }

        CGContextScaleCTM(bitmap, yFlip, -1.0)
        CGContextDrawImage(bitmap, CGRectMake(-size.width / 2, -size.height / 2, size.width, size.height), CGImage)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage
    }
}

Like I said, for ever 1-3 times it works perfectly, then every now and then a few images won't be rotated.

I've tried iterating through the array checking the image.imageOrientation. Which returns the same results Up, even if it's not Up.

random
  • 8,568
  • 12
  • 50
  • 85
  • 2
    You are modifying the array from another thread; array access isn't thread safe, so in some cases you are losing the update to the array. You should dispatch your update to the array (where you set the new image) on a serial queue to avoid concurrent updates; http://stackoverflow.com/questions/24045895/what-is-the-swift-equivalent-to-objective-cs-synchronized – Paulw11 Dec 07 '15 at 20:20
  • @Paulw11 that was the issue. I tried the serial queue but I was still getting the same issue. Now I've moved to flipping the images and putting them into a temporary array. Then replace my original with the temporary one. – random Dec 07 '15 at 20:56
  • @Paulw11 can you move your comment to an answer so I can accept :) – random Dec 07 '15 at 21:12

1 Answers1

1

You are mutating the array from multiple threads at the same time as you are enumerating it. Access to an array is not thread safe so sometimes the change made by one thread is overwritten by another thread.

You should store your update image references in a temporary arrays and assign this to your property when you are done. This will avoid modifying the array as it is enumerated.

I also recommend that the updates to the temporary array are dispatched synchronously on a serial queue to avoid concurrent updates.

Paulw11
  • 108,386
  • 14
  • 159
  • 186