I have to create UIView with custom shape, such as triangular, half-rect, etc.
I used to crop special image to that form and set as a background of my view.
Although it is a popular solution, I am not sure whether it is the most efficient one in terms of sustainability.
On the other hand, I found useful way of solving this problem with CAShapeLayer()
Could you please provide pros and cons of both approaches?
CAShapeLayer all the way!
With little effort you can achieve the same result with less memory (RAM) and less maintenance time (if you want to make the triangle thicker for example, you'll need a new image however a small change in code and you have it!). Moreover your app size will be relatively smaller and you don't have to worry about the resolution like the images.
Hope this helps!
Related
I'm having a segment control design like this
How to design my segments so that the selected one should look like the "ALL" like in the image above. What I could think of to use different images on selection but if I do that then some part of the curve which is going in "Others" won't be visible. Any suggestion on designing UISegmentControl like this ?
I have two suggestions:
Find an alternative approach that avoids this.
A lot of apps try to add delight by designing custom components and UI when actually it doesn't really add that much to the app. At worst you might frustrate people by using non-standard components that don't work the way they expect, or increase cognitive load as they're trying to use you're app.
Go 100% with a custom subclass.
Don't just settle for setting a static background image, but invest in creating a component that not only looks like this in a selected state, but also provides animations as people change the selected item.
This is going to be a fair amount of work, but would be something like this:
subclassing UISegmentedController -- it provides the base of the functionality that you're looking for which is good;
adding a new CAShapeLayer as to the controls background layer
figuring out a dynamic bezier curve that can update for all your states (will probably end up having control points for each segment) you might be able to do this by hand, but I'd have to use a tool like PaintCode to generate the bezier curve code, and Illustrator to make the initial curve
add listeners for event changes of the segment changing, and recalculate the curve control points as needed
The positive note of this is that the path property on CAShapeLayer is animatable, so when the segment changes and the curve updates the animation will probably be the easiest part!
I have UIView ancestor - let say MyView, which overrides method -drawInRect to display an image
override func draw(_ rect: CGRect) {
super.draw(rect)
image?.draw(in: rect)
/*some code here*/
}
This view is scaling in both directions at some point. After enlarging, displayed image loses it's quality. Saying, if I create MyView with size 20*20, bounds property remains the same after scaling, but transformed image I'm trying to display is low quality, because I'm trying to paint it with the same 20x20 size. Is there any way to draw an image respectively to my view scaling?
First, strongly consider using a UIImageView as a subview rather than trying to implement even modestly complicated image drawing yourself. UIImageView is optimized for many of the problems you can face. It's not perfect, and sometimes it's possible to do better by hand if you know what you're doing, but it does its job well.
To your specific problem, your contentMode is almost certainly scaleToFill (that's the default). The simplest solution is to change it to redraw to force a full redraw when the bounds change. That's not highly efficient, but may be ok for your problem (it often is fine). Making it both high quality and highly efficient is a bit more complicated, and often requires a lot of special knowledge how your app behaves. For the easy cases, making this high quality and efficient is what UIImageView does for you.
If you're reliably scaling from 20x20 to some other specific size, and you don't have too many views, another trick is to draw at your full size and then scale down to 20x20. This is very inefficient if you have a lot of views and the size of the final images is large, but can be very fast and high quality, so is something to at least consider. I've done this to deal with small zooms (for example to 110% size). It probably wouldn't be a good way to turn thumbnails into full-screen images, though.
I'm considering building an app that would make heavy use of a flood fill / paint bucket feature. The images I'd be coloring are simply like coloring book pages; white background, black borders. I'm debating which is better to use UIImage (by manipulating pixel data) or drawing the images with Core Graphics and changing the fill color on touch.
With UIImage, I'm unable to account for retina images properly; it destroys the image when I write the context into a new UIImage, but I can probably figure out. I open to tips though...
With CoreGraphics, I have no idea how to calculate which shape to fill when a user touches an area and then actually filling that area. I've looked but I have not turned up a successful search.
Overall, I believe the optimal solution is using CoreGraphics, since it'll be lighter overall and I won't have to keep several copies of the same image for different sizes.
Thoughts? Go easy on me! It's my first app and first SO question ;)
I'd suggest using Core Graphics.
Instead of images, define the shapes using CGPath or NSBezierPath, and use Core Graphics to stroke and/or fill the shapes. Filling shapes is then as easy as switching drawing mode from just stroking to stroking and filling.
Creating even more complex shapes is made much easier with the "PaintCode" app (which lets you draw and creates the path code for you).
As your first app, I would suggest something with a little less custom graphics fiddling, though.
What is the difference between CGContextDrawLinearGradient, called in a UIView's drawRect method, and a CAGradientLayer? How do they compare performance wise? What is the best practice for creating gradient views? I'd really like a nice explanation of how they relate to each other and why one is better performance than the other.
Thanks.
If you just want a box with a gradient in it then performance isn't really an issue. You should go with whatever is simplest to implement for your particular requirements.
Adding a CAGradientLayer means you don't have to create a view subclass, you can just add the layer to an existing view. The setup is also slightly easier, since you don't need to worry about frame sizes or any c-style core graphics functions. You can also add rounded corners, shadows etc without too much effort.
However, if you want more than one gradient view, a subclass might be a good idea, so you can just instatiate new ones.
So, unfortunately, there isn't a clear cut answer to your question. Neither is definitively better. If you are concerned with performance, implement both and test using instruments.
Let's say I want to add 50 images to a view for the purpose of animating them. And let's suppose I'm planning on using Core Animation (e.g., CABasicAnimation) rather than "UIView" animation.
Am I better off implementing this by adding 50 subviews or 50 sublayers? Does it make a difference?
Thanks.
As I describe here, I've used both UIViews and CALayers in animations and found a negligible performance difference between them. UIViews are very lightweight wrappers around the layers. Also, any layer-based animations you need can be applied to a UIView's backing layer easily.
I've used CALayers directly in situations where I wanted to create cross-platform (Mac / iOS) UI elements, because CALayers are almost identical in their implementation on both platforms (unlike the significantly different NSViews and UIViews). CALayers don't have any touch-handling routines out of the box, but you can add that capability if you need to.
There are also some edge cases where you might want to work directly with layers, like when trying to do limited 3-D manipulation of the layers (as in a CoverFlow effect) or when using a CAReplicatorLayer to produce particle effects.
UIViews contain sublayers, so they are heavier weight, and contain stuff you probably don't need for all 50 images, such as event and touch handlers/variables. So using layers would probably be slightly more efficient and use a bit less memory than using views for each image.
The difference for such a small number of images is negligible. Use what's most convenient.
I've not done animation (yet :-), but the stuff I remember reading about it suggests to create one image with all 50 tiled on it and then just offset to the correct image when drawing. That way you only need one layer or UIImage or whatever to display it. I don't know about speed, but I'd guess it would save memory and would probably be easier to manage and code.