How to fil rectangular ares in the screen with good performance in ios - ios

I'm making simple number coloring game (like sandbox, pixel art, unicorn, etc) and came across a problem. I tried various methods of filling rects in the screen.
At first i created a backgroundView(a simple UIView) which had 2500 subviews(also UIViews), each one had size = (CGSize){50,50}. Added a tap gesture recognizer, detected which view should be filled, and simply changed the background color of that view. But when i placed the backgroundView in the scrollView, the scroll and zooming were awful.
Tried same thing but this time each rectangle was a CALayer. So a backgroundView, which had 2500 sublayers(each was a CALayer with 50,50 size) - zoom and scroll was also awful.
Tired to use custom drawing with overriding drawRect method in UIView subclass or drawInContext in CALayer subclass(i was filling rects with CGContext), this time problem was also caused by the zooming and scrolling. Because i needed to update every rect when user zooms, it was triggering a lot of drawRect calls, and performance was also bad.
Any thoughts how can i fill rects in the screen ?

Explanation
Yup! Adding 2,500 tiny UIViews to a scroll view can destroy performance. I wouldn't use that approach, but if you do, at least make sure that subview_N.opaque = YES; on all subviews to disable compositing.
The CALayer approach that you described is basically the same as the UIView approach because views are backed by CoreAnimation layers (see -[UIView layer]).
The best options for drawing thousands of rectangles to the screen on iOS without decimating frame rate, is to use one of the following approaches:
Draw directly using CoreGraphics
Draw directly using OpenGL (extremely involved)
Use a layout engine such as UICollectionViewLayout
Now, you said you had tried overriding -[UIView drawRect:], but you didn't provide any example code for us to checkout. Please update your question with actual code for method #3 if you want more specific feedback. It's very likely that something is wrong with the drawRect code you created. CoreGraphics can definitely draw thousands of squares on screen without dragging frame rate down that badly.
One Solution
I recently released a project, YMTreeMap, that draws thousands of rectangles into a UIView to create financial TreeMaps. It's in Swift, not Objective-C, but the underlying concepts are the same. For this, I created a custom UICollectionViewLayout that lets Apple's well tested UICollectionView class handle the nitty gritty of selective drawing, zooming and animation.
The example UIViewController code in the YMTreeMap project shows how to draw thousands of colored rectangles to the screen if all you know is their location and size. This sounds like what you are doing. Since you're also scrolling and zooming, this solution might be perfect for you because UICollectionView has native support for both of those.

Related

Swift: make UIScrollView have a "going out of focus" effect

I'd like to know which library/object/function I should refer to to be able to make my scroll views have that nice out of focus effect. For example make the image coming on screen look smaller as it goes away and blur it at the same time.
My scroll view contains ImageViews that take up the entire bounds of the scroll view.
Blur
For iOS 8+, you can use UIVisualEffectView to achieve this effect. This is a native API that has been fine tuned for performance and great battery life, and it's easy to implement. Alternatively you can check out GPUImageGaussianBlurFilter as described here.
Scale
Check out transform property of the UIView. Using CGAffineTransformMakeScale you can change the scale. Read this very informative blog post about transformations.

How to clip image to UIView animating over it?

I've been looking around here and seeing lots of questions that seem related, but I haven't found anything that's exactly what I need.
I have three UIViews that are animating inside a custom viewController. I want there to be a static background inside the viewController that is only revealed within the clipping bounds of the UIViews.
It's kind of like spotlights on a prison yard: the background should stay in one place, but as the spotlights move you can see different parts of it. Except that I don't need circles, regular old UIView frames are fine.
For instance, it seems like what I need is in the answer to this question: How to achieve dynamic UIView masking?
...the answerer provides a link to a page that shows how to statically mask an image, and then the answerer says "But personally I think i would make 2 UIImage views and crop the content of the draggable UIView"--without any info on how to do that.
How do you do that?

iOS - UIImageView slight glitch

