iOS 8 SpriteKit rendering issues - ios8

Upon testing my app in iOS 8, I ran into some serious rendering issues. Instead of wasting text trying to write a comprehensible description, I'll just show you a short video:
This is what it's supposed to work like. iOS 7.
And this is how it breaks in iOS 8.
A slight breakdown of what you saw: The tiles zooming in is an SKAction running on each tile individually. The tiles spawn with an alpha of 0 with the SKAction fading them in, one at a time. If I spawn the tiles with an alpha of 1.0, they show up, but don't perform any action. Other actions randomly don't run either. So there at least appears to be some issue with SKAction. I cannot figure out what's making the rest of the scene disappear when a new round occurs, though. There are no SKActions relating to those nodes or textures at all.
Does anyone know if there are new limits or issues with SpriteKit in iOS 8? Especially relating to SKAction it seems, but anything that might relate to this at all?
Edit: I just want to clarify that even though the video for iOS 8 is the simulator, I've had a report from a beta tester of a problem of this exact description on a real iOS 8 device. The iOS 7 video is a screen capture from my own iOS 7 device.
Edit: here's some code relating to this:
-(void)startNewRoundWithGridSize:(CGFloat)newGridSize andWormCount:(NSInteger)wormCount{
gameData.currentWormScore = (int)currentRound;
[gameData checkAchievements];
previousRandomShake = sharedUtilities.currentTime + 10;
[self randomizeShakeType];
if (gameData.isComputer || gameData.isPhone5) {
directionImage.position = wormDirectionPhone5GamePos;
} else if (gameData.isPhone4) {
directionImage.position = wormDirectionPhone4GamePos;
} else if (gameData.isPad) {
directionImage.position = wormDirectionPadGamePos;
}
directionImage.hidden = VISIBLE;
[directionImage setScale:0.33];
roundCounter.hidden = VISIBLE;
timerBar.hidden = VISIBLE;
int additionalTime = [settingsDict[#"subsequentRoundTimerPoints"] integerValue] + 0.5 * timerValue;
if (currentRound == 1) {
timerValue = [settingsDict[#"firstRoundTimerPoints"] integerValue];
} else {
timerValue = additionalTime;
}
if (gameData.godMode) {
timerValue = 600000000;
}
gridSize = newGridSize;
if (gameData.isPhone4 || gameData.isPhone5 || gameData.isComputer) {
tileBaseSize = 80;
} else if (gameData.isPad) {
tileBaseSize = 192;
}
roundCounter.intValue = currentRound;
SKNode* oldWorld = [self childNodeWithName:#"wormWorld"];
if (oldWorld.parent) {
[oldWorld removeFromParent];
}
[startButton disableButton];
[backButton disableButton];
[instructionsButton disableButton];
startButton.hidden = HIDDEN;
backButton.hidden = HIDDEN;
instructionsButton.hidden = HIDDEN;
wormArray = nil;
wormArray = [[NSMutableArray alloc] init];
tilesArray = nil;
tilesArray = [[NSMutableArray alloc] init];
SKSpriteNode* alphaNode = [SKSpriteNode spriteNodeWithColor:[SKColor whiteColor] size:CGSizeMake(self.size.width, self.size.width)];
alphaNode.anchorPoint = CGPointZero;
alphaNode.position = CGPointMake(0, 0);
CGFloat worldYPos = (self.size.height - self.size.width) / 2 ;
SKCropNode* wormWorld = [SKCropNode node];
wormWorld.position = CGPointMake(0, worldYPos);
wormWorld.name = #"wormWorld";
wormWorld.maskNode = alphaNode;
wormWorld.zPosition = zGameLayer;
[self addChild:wormWorld];
SKSpriteNode* wormAreaBackground = [SKSpriteNode spriteNodeWithColor:[SKColor colorWithRed:0 green:0 blue:0 alpha:0.45] size:CGSizeMake(self.size.width, self.size.width)];
wormAreaBackground.anchorPoint = CGPointZero;
wormAreaBackground.name = #"wormAreaBackground";
wormAreaBackground.zPosition = 0;
[wormWorld addChild:wormAreaBackground];
SKNode* wormScaler = [SKNode node];
wormScaler.name = #"wormScaler";
wormScaler.xScale = 4 / gridSize;
wormScaler.yScale = 4 / gridSize;
wormScaler.zPosition = 1;
[wormWorld addChild:wormScaler];
//tiles
CGFloat totalDuration = 1;
CGFloat duration = 0.15;
SKAction* tileScaleIn = [SKAction sequence:#[
[SKAction scaleTo:5 duration:0],
[SKAction scaleXTo:1.0f y:1.0f duration:0.1],
]];
SKAction* tileFadeIn;
if (gameData.debugMode) {
tileFadeIn = [SKAction fadeAlphaTo:0.5f duration:duration];
} else {
tileFadeIn = [SKAction fadeAlphaTo:1.0f duration:duration];
}
SKAction* tileAnimation = [SKAction group:#[
tileScaleIn,
tileFadeIn,
]];
for (int i = 0; i < gridSize; i++) {
int y = i * tileBaseSize + 0.5 * tileBaseSize; //start new row of tiles and set y pos
for (int h = 0; h < gridSize; h++) {
int x = h * tileBaseSize + 0.5 * tileBaseSize; //set x of current tile
SGG_TileNode* tile = [SGG_TileNode node];
if (gameData.debugMode) {
tile.debugMode = YES;
}
tile.position = CGPointMake(x, y);
tile.gridSize = CGSizeMake(gridSize, gridSize);
tile.tileIndex = i * gridSize + h;
tile.name = #"tile";
tile.zPosition = zGameLayer + 15;
tile.alpha = 0.0;
[wormScaler addChild:tile];
[tilesArray addObject:tile];
SKAction* waitABit = [SKAction waitForDuration:((totalDuration - 0.2) / (gridSize * gridSize)) * tile.tileIndex];
SKAction* totalAction = [SKAction sequence:#[
waitABit,
tileAnimation,
]];
[tile runAction:totalAction withKey:#"startingZoom"];
}
}
gameStarting = YES;
}
I know it's rather long. :(
Edit: Okay, upon some further testing, (in iOS 8 and 8.1) I've added an SKAction to the scene before the game gets started. It works fine until the game starts, but once it starts, the action just stops. Once again, this only happens in iOS 8+ and not iOS 7. Is there some limitation on the number of SKActions that can run simultaneously or something? Do SKActions stop when reaching some sort of memory limit (that would include art assets and other objects in memory)? And if it is, why would it only affect iOS 8+? And no, I'm not pausing the scene by accident. To be absolutely sure, I added
self.paused = NO;
To the update method to be certain the game isn't paused.

Related

Spritekit SKPhysicsBody pin joints

I'm trying to create a ferris wheel. It is composed of three different sprites: the base (legs), the body (circular part that rotates) and the seats (16 in total).
In the code below, I added the base to the scene, then attached the body to the base and finally attached each seat to the body. The seats are currently rotating (in a weird way) with the body. I do not want the seats to rotate at all. What am I doing wrong?
NOTE: Updated with comments and suggestions from #dragoneye
-(void) initFerrisWheel {
self.physicsWorld.gravity = CGVectorMake(0, 0);
//creates the body of the ferris wheel and attaches it to the base
SKSpriteNode *ferrisWheel = (SKSpriteNode*)[self childNodeWithName:#"ferriswheel_base"];
SKTexture *bodyTexture = [[SharedAssetsManager sharedData].menuAssets objectForKey:#"ferriswheel_body"];
SKSpriteNode *body = [[SKSpriteNode alloc] initWithTexture:bodyTexture];
[body setZPosition:2];
[body setPosition:CGPointMake(0, 55)];
body.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.height/2];
[ferrisWheel addChild:body];
//rotates the body of the ferris wheel
SKAction *rotateSequence = [SKAction rotateByAngle:-0.785 duration:2.0];
SKAction *rotateFinal = [SKAction repeatActionForever:rotateSequence];
[body runAction:rotateFinal];
SKTexture *seatTexture = [[SharedAssetsManager sharedData].menuAssets objectForKey:#"ferriswheel_seat"];
int r = 130; //radius of the body for the ferris wheel
for (int i = 0; i < 16; i++) {
//creates a seat and places it on the body
SKSpriteNode *seat = [[SKSpriteNode alloc] initWithTexture:seatTexture];
float x = r * cosf(2*M_PI*i/16);
float y = r * sinf(2*M_PI*i/16);
[seat setPosition:CGPointMake(x, y)];
[seat setZPosition:3];
[body addChild:seat];
//physics body stuff
seat.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:seat.size.height/2];
seat.physicsBody.dynamic = YES;
CGPoint anchor = CGPointMake(x, y);
SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:body.physicsBody bodyB:seat.physicsBody anchor:anchor];
[self.physicsWorld addJoint:pin];
pin.shouldEnableLimits = YES;
pin.lowerAngleLimit = -0.25;
pin.upperAngleLimit = 0.25;
}
}
set joints upper and lower limits for e.g.
pinJoint.shouldEnableLimits=TRUE;
pinJoint.lowerAngleLimit = -0.25;
pinJoint.upperAngleLimit = 0.25;
After wrestling with this for a little while and creating a simple testing project I realized that my issue was that I was attaching the body of the ferris wheel to the base as a child node and I was also attaching the seats to the body as child nodes. While there may be a way to get this to work I based all coordinates on world coordinates instead. This may help someone out in future. Here's the fixed code:
-(void) initFerrisWheel {
//creates the body of the ferris wheel and attaches it to the base
SKSpriteNode *ferrisWheel = (SKSpriteNode*)[self childNodeWithName:#"ferriswheel_base"];
SKTexture *bodyTexture = [[SharedAssetsManager sharedData].menuAssets objectForKey:#"ferriswheel_body"];
SKSpriteNode *body = [[SKSpriteNode alloc] initWithTexture:bodyTexture];
[body setZPosition:2];
[body setPosition:CGPointMake(ferrisWheel.position.x, ferrisWheel.position.y + 55)];
body.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:body.size];
body.physicsBody.affectedByGravity = NO;
[self addChild:body];
//rotates the body of the ferris wheel
SKAction *rotateSequence = [SKAction rotateByAngle:-0.785 duration:2.0];
SKAction *rotateFinal = [SKAction repeatActionForever:rotateSequence];
[body runAction:rotateFinal];
SKTexture *seatTexture = [[SharedAssetsManager sharedData].menuAssets objectForKey:#"ferriswheel_seat"];
int r = 130; //radius of the body for the ferris wheel
for (int i = 0; i < 16; i++) {
//creates a seat and places it on the body
SKSpriteNode *seat = [[SKSpriteNode alloc] initWithTexture:seatTexture];
float x = body.position.x + r * cosf(2*M_PI*i/16);
float y = body.position.y + r * sinf(2*M_PI*i/16);
[seat setPosition:CGPointMake(x, y)];
[seat setZPosition:3];
[self addChild:seat];
//physics body stuff
seat.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:seat.size];
seat.physicsBody.affectedByGravity = NO;
SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:body.physicsBody bodyB:seat.physicsBody anchor:seat.position];
[self.physicsWorld addJoint:pin];
}
}

Issue with mapKit and annotations

I have an app here which uses MapKit, and I draw some overlays and annotations to it.
They do show up, but I have to move the map first or zoom in / out a bit before they are drawn.
Is there any fix to it?
Just call it for map zoom and fit annotations:-
[self zoomToFitMapAnnotations:self.mapView];
(void)zoomToFitMapAnnotations:(MKMapView*)aMapView {
if([aMapView.annotations count] == 0)
return;
CLLocationCoordinate2D topLeftCoord;
topLeftCoord.latitude = -90;
topLeftCoord.longitude = 180;
CLLocationCoordinate2D bottomRightCoord;
bottomRightCoord.latitude = 90;
bottomRightCoord.longitude = -180;
for(PlaceMark *annotation in aMapView.annotations) {
topLeftCoord.longitude = fmin(topLeftCoord.longitude, annotation.coordinate.longitude);
topLeftCoord.latitude = fmax(topLeftCoord.latitude, annotation.coordinate.latitude);
bottomRightCoord.longitude = fmax(bottomRightCoord.longitude, annotation.coordinate.longitude);
bottomRightCoord.latitude = fmin(bottomRightCoord.latitude, annotation.coordinate.latitude);
}
MKCoordinateRegion region;
region.center.latitude = topLeftCoord.latitude - (topLeftCoord.latitude - bottomRightCoord.latitude) * 0.5;
region.center.longitude = topLeftCoord.longitude + (bottomRightCoord.longitude - topLeftCoord.longitude) * 0.5;
region.span.latitudeDelta = fabs(topLeftCoord.latitude - bottomRightCoord.latitude) * 1.1; // Add a little extra space on the sides
region.span.longitudeDelta = fabs(bottomRightCoord.longitude - topLeftCoord.longitude) * 1.1; // Add a little extra space on the sides
region = [aMapView regionThatFits:region];
[aMapView setRegion:region animated:YES];
}

SKPhysicsJoint is joining at what appears to be incorrect coordinates

In my simple archery game, I have defined an arrow node and a target node with associated physics bodies.
When I attempt to join them together using an SKPhysicsJointFixed (I have also tried other types), the behaviour is inaccurate with the joint seemingly created at random points before actually hitting the target node.
I have played with friction and restitution values and also SKShapeNode (with a CGPath) and SKSpriteNode (with a rectangle around the sprite) to define the target but the problem occurs with both.
When just using collisions, the arrows bounce off the correct locations of the target, which seems OK. It is only when the join occurs that the results become random on-screen, usually 10-20 points away from the target node "surface".
static const uint32_t arrowCategory = 0x1 << 1;
static const uint32_t targetCategory = 0x1 << 2;
- (SKSpriteNode *)createArrowNode
{
SKSpriteNode *arrow = [[SKSpriteNode alloc] initWithImageNamed:#"Arrow.png"];
arrow.position = CGPointMake(165, 110);
arrow.name = #"arrowNode";
arrow.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:arrow.frame.size];
arrow.physicsBody.angularVelocity = -0.25;
arrow.physicsBody.usesPreciseCollisionDetection = YES;
arrow.physicsBody.restitution = 0.0;
arrow.physicsBody.friction = 0.0;
arrow.physicsBody.categoryBitMask = arrowCategory;
arrow.physicsBody.collisionBitMask = targetCategory;
arrow.physicsBody.contactTestBitMask = /*arrowCategory | */targetCategory | bullseyeCategory;
return arrow;
}
-void(createTargetNode)
{
SKSpriteNode *sprite = [[SKSpriteNode alloc] initWithImageNamed:#"TargetOutline.png"];
sprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:sprite.size];
sprite.position = CGPointMake(610, 100);
sprite.name = #"targetNode";
sprite.physicsBody.usesPreciseCollisionDetection = NO;
// sprite.physicsBody.affectedByGravity = NO;
// sprite.physicsBody.mass = 20000;
sprite.physicsBody.dynamic = NO;
sprite.physicsBody.friction = 0.0;
sprite.physicsBody.restitution = 0.0;
sprite.physicsBody.categoryBitMask = targetCategory;
sprite.physicsBody.contactTestBitMask = targetCategory | arrowCategory;
[self addChild:sprite];
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ((firstBody.categoryBitMask & arrowCategory) != 0 &&
(secondBody.categoryBitMask & targetCategory) != 0)
{
CGPoint contactPoint = contact.contactPoint;
NSLog(#"contactPoint is %#", NSStringFromCGPoint(contactPoint));
float contact_x = contactPoint.x;
float contact_y = contactPoint.y;
SKPhysicsJoint *joint = [SKPhysicsJointFixed jointWithBodyA:firstBody bodyB:secondBody anchor:(CGPoint)contactPoint ];
[self.physicsWorld addJoint:joint];
CGPoint bullseye = CGPointMake(590, 102.5);
NSLog(#"Center is %#", NSStringFromCGPoint(bullseye));
CGFloat distance = SDistanceBetweenPoints(contactPoint, bullseye);
NSLog(#"Distance to bullseye is %f", distance);
}

Xcode: change row for looping buttons

using the code down below to loop out 200 buttons and wnat the row go down a notch when the row is full.
Im guessing there must be a better way because my way dosent work.
When the second and third row begins i onl have one button. No errors just buttons on each other on the last rows.
-(void)viewDidLoad {
int numba=0;
int x=-20;
int y=20;
for(int i = 1; i <= 200; ++i) {
numba ++;
if (numba <16) {
x =x+20;
} else if (numba >16 && numba <26){
x=-20;
x = x + 20;
y=40;
} else if (numba >26 && numba <36){
x=-20;
x =x+20;
y=60;
} else {
x=-20;
x =x+20;
y=80;
}
UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
btn.frame = CGRectMake(x, y, 20, 20);
NSLog(#"numba = %d",numba);
NSLog(#"x = %d",x);
btn.tag = numba;
[btn setTitle:[NSString stringWithFormat: #"%d", numba] forState:UIControlStateNormal];
[self.view addSubview:btn];
}
}
When you want to create a 2-dimensional grid, it's best to just use nested loops instead of trying to be clever with a single loop.
Don't sprinkle constant numbers all over your code. You can define symbolic constants in a method or function.
Here's how I'd do it:
- (void)viewDidLoad {
static const CGFloat ButtonWidth = 20;
static const CGFloat ButtonHeight = 20;
static const CGFloat RowWidth = 320;
int buttonNumber = 0;
for (CGFloat y = 0; buttonNumber < 200; y += ButtonHeight) {
for (CGFloat x = 0; buttonNumber < 200 && x + ButtonWidth <= RowWidth; x += ButtonWidth) {
++buttonNumber;
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(x, y, ButtonWidth, ButtonHeight);
button.tag = buttonNumber;
[button setTtle:[NSString stringWithFormat:#"%d", buttonNumber] forState:UIControlStateNormal];
[self.view addSubview:button];
}
}
}

UIImagePickerController returning incorrect image orientation

I'm using UIImagePickerController to capture an image and then store it. However, when i try to rescale it, the orientation value i get out of this image is incorrect. When i take a snap by holding the phone Up, it gives me orientation of Left. Has anyone experienced this issue?
The UIImagePickerController dictionary shows following information:
{
UIImagePickerControllerMediaMetadata = {
DPIHeight = 72;
DPIWidth = 72;
Orientation = 3;
"{Exif}" = {
ApertureValue = "2.970853654340484";
ColorSpace = 1;
DateTimeDigitized = "2011:02:14 10:26:17";
DateTimeOriginal = "2011:02:14 10:26:17";
ExposureMode = 0;
ExposureProgram = 2;
ExposureTime = "0.06666666666666667";
FNumber = "2.8";
Flash = 32;
FocalLength = "3.85";
ISOSpeedRatings = (
125
);
MeteringMode = 1;
PixelXDimension = 2048;
PixelYDimension = 1536;
SceneType = 1;
SensingMethod = 2;
Sharpness = 1;
ShutterSpeedValue = "3.910431673351467";
SubjectArea = (
1023,
767,
614,
614
);
WhiteBalance = 0;
};
"{TIFF}" = {
DateTime = "2011:02:14 10:26:17";
Make = Apple;
Model = "iPhone 3GS";
Software = "4.2.1";
XResolution = 72;
YResolution = 72;
};
};
UIImagePickerControllerMediaType = "public.image";
UIImagePickerControllerOriginalImage = "<UIImage: 0x40efb50>";
}
However picture returns imageOrientation == 1;
UIImage *picture = [info objectForKey:UIImagePickerControllerOriginalImage];
I just started working on this issue in my own app.
I used the UIImage category that Trevor Harmon crafted for resizing an image and fixing its orientation, UIImage+Resize.
Then you can do something like this in -imagePickerController:didFinishPickingMediaWithInfo:
UIImage *pickedImage = [info objectForKey:UIImagePickerControllerEditedImage];
UIImage *resized = [pickedImage resizedImageWithContentMode:UIViewContentModeScaleAspectFit bounds:pickedImage.size interpolationQuality:kCGInterpolationHigh];
This fixed the problem for me. The resized image is oriented correctly visually and the imageOrientation property reports UIImageOrientationUp.
There are several versions of this scale/resize/crop code out there; I used Trevor's because it seems pretty clean and includes some other UIImage manipulators that I want to use later.
This what I have found for fixing orientation issue; Works for me
UIImage *initialImage = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
NSData *data = UIImagePNGRepresentation(self.initialImage);
UIImage *tempImage = [UIImage imageWithData:data];
UIImage *fixedOrientationImage = [UIImage imageWithCGImage:tempImage.CGImage
scale:initialImage.scale
orientation:self.initialImage.imageOrientation];
initialImage = fixedOrientationImage;
Here's a Swift snippet that fixes the problem efficiently:
let orientedImage = UIImage(CGImage: initialImage.CGImage, scale: 1, orientation: initialImage.imageOrientation)!
I use the following code that I have put in a separate image utility object file that has a bunch of other editing methods for UIImages:
+ (UIImage*)imageWithImage:(UIImage*)sourceImage scaledToSizeWithSameAspectRatio:(CGSize)targetSize
{
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO) {
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor > heightFactor) {
scaleFactor = widthFactor; // scale to fit height
}
else {
scaleFactor = heightFactor; // scale to fit width
}
scaledWidth = width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor) {
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
}
else if (widthFactor < heightFactor) {
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
CGImageRef imageRef = [sourceImage CGImage];
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGColorSpaceRef colorSpaceInfo = CGImageGetColorSpace(imageRef);
if (bitmapInfo == kCGImageAlphaNone) {
bitmapInfo = kCGImageAlphaNoneSkipLast;
}
CGContextRef bitmap;
if (sourceImage.imageOrientation == UIImageOrientationUp || sourceImage.imageOrientation == UIImageOrientationDown) {
bitmap = CGBitmapContextCreate(NULL, targetWidth, targetHeight, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo);
} else {
bitmap = CGBitmapContextCreate(NULL, targetHeight, targetWidth, CGImageGetBitsPerComponent(imageRef), CGImageGetBytesPerRow(imageRef), colorSpaceInfo, bitmapInfo);
}
// In the right or left cases, we need to switch scaledWidth and scaledHeight,
// and also the thumbnail point
if (sourceImage.imageOrientation == UIImageOrientationLeft) {
thumbnailPoint = CGPointMake(thumbnailPoint.y, thumbnailPoint.x);
CGFloat oldScaledWidth = scaledWidth;
scaledWidth = scaledHeight;
scaledHeight = oldScaledWidth;
CGContextRotateCTM (bitmap, M_PI_2); // + 90 degrees
CGContextTranslateCTM (bitmap, 0, -targetHeight);
} else if (sourceImage.imageOrientation == UIImageOrientationRight) {
thumbnailPoint = CGPointMake(thumbnailPoint.y, thumbnailPoint.x);
CGFloat oldScaledWidth = scaledWidth;
scaledWidth = scaledHeight;
scaledHeight = oldScaledWidth;
CGContextRotateCTM (bitmap, -M_PI_2); // - 90 degrees
CGContextTranslateCTM (bitmap, -targetWidth, 0);
} else if (sourceImage.imageOrientation == UIImageOrientationUp) {
// NOTHING
} else if (sourceImage.imageOrientation == UIImageOrientationDown) {
CGContextTranslateCTM (bitmap, targetWidth, targetHeight);
CGContextRotateCTM (bitmap, -M_PI); // - 180 degrees
}
CGContextDrawImage(bitmap, CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledWidth, scaledHeight), imageRef);
CGImageRef ref = CGBitmapContextCreateImage(bitmap);
UIImage* newImage = [UIImage imageWithCGImage:ref];
CGContextRelease(bitmap);
CGImageRelease(ref);
return newImage;
}
And then I call
UIImage *pickedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
UIImage *fixedOriginal = [ImageUtil imageWithImage:[mediaInfoDict objectForKey:UIImagePickerControllerOriginalImage] scaledToSizeWithSameAspectRatio:pickedImage.size];
In iOS 7, I needed code dependent on UIImage.imageOrientation to correct for the different orientations. Now, in iOS 8.2, when I pick my old test images from the album via UIImagePickerController, the orientation will be UIImageOrientationUp for ALL images. When I take a photo (UIImagePickerControllerSourceTypeCamera), those images will also always be upwards, regardless of the device orientation.
So between those iOS versions, there obviously has been a fix where UIImagePickerController already rotates the images if neccessary.
You can even notice that when the album images are displayed: for a split second, they will be displayed in the original orientation, before they appear in the new upward orientation.
The only thing that worked for me was to re-render the image again which forces the correct orientation.
if (photo.imageOrientation != .up) {
UIGraphicsBeginImageContextWithOptions(photo.size, false, 1.0);
let rect = CGRect(x: 0, y: 0, width: photo.size.width, height: photo.size.height);
photo.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
photo = newImage;
}

Resources