How to fix SafeAreaLayoutGuide - ios

I am making a game with canvas. The game is clipped because of the top notch, I've tried SafeAreaLayoutGuide but nothing happened. Please look at the code below and let me know what I am doing wrong.
-(void) createGLView {
//create our openglview and size it correctly
OpenGLView *glView = [[OpenGLView alloc] initWithFrame:self.appDelegate.initFrame];
self.view = glView;
self.appDelegate.canvas = glView;
core_init_gl(1);
glView.backgroundColor = [UIColor redColor];
glView.translatesAutoresizingMaskIntoConstraints = NO;
[glView.leadingAnchor constraintEqualToAnchor:glView.safeAreaLayoutGuide.leadingAnchor].active = YES;
[glView.trailingAnchor constraintEqualToAnchor:glView.safeAreaLayoutGuide.trailingAnchor].active = YES;
[glView.topAnchor constraintEqualToAnchor:glView.safeAreaLayoutGuide.topAnchor].active = YES;
[glView.bottomAnchor constraintEqualToAnchor:glView.safeAreaLayoutGuide.bottomAnchor].active = YES;
int w = self.appDelegate.screenWidthPixels;
int h = self.appDelegate.screenHeightPixels;
tealeaf_canvas_resize(w, h);
NSLOG(#"{tealeaf} Created GLView (%d, %d)", w, h);
}
The red color goes inside the top notch. I mean full screen. How to solve this?

You need to have a fullscreen parent view. Then you can add the OpenGLView as subview and connect its constraints to the parent view's safeAreaLayoutGuide.
- (void)createGLView {
OpenGLView *glView = [[OpenGLView alloc] initWithFrame:self.appDelegate.initFrame];
[self.view addSubview:glView];
self.appDelegate.canvas = glView;
core_init_gl(1);
glView.backgroundColor = [UIColor redColor];
glView.translatesAutoresizingMaskIntoConstraints = NO;
[glView.leadingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.leadingAnchor].active = YES;
[glView.trailingAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.trailingAnchor].active = YES;
[glView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = YES;
[glView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor].active = YES;
int w = self.appDelegate.screenWidthPixels;
int h = self.appDelegate.screenHeightPixels;
tealeaf_canvas_resize(w, h);
NSLOG(#"{tealeaf} Created GLView (%d, %d)", w, h);
}

Related

How do I add both shadow and rounded corners to a UIVisualEffectView?

I'm using a container for elements which I'd like for it to be blurred. In order to add rounded corners I modified the layer while for the shadow I created a second view named containerShadow and placed it below it.
It works, but not flawlessly. The shadow darkens the effect of the blur. Is there a way to perfect it?
.h
#property (strong) UIVisualEffectView *containerView;
#property (strong) UIView *containerShadowView;
.m
- (instancetype)init {
if (self = [super init]) {
self.containerShadowView = [[UIView alloc] init];
self.containerShadowView.layer.masksToBounds = NO;
self.containerShadowView.layer.shadowRadius = 80.0;
self.containerShadowView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.containerShadowView.layer.shadowOffset = CGSizeZero;
self.containerShadowView.layer.shadowOpacity = 0.25;
[self addSubview:self.containerShadowView];
self.containerView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]];
self.containerView.clipsToBounds = YES;
self.containerView.layer.cornerRadius = 20.0;
[self addSubview:self.containerView];
}
return self;
}
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
// Random container frame for testing...
self.containerView.frame =
CGRectMake(20.0,
200.0,
380,
480);
self.containerShadowView.frame = self.containerView.frame;
self.containerShadowView.layer.shadowPath =
[[UIBezierPath bezierPathWithRoundedRect:self.containerShadowView.bounds cornerRadius:self.containerView.layer.cornerRadius] CGPath];
}
You can do this by masking your containerShadowView with a "cutout" that matches the containerView effect view.
So, this is how it looks - I centered the 380x480 view, and used 0.9 for the .shadowOpacity to emphasize the differences.
Your original on the left, masked version on the right:
Kinda difficult to tell what's really going on, since that could be an opaque layer, we'll add a label behind it:
and, to clarify what we're doing, let's look at it with the containerView effect view hidden:
Here's the source code I used for that - each tap anywhere will cycle through the 8 different layouts:
#import <UIKit/UIKit.h>
#interface OrigShadowView : UIView
#property (strong) UIVisualEffectView *containerView;
#property (strong) UIView *containerShadowView;
#end
#implementation OrigShadowView
- (instancetype)init {
if (self = [super init]) {
self.containerShadowView = [[UIView alloc] init];
self.containerShadowView.layer.masksToBounds = NO;
self.containerShadowView.layer.shadowRadius = 80.0;
self.containerShadowView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.containerShadowView.layer.shadowOffset = CGSizeZero;
self.containerShadowView.layer.shadowOpacity = 0.9;
[self addSubview:self.containerShadowView];
self.containerView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]];
self.containerView.clipsToBounds = YES;
self.containerView.layer.cornerRadius = 20.0;
[self addSubview:self.containerView];
}
return self;
}
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
// let's center a 380 x 480 rectangle in self
CGFloat w = 380.0;
CGFloat h = 480.0;
CGRect vRect = CGRectMake((frame.size.width - w) * 0.5, (frame.size.height - h) * 0.5, w, h);
self.containerView.frame = vRect;
self.containerShadowView.frame = self.containerView.frame;
// change origin to 0,0 because the following will be relative to the subviews
vRect.origin = CGPointZero;
self.containerShadowView.layer.shadowPath =
[[UIBezierPath bezierPathWithRoundedRect:vRect cornerRadius:self.containerView.layer.cornerRadius] CGPath];
}
#end
#interface MaskShadowView : UIView
#property (strong) UIVisualEffectView *containerView;
#property (strong) UIView *containerShadowView;
#end
#implementation MaskShadowView
- (instancetype)init {
if (self = [super init]) {
self.containerShadowView = [[UIView alloc] init];
self.containerShadowView.layer.masksToBounds = NO;
self.containerShadowView.layer.shadowRadius = 80.0;
self.containerShadowView.layer.shadowColor = [[UIColor blackColor] CGColor];
self.containerShadowView.layer.shadowOffset = CGSizeZero;
self.containerShadowView.layer.shadowOpacity = 0.9;
[self addSubview:self.containerShadowView];
self.containerView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]];
self.containerView.clipsToBounds = YES;
self.containerView.layer.cornerRadius = 20.0;
[self addSubview:self.containerView];
}
return self;
}
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
// let's center a 380 x 480 rectangle in self
CGFloat w = 380.0;
CGFloat h = 480.0;
CGRect vRect = CGRectMake((frame.size.width - w) * 0.5, (frame.size.height - h) * 0.5, w, h);
self.containerView.frame = vRect;
self.containerShadowView.frame = self.containerView.frame;
// change origin to 0,0 because the following will be relative to the subviews
vRect.origin = CGPointZero;
self.containerShadowView.layer.shadowPath =
[[UIBezierPath bezierPathWithRoundedRect:vRect cornerRadius:self.containerView.layer.cornerRadius] CGPath];
UIBezierPath *bigBez;
UIBezierPath *clipBez;
// we need a rectangle that will encompass the shadow radius
// double the shadowRadius is probably sufficient, but since it won't be seen
// and won't affect anything else, we'll make it 4x
CGRect expandedRect = CGRectInset(vRect, -self.containerShadowView.layer.shadowRadius * 4.0, -self.containerShadowView.layer.shadowRadius * 4.0);
bigBez = [UIBezierPath bezierPathWithRect:expandedRect];
// we want to "clip out" a rounded rect in the center
// which will be the same size as the visual effect view
clipBez = [UIBezierPath bezierPathWithRoundedRect:vRect cornerRadius:self.containerView.layer.cornerRadius];
[bigBez appendPath:clipBez];
bigBez.usesEvenOddFillRule = YES;
CAShapeLayer *maskLayer = [CAShapeLayer new];
maskLayer.fillRule = kCAFillRuleEvenOdd;
maskLayer.fillColor = UIColor.whiteColor.CGColor;
maskLayer.path = bigBez.CGPath;
self.containerShadowView.layer.mask = maskLayer;
}
#end
#interface BlurTestViewController : UIViewController
{
OrigShadowView *origView;
MaskShadowView *newView;
UILabel *bkgLabel;
// so we can step through on taps to see the results
NSInteger step;
UILabel *infoLabel;
}
#end
#implementation BlurTestViewController
- (void)viewDidLoad {
[super viewDidLoad];
bkgLabel = [UILabel new];
bkgLabel.textColor = UIColor.blueColor;
bkgLabel.font = [UIFont systemFontOfSize:48.0 weight:UIFontWeightBlack];
bkgLabel.textAlignment = NSTextAlignmentCenter;
bkgLabel.numberOfLines = 0;
bkgLabel.text = #"A label can contain an arbitrary amount of text, but UILabel may shrink, wrap, or truncate the text, depending on the size of the bounding rectangle and properties you set. You can control the font, text color, alignment, highlighting, and shadowing of the text in the label.";
bkgLabel.text = #"I'm using a container for elements which I'd like for it to be blurred. In order to add rounded corners I modified the layer while for the shadow I created a second view named containerShadow and placed it below it.";
origView = [OrigShadowView new];
newView = [MaskShadowView new];
[self.view addSubview:bkgLabel];
[self.view addSubview:origView];
[self.view addSubview:newView];
infoLabel = [UILabel new];
infoLabel.font = [UIFont systemFontOfSize:20.0 weight:UIFontWeightBold];
infoLabel.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:infoLabel];
step = 0;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// let's inset the "shadow blur" views 40-points
CGRect r = CGRectInset(self.view.frame, 40.0, 40.0);
origView.frame = r;
newView.frame = r;
// let's put the background label midway down the screen
r.origin.y += r.size.height * 0.5;
r.size.height *= 0.5;
bkgLabel.frame = r;
// put the info label near the top
infoLabel.frame = CGRectMake(40.0, 80.0, r.size.width, 40.0);
[self nextStep];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self nextStep];
}
- (void)nextStep {
bkgLabel.hidden = YES;
origView.hidden = YES;
newView.hidden = YES;
origView.containerView.hidden = NO;
newView.containerView.hidden = NO;
step++;
switch (step) {
case 1:
origView.hidden = NO;
infoLabel.text = #"1: Original View";
break;
case 2:
newView.hidden = NO;
infoLabel.text = #"2: Masked View";
break;
case 3:
bkgLabel.hidden = NO;
origView.hidden = NO;
infoLabel.text = #"3: Original View";
break;
case 4:
bkgLabel.hidden = NO;
newView.hidden = NO;
infoLabel.text = #"4: Masked View";
break;
case 5:
origView.hidden = NO;
origView.containerView.hidden = YES;
infoLabel.text = #"5: Original View - effect view hidden";
break;
case 6:
newView.hidden = NO;
newView.containerView.hidden = YES;
infoLabel.text = #"6: Masked View - effect view hidden";
break;
case 7:
bkgLabel.hidden = NO;
origView.hidden = NO;
origView.containerView.hidden = YES;
infoLabel.text = #"7: Original View - effect view hidden";
break;
default:
bkgLabel.hidden = NO;
newView.hidden = NO;
newView.containerView.hidden = YES;
infoLabel.text = #"8: Masked View - effect view hidden";
step = 0;
break;
}
}
#end