I have a myriad of UIImageViews that act as 'tiles' on a map. They align up next to one another to form a grid of images.
Ordinarily, the grid images are flush against each other, but whenever the iPhone or iPad device is rotated, or if a UIView is applied to the view that contains these tiles, the spaces between the UIImageViews become momentarily visible, showing off the grid pattern that I'd like to be invisible.
Any ideas what might be causing this?
Thanks
Consider using CATiledLayer to draw all the images in a single view instead of using multiple subviews. There's a nice writeup on using it at Cocoa Is My Girlfriend. CATiledLayer makes it easy to build an image out of smaller tiles, display higher resolution images as the user zooms in, and avoid memory problems that come from keeping too much of a large image loaded.
this is happening because you are using different views (here uiimageview). Since they are not part of the same fabric during any animation or change in view hierarchy there is a chance that the gap between them would be visible.
This is a very common problem in graphics programming. The way to fix this problem is to have one single view and add these uiimageviews as sub-layers to this view. That way all the imageviews are part of the same fabric and the gap would not be visible.
I did not post any code but then so did you ;)

Fade UIImageView as it approaches the edges of a UIScrollView

I have a UIScrollView over an image at the bottom of my app that acts as a dock with icons that can be scrolled through horizontally. Instead of the harsh edges of the UIScrollView, I would like the icons to fade out for a more aesthetically pleasing look. Being new to iOS development, I don't know if either of these would be valid options:
Create a faded image to use as an overlay on the scrollview so the
icons only appear through the visible portion.
Actually change the
alpha of the images based on their distance from the center (or from
each edge).
I suspect the first idea would be the most simple, but I'd like to throw this out there for any other ideas.
Note: I did see this tutorial, however that technique assumes that the background is a solid color. If I were to do it programatically, I would probably need to fade the individual images.
You can definitely implement something along the lines of #2. It'd be something similar to what the tutorial describes. The alpha transition however won't be as smooth as using the gradient layer mentioned in the tutorial or using an image since the entire icon would have the same alpha. How much discernible the difference is depends on the size of your icons. Smaller icons, very few will be able to tell the difference. Larger icons the difference would be quite clear.
You'd have to implement the
(void)scrollViewDidScroll:(UIScrollView *)scrollView
method in your scroll view's delegate class. This method will get called every time the scroll view changes the location of its content. In this method you can call its subviews and adjust their alphas as required. To optimize it a bit instead of calling the alpha adjustments on all the elements you can just update the subviews which are still partially/completely visible.
EDIT: to figure out which views to adjust you'll use the contentOffset property of the scrollView that gets passed as a parameter in the above method.

How to set a background pattern for a large UIScrollView (that scrolls with the view)

Seems like a very simple problem! But I'm having great difficulty.
My ideas and attempts so far:
scrollView.backgroundColor = [UIColor colorWithPatternImage:myImage]]
Doesn't scroll with contents
Create UIView the size of contentSize (actually, larger due to elastic bounces on scroll view), and use pattern as above on this sub view of UIScrollView.
Causes massive memory hit (and crash) when UIView gets too large. See also: Large UIScrollView with background pattern fails
Similar to (2), exept I only create a UIView the size of the maximum number of repetitions of the background image that will be seen, and cleverly move the view to wherever is needed to show the correct background repetitions.
Causes this background view to shift left and right unexpectedly when animating the scroll.
Create UIView subclass and override drawRect. Use various Core Graphics techniques to draw content by hand.
Not animatable. Implementing my own contentOffset property isn't a standard Core Animation property.
Overriding drawRect on UIScrollView doesn't respect the content offset, and doesn't get called multiple times as it scrolls/animates. The rect parameter is always simply the bounds of the UIScrollView.
As with (4), except I set the bounds.origin of my UIView subclass in my setContentOffset implementation, since it's an animatable property.
drawRect doesn't seem to get called every frame.
Use CATiledLayer, as suggested in this answer: Large UIScrollView with background pattern fails. Implementation details here: http://www.cimgf.com/2011/03/01/subduing-catiledlayer/.
I really don't want the ugliness of seeing tiles asynchronously being drawn as user scrolls. It's just a simple background pattern!
This seems like the simplest thing! Why is it so hard!?
Maybe the sample code:ScollViewSuit->3_Tiling can help you. You can search it in the official docset.
This works like CATiledLayer but only use UIKit, the tile was loaded on the main thread.
And I really don't think this is a good solution.

Resources