iOS Layer animation with auto layout

I have a simple container view(green) and two sub views(red and blue) as below.
The container view is not applying auto layout and I config its size & location by frame.
While the sub views are applying auto layout(see code below)
#implementation XXView {
UIView *_leftView;
UIView *_rightView;
}
- (instancetype)init {
self = [super initWithFrame:CGRectZero];
if (self) {
[self setupViewHierarchy];
[self setupConstraints];
}
return self;
}
- (void)setupViewHierarchy {
_leftView = [[UIView alloc] init];
_leftView.backgroundColor = UIColor.redColor;
_leftView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_leftView];
_rightView = [[UIView alloc] init];
_rightView.backgroundColor = UIColor.blueColor;
_rightView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_rightView];
self.backgroundColor = UIColor.greenColor;
}
- (void)setupConstraints {
[NSLayoutConstraint activateConstraints:#[
[_leftView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:10],
[_leftView.topAnchor constraintEqualToAnchor:self.topAnchor],
[_leftView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
[_leftView.widthAnchor constraintEqualToConstant:50],
[_rightView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-10],
[_rightView.topAnchor constraintEqualToAnchor:_leftView.topAnchor],
[_rightView.bottomAnchor constraintEqualToAnchor:_leftView.bottomAnchor],
[_rightView.widthAnchor constraintEqualToAnchor:_leftView.widthAnchor],
]];
}
...
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
XXView *tv = [[XXView alloc] init];
CGFloat width = self.view.bounds.size.width;
tv.frame = CGRectMake(50, 400, width-100, 100);
[self.view addSubview:tv];
self.tv = tv;
}
Then I would like to animate the container's width change by using the CABasicAnimation as below:
- (void)startAnimation {
[CATransaction begin];
CGFloat width = self.bounds.size.width;
CABasicAnimation *widthAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size.width"];
widthAnimation.fromValue = #(width/2);
widthAnimation.toValue = #(width);
widthAnimation.duration = 1;
[self.layer addAnimation:widthAnimation forKey:#"123"];
[CATransaction commit];
}
However, the animation is not as I would expect. I would expect the left view moves as the container's leading side and the right view does as the trailing side.
What I see is, the green view expands as expected and the left view moves as green view's leading side. However, the right view is always keeping the same distance to left view. Below is the screenshot taken at the beginning of the animation.
Why the animation is not working as expected?
The problem is that your CABasicAnimation is modifying the bounds of the layer ... but as far as auto-layout is concerned that does not change the width of the view.
It's not really clear what your goal is here, but if you change your animation method to this it might get you on your way:
- (void)startAnimation {
CGRect f = self.frame;
CGFloat fw = f.size.width;
f.size.width = fw / 2;
self.frame = f;
[self layoutIfNeeded];
f.size.width = fw;
[UIView animateWithDuration:1.0 animations:^{
self.frame = f;
[self layoutIfNeeded];
}];
}

How to create view based animation in objective c?

I have to add the following animation in my iOS app, I have used a scroll bar along with UITableView and achieved the top and bottom animation, but I'm still stuck at the middle animation part where the 4 UIViews come in a single horizontal line. Any suggestions?
http://www.image-maps.com/m/private/0/af8u4ulika9siddnf6k6hhrtg2_untitled-2.gif
Code:-
#implementation AnimatedView {
UIScrollView *mainScroll;
UIScrollView *backgroundScrollView;
UILabel *_textLabel;
UITableView *_commentsTableView;
UIView *menuView;
UIView *_commentsViewContainer;
UIView *fadeView;
UIImageView *imageView;
NSMutableArray *comments;
}
- (id)init {
self = [super init];
if (self) {
_mainScrollView = [[UIScrollView alloc] initWithFrame:[UIApplication sharedApplication].keyWindow.frame];
self.view = _mainScrollView;
_backgroundScrollView = [[UIScrollView alloc] initWithFrame:HEADER_INIT_FRAME];
imageView = [[UIImageView alloc] initWithFrame:HEADER_INIT_FRAME];
fadeView = [[UIView alloc] initWithFrame:imageView.frame];
_textLabel = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 100.0f, 150.0f, 25.0f)];
menuView = [[UIView alloc] initWithFrame:CGRectMake(0,_textLabel.frame.size.height+150, self.view.frame.size.width+30, 180)];
[_backgroundScrollView addSubview:imageView];
[_backgroundScrollView addSubview:fadeView];
[_backgroundScrollView addSubview:menuView];
[_backgroundScrollView addSubview:_textLabel];
_commentsViewContainer = [[UIView alloc] init];
_commentsTableView = [[UITableView alloc] init];
_commentsTableView.scrollEnabled = NO;
_commentsTableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
[self.view addSubview:_backgroundScrollView];
[_commentsViewContainer addSubview:_commentsTableView];
[self.view addSubview:_commentsViewContainer];
// fake data!
comments = [#[#"Array for tableview"] mutableCopy];
}
return self;
}
#pragma mark Scroll
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat delta = 0.0f;
CGRect rect = HEADER_INIT_FRAME;
// Here is where I do the "Zooming" image and the quick fade out the text and toolbar
if (scrollView.contentOffset.y < 0.0f) {
delta = fabs(MIN(0.0f, _mainScrollView.contentOffset.y));
_backgroundScrollView.frame = CGRectMake(CGRectGetMinX(rect) - delta / 2.0f, CGRectGetMinY(rect) - delta, CGRectGetWidth(rect) + delta, CGRectGetHeight(rect) + delta);
[_commentsTableView setContentOffset:(CGPoint){0,0} animated:NO];
} else {
delta = _mainScrollView.contentOffset.y;
_textLabel.alpha = 1.0f;
CGFloat backgroundScrollViewLimit = _backgroundScrollView.frame.size.height - kBarHeight;
// Here I check whether or not the user has scrolled passed the limit where I want to stick the header, if they have then I move the frame with the scroll view
// to give it the sticky header look
if (delta > backgroundScrollViewLimit) {
_backgroundScrollView.frame = (CGRect) {.origin = {0, delta - _backgroundScrollView.frame.size.height + kBarHeight}, .size = {self.view.frame.size.width, HEADER_HEIGHT}};
_commentsViewContainer.frame = (CGRect){.origin = {0, CGRectGetMinY(_backgroundScrollView.frame) + CGRectGetHeight(_backgroundScrollView.frame)}, .size = _commentsViewContainer.frame.size };
_commentsTableView.contentOffset = CGPointMake (0, delta - backgroundScrollViewLimit);
CGFloat contentOffsetY = -backgroundScrollViewLimit * kBackgroundParallexFactor;
[_backgroundScrollView setContentOffset:(CGPoint){0,contentOffsetY} animated:NO];
}
else {
_backgroundScrollView.frame = rect;
_commentsViewContainer.frame = (CGRect){.origin = {0, CGRectGetMinY(rect) + CGRectGetHeight(rect)}, .size = _commentsViewContainer.frame.size };
[_commentsTableView setContentOffset:(CGPoint){0,0} animated:NO];
[_backgroundScrollView setContentOffset:CGPointMake(0, -delta * kBackgroundParallexFactor)animated:NO];
}
}
}
- (void)viewDidAppear:(BOOL)animated {
_mainScrollView.contentSize = CGSizeMake(CGRectGetWidth(self.view.frame), _commentsTableView.contentSize.height + CGRectGetHeight(_backgroundScrollView.frame));
}
You don't need any scrollview to implement this really. All you need is 1 UITableView with 2 sections. First section has a single empty element (but set the row height to 0), and enable the header for both sections. You can use UIViews for the headerViews. Then, you only need to change the header height (with icon positioning) based on tableview Delegate scrollViewDidScroll. scrollViewDidScroll is also a delegate of UITableView since one of TableView's element inherits from UIScrollView.

updating custom uiview's frame after adding subviews to it

I am having a big mental block figuring out something I think easy. Please see this very short video: http://screencast.com/t/eaW7rbECv4Ne.
I would like to draw a rectangle in my layoutSubviews method around the whole area that covers the subviews. I currently draw yellow rect around each term as you can see in the video.
I have a StatementView. In each key tap I am creating new objectView and adding it to my StatementView like below. I add the objectViews to a containerView and try to get the origin and size of the containerView in order to draw the stroke around it. However, it always gives me 0.
How should I update containerViews bounds values after I add the subviews to it?
// Created by ilteris on 1/31/15.
// Copyright (c) 2015 ilteris. All rights reserved.
#implementation QWZStatementView
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = NO;
self.textField = [[MyUITextField alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
self.textField.inputView = [LNNumberpad defaultLNNumberpad];
self.textField.delegate = self;
self.isNumerator = NO;
self.isDenominator = NO;
[self addSubview:self.textField];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(statementOneTextFieldChanged:) name:UITextFieldTextDidChangeNotification object:nil];
self.containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
[self addSubview:self.containerView];
self.autoresizesSubviews = YES;
self.objectsArray = [NSMutableArray arrayWithCapacity:1];
self.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
}
return self;
}
-(void)statementOneTextFieldChanged:(NSNotification *)notification {
NSLog(#"statementOneTextFieldChanged");
QWZObjectView *objectView = [[QWZObjectView alloc] initWithFrame:self.bounds];
[self.containerView addSubview:objectView];
QWZTerm* term = [QWZQuestionDetailViewController createAndReturnCoreDataTermForJSONDict:objectData];
[objectView createTerm:term];
[self.objectsArray addObject:objectView];
[self setNeedsLayout];
}
- (void)layoutSubviews {
NSLog(#"layoutSubviews StatementView");
[super layoutSubviews];
CGSize totalSize = CGSizeMake(0, 0);
for (QWZObjectView* objectView in self.objectsArray) {
CGSize textSize = objectView.bounds.size;
objectView.frame = CGRectMake(totalSize.width , totalSize.height, textSize.width , textSize.height);
totalSize.width = textSize.width + totalSize.width;
}
CGRect bounds = self.containerView.bounds;
/*
NSLog(#"self.containerView.bounds is %#", NSStringFromCGRect(self.containerView.bounds)); //always 0.
bounds.origin = CGPointMake(totalSize.width/2, self.containerView.bounds.origin.y); //adding this code didn't help at all.
CGFloat borderWidth = 2.0f;
self.bounds = CGRectInset(self.bounds, -borderWidth, -borderWidth);
self.layer.borderColor = [UIColor redColor].CGColor;
self.layer.borderWidth = borderWidth;
*/
bounds.origin = CGPointMake(totalSize.width/2, self.containerView.bounds.origin.y);
self.containerView.bounds = bounds;
}
#end

CATiledLayer, iOS7 tiles not updating

In upgrading from iOS6 to iOS7, I think I've found a bug with CATiledLayer, and I'd like to have it verified by the community before submitting to Apple.
The issue is that if you have a UIScrollView with many CATiledLayers within it, the tiles will eventually stop updating.
I have a sample project demonstrating the issue here:
https://github.com/sbudhram/CATiledLayerBug
Please download and run on an iPad running iOS6 vs iOS7.
This project generates 900 CATiledLayers within a UIScrollView, at 3 levels of resolution. As the user zooms in, the tiles update to more refined resolution. The code works on iOS6, but tiles eventually stop updating on iOS7.
I've googled around to see if anyone's had similar problems to this, and found this:
http://www.cocoanetics.com/2013/09/welcome-to-ios-7-issues/
This is different though, because I believe this can happen without a memory warning.
Here is the relevant piece of code in UIViewController:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_scrollView.backgroundColor = [UIColor yellowColor];
_scrollView.contentSize = CGSizeMake(self.view.frame.size.width, self.view.frame.size.height);
_scrollView.minimumZoomScale = 1;
_scrollView.maximumZoomScale = 4.1;
_scrollView.zoomScale = 2;
_scrollView.showsHorizontalScrollIndicator = YES;
_scrollView.showsVerticalScrollIndicator = YES;
_scrollView.delegate = self;
[self.view addSubview:_scrollView];
self.contentView = [[UIView alloc] initWithFrame:_scrollView.bounds];
_contentView.backgroundColor = [UIColor lightGrayColor];
[_scrollView addSubview:_contentView];
CGFloat tileSize = 20.0f;
CGFloat tileSpacing = 4.0f;
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 30; j++) {
CATiledLayer *tLayer = [CATiledLayer layer];
tLayer.bounds = CGRectMake(0, 0, tileSize, tileSize);
tLayer.position = CGPointMake(tileSize/2 + i*(tileSpacing+tileSize), tileSize/2 + j*(tileSpacing+tileSize));
tLayer.delegate = self;
tLayer.contentsGravity = kCAGravityResize;
tLayer.contentsScale = [[UIScreen mainScreen] scale];
tLayer.masksToBounds = NO;
tLayer.opacity = 1.0f;
tLayer.backgroundColor = [UIColor colorWithRed:.2 green:.2 blue:.8 alpha:.5].CGColor;
tLayer.levelsOfDetail = 3;
tLayer.levelsOfDetailBias = 3;
tLayer.tileSize = CGSizeMake(1024., 1024.);
[_contentView.layer addSublayer:tLayer];
}
}
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
NSLog(#"Zoom: %f", scrollView.zoomScale);
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _contentView;
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
UIImage *drawImage = nil;
if (_scrollView.zoomScale < 2) {
drawImage = [UIImage imageNamed:#"low.png"];
NSLog(#"Drawing - Low");
}
else if (_scrollView.zoomScale < 4) {
drawImage = [UIImage imageNamed:#"med.png"];
NSLog(#"Drawing - Med");
}
else {
drawImage = [UIImage imageNamed:#"high.png"];
NSLog(#"Drawing - Hi");
}
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -layer.bounds.size.height);
CGContextDrawImage(ctx, layer.bounds, [drawImage CGImage]);
}
Here's a snapshot of what happens on iOS7 (everything is filled in on iOS6):
As mentioned by gavi, this now works in 7.1.

Resources