Performance issue when try to make rounded UIImage (using mask) - objective-c

I've developed method which make from rect UIImage to rounded. The problem is that it decrease performance if do that operation for 10 different images in a row. Images have resolution 120x120
- (UIImage *)roundedImage:(UIImage*)anOriginalImage radius:(CGFloat)aRadius
{
UIImage *result = nil;
if (anOriginalImage != nil) {
UIGraphicsBeginImageContextWithOptions(anOriginalImage.size, NO, 0);
[[UIBezierPath bezierPathWithRoundedRect:(CGRect){CGPointZero, anOriginalImage.size}
cornerRadius:aRadius] addClip];
[anOriginalImage drawInRect:(CGRect){CGPointZero, anOriginalImage.size}];
result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return result;
}
How to fix that?

Import the QuartzCore framework to get access to the .cornerRadius property of your UIView or UIImageView.
#import <QuartzCore/QuartzCore.h>
Also manually add it to your project's Frameworks folder.
Add this method to your view controller or wherever you need it:
-(void)setRoundedView:(UIImageView *)roundedView toDiameter:(float)newSize;
{
CGPoint saveCenter = roundedView.center;
CGRect newFrame = CGRectMake(roundedView.frame.origin.x, roundedView.frame.origin.y, newSize, newSize);
roundedView.frame = newFrame;
roundedView.layer.cornerRadius = newSize / 2.0;
roundedView.center = saveCenter;
}
To use it, just pass it a UIImageView and a diameter. This example assumes you have a UIImageView named "circ" added as a subview to your view. It should have a backgroundColor set so you can see it.
[self setRoundedView:circ toDiameter:100.0];
This just handles UIImageViews but you can generalize it to any UIView.

Related

UIImage from SKTexture

How to get UIImage from SKTexture?
I tried to get UIImage from SKTextureAtlas, but it seems not working too:
// p40_prop1 is a part of SKTextureAtlas
UIImage *image = [UIImage imageNamed:#"p40_prop1"];
image is nil.
Starting from iOS 9 it is a piece of cake. SKTexture now has CGImage property, which is of CGImageRef type. So getting image from a texture is just one line now:
let image : UIImage = UIImage(CGImage:texture.CGImage)
This seems to be working for me:
- (UIImage*) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}
- (UIImage*) imageFromNode:(SKNode*)node
{
SKTexture* tex = [self.scene.view textureFromNode:node];
SKView* view = [[SKView alloc]initWithFrame:CGRectMake(0, 0, tex.size.width, tex.size.height)];
SKScene* scene = [SKScene sceneWithSize:tex.size];
SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:tex];
sprite.position = CGPointMake( CGRectGetMidX(view.frame), CGRectGetMidY(view.frame) );
[scene addChild:sprite];
[view presentScene:scene];
return [self imageWithView:view];
}
get the SKTexture for your node using the current SKView
make another SKView that is just big enough for your texture
add a SKSpriteNode with the texture into your new scene, placing it in the middle
render the view into a graphics context
Or for those who prefer Swift:
func imageWithView(view : UIView) -> UIImage {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0)
view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return img
}
func imageFromNode(node : SKNode) -> UIImage? {
if let tex = self.scene?.view?.textureFromNode(node) {
let view = SKView(frame:CGRectMake(0, 0, tex.size().width, tex.size().height))
let scene = SKScene(size: tex.size())
let sprite = SKSpriteNode(texture: tex)
sprite.position = CGPoint(x: CGRectGetMidX(view.frame), y: CGRectGetMidY(view.frame))
scene.addChild(sprite)
view.presentScene(scene)
return self.imageWithView(view)
}
return nil
}
There is actually a way to get a UIImage out of a SKView in iOS 7.0!
It uses regular UIView APIs to render the view into an ImageContext, then pulls a UIImage out of that. However, this solution is very limited in scope. It draws the SKView into a UIImage, then crops the resulting image to fit a given node's frame. So there must not be anything covering that node you want to snapshot. Also, both the view and scene must be visible on-screen (which is stricter than the usual -[SKView textureFromNode:] method). There may even be further restrictions that I haven't discovered.
Given all that, this procedure was still enough for what I needed, so I thought it was worth sharing.
+(UIImage *)imageFromNode:(SKNode *)node {
SKView *view = node.scene.view;
CGFloat scale = [UIScreen mainScreen].scale;
CGRect nodeFrame = [node calculateAccumulatedFrame];
// render SKView into UIImage
UIGraphicsBeginImageContextWithOptions(view.bounds.size, YES, 0.0);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES];
UIImage *sceneSnapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// crop to the requested node (making sure to flip the y-coordinate)
CGFloat originY = sceneSnapshot.size.height*scale - nodeFrame.origin.y*scale - nodeFrame.size.height*scale;
CGRect cropRect = CGRectMake(nodeFrame.origin.x * scale, originY, nodeFrame.size.width*scale, nodeFrame.size.height*scale);
CGImageRef croppedSnapshot = CGImageCreateWithImageInRect(sceneSnapshot.CGImage, cropRect);
UIImage *nodeSnapshot = [UIImage imageWithCGImage:croppedSnapshot];
CGImageRelease(croppedSnapshot);
return nodeSnapshot;
}
I've tested this on the simulator in 3.5" and 4" retina iPhones, retina and non-retina iPads. As for actual devices, it worked on iPhone 4S, iPhone 5S, and iPad 2, all running 7.0.4.
func loadBackground() {
guard let _ = childNode(withName: "background") as! SKSpriteNode? else {
let texture = SKTexture(image: UIImage(named: "stick.jpg")!)
let node = SKSpriteNode(texture: texture)
node.size = texture.size()
node.zPosition = StickHeroGameSceneZposition.backgroundZposition.rawValue
self.physicsWorld.gravity = CGVector(dx: 0, dy: gravity)
addChild(node)
return
}
}
As of iOS 7.0 there's no way to get a UIImage from SKTexture, SKTextureAtlas or the SKView.

UIImageView auto resizes when UIScrollView tapped

Ok, so I have a UIImageView inside UIScrollView. I dynamically load an image into UIImageView and then manually resize it. So far so good. But then, if I touch UIScroll scroll view (containing resized elements) everything is resized back to the original size. Just in case, here's the code that resizes the image:
/**
* Proportionally resize and position doodle
*/
- (void)resizeDoodle:(CGFloat)height {
self.doodleImageView.frame = CGRectMake(0, 0, self.view.bounds.size.width, height);
[self.scrollView setContentSize:self.doodleImageView.frame.size];
}
self.Imageview.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
And This line keep image to fit
self.Imageview.contentMode = UIViewContentModeScaleAspectFit;
Write above code for restrict your issue.
Another way:
Use following method for specific hight and width (That you want) with image
+ (UIImage*)resizeImage:(UIImage*)image withWidth:(int)width withHeight:(int)height
{
CGSize newSize = CGSizeMake(width, height);
float widthRatio = newSize.width/image.size.width;
float heightRatio = newSize.height/image.size.height;
if(widthRatio > heightRatio)
{
newSize=CGSizeMake(image.size.width*heightRatio,image.size.height*heightRatio);
}
else
{
newSize=CGSizeMake(image.size.width*widthRatio,image.size.height*widthRatio);
}
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
This method return NewImage, with specific size that you specify :)
This code may be helpful for you:)

Making UITableView with cell-sized images smooth scrolled

EVERYTHING WRITTEN HERE ACTUALLY WORKS RIGHT
EXEPT FOR [UIImage imageNamed:] METHOD USAGE
Implementation
I am using model in witch you have a custom UITableViewCell with one custom UIView set up as Cell's backgroundView.
Custom UIView contains two Cell-sized images (320x80 px), one of which is 100% transparent to half of the view. All elements are set to be Opaque and have 1.0 Alpha property.
I don't reuse Cells because I failed to make them loading different images. Cell's reused one-by-one up to 9 cells overall. So I have 9 reusable Cells in memory.
Cell initWithStyle:reuseIdentifier method part:
CGRect viewFrame = CGRectMake(0.0f, 0.0f, 320.0f, 80.0f);
customCellView = [[CustomCellView alloc] initWithFrame:viewFrame];
customCellView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self setBackgroundView:customCellView];
CustomCellView's initialization method:
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.opaque = YES;
self.backgroundColor = [UIColor UICustomColor];
}
return self;
}
Images are being pre-loaded to NSMutableArray as UIImage objects from PNG files with UIImage's imageNamed: method.
They are being set in UITableViewDelegate's method tableView:cellForRowAtIndexPath: and passed through UITableViewCell with custom method to UIView.
And then drawn in UIView's drawRect: overridden method:
- (void)drawRect:(CGRect)rect {
CGRect contentRect = self.bounds;
if (!self.editing) {
CGFloat boundsX = contentRect.origin.x;
CGFloat boundsY = contentRect.origin.y;
CGPoint point;
point = CGPointMake(boundsX, boundsY);
if (firstImage) { [firstImage drawInRect:contentRect blendMode:kCGBlendModeNormal alpha:1.0f]; }
if (secondImage) { [secondImage drawInRect:contentRect blendMode:kCGBlendModeNormal alpha:1.0f]; }
}
}
As you see images are being drawn with drawInRect:blendMode:alpha: method.
Problem
Well, UITableView can't be scrolled at all, it's being struck on every cell, it's chunky and creepy.
Thoughts
Well digging sample code, stackoverflow and forums gave me thought to use OpenGL ES to pre-render images, but, really, is it that hard to make a smooth scrolling?
What's wrong with just using UIImageViews? Are they not fast enough? (They should be if you're preloading the UIImages).
One thing to note is that [UIImage imageNamed:] won't explicitly load the image data into memory. It'll give you a reference which is backed by the data on disk. You can get around this by making a call to [yourImage CGImage].

UIScrollView setZoomScale not working?

I am struggeling with my UIScrollview to get it to zoom-in the underlying UIImageView. In my view controller I set
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return myImageView;
}
In the viewDidLoad method I try to set the zoomScale to 2 as follows (note the UIImageView and Image is set in Interface Builder):
- (void)viewDidLoad {
[super viewDidLoad];
myScrollView.contentSize = CGSizeMake(myImageView.frame.size.width, myImageView.frame.size.height);
myScrollView.contentOffset = CGPointMake(941.0, 990.0);
myScrollView.minimumZoomScale = 0.1;
myScrollView.maximumZoomScale = 10.0;
myScrollView.zoomScale = 0.7;
myScrollView.clipsToBounds = YES;
myScrollView.delegate = self;
NSLog(#"zoomScale: %.1f, minZoolScale: %.3f", myScrollView.zoomScale, myScrollView.minimumZoomScale);
}
I tried a few variations of this, but the NSLog always shows a zoomScale of 1.0.
Any ideas where I screw this one up?
I finally got this to work. what caused the problem was the delegate call being at the end. I now moved it up and .... here we go.
New code looks like this:
- (void)viewDidLoad {
[super viewDidLoad];
myScrollView.delegate = self;
myScrollView.contentSize = CGSizeMake(myImageView.frame.size.width, myImageView.frame.size.height);
myScrollView.contentOffset = CGPointMake(941.0, 990.0);
myScrollView.minimumZoomScale = 0.1;
myScrollView.maximumZoomScale = 10.0;
myScrollView.zoomScale = 0.7;
myScrollView.clipsToBounds = YES;
}
Here is another example I made. This one is using an image that is included in the resource folder. Compared to the one you have this one adds the UIImageView to the view as a subview and then changes the zoom to the whole view.
-(void)viewDidLoad{
[super viewDidLoad];
UIImage *image = [UIImage imageNamed:#"random.jpg"];
imageView = [[UIImageView alloc] initWithImage:image];
[self.view addSubview:imageView];
[(UIScrollView *) self.view setContentSize:[image size]];
[(UIScrollView *) self.view setMaximumZoomScale:2.0];
[(UIScrollView *) self.view setMinimumZoomScale:0.5];
}
I know this is quite late as answers go, but the problem is that your code calls zoomScale before it sets the delegate. You are right the other things in there don't require the delegate, but zoomScale does because it has to be able to call back when the zoom is complete. At least that's how I think it works.
My code must be completely crazy because the scale that I use is completely opposite to what tutorials and others are doing. For me, minScale = 1 which indicates that the image is fully zoomed out and fits the UIImageView that contains it.
Here's my code:
[self.imageView setImage:image];
// Makes the content size the same size as the imageView size.
// Since the image size and the scroll view size should be the same, the scroll view shouldn't scroll, only bounce.
self.scrollView.contentSize = self.imageView.frame.size;
// despite what tutorials say, the scale actually goes from one (image sized to fit screen) to max (image at actual resolution)
CGRect scrollViewFrame = self.scrollView.frame;
CGFloat minScale = 1;
// max is calculated by finding the max ratio factor of the image size to the scroll view size (which will change based on the device)
CGFloat scaleWidth = image.size.width / scrollViewFrame.size.width;
CGFloat scaleHeight = image.size.height / scrollViewFrame.size.height;
self.scrollView.maximumZoomScale = MAX(scaleWidth, scaleHeight);
self.scrollView.minimumZoomScale = minScale;
// ensure we are zoomed out fully
self.scrollView.zoomScale = minScale;
This works as I expect. When I load the image into the UIImageView, it is fully zoomed out. I can then zoom in and then I can pan the image.

UITableViewCell's imageView fit to 40x40

I use the same big images in a tableView and detailView.
Need to make imageView filled in 40x40 when an imags is showed in tableView, but stretched on a half of a screen. I played with several properties but have no positive result:
[cell.imageView setBounds:CGRectMake(0, 0, 50, 50)];
[cell.imageView setClipsToBounds:NO];
[cell.imageView setFrame:CGRectMake(0, 0, 50, 50)];
[cell.imageView setContentMode:UIViewContentModeScaleAspectFill];
I am using SDK 3.0 with build in "Cell Objects in Predefined Styles".
I put Ben's code as an extension in my NS-Extensions file so that I can tell any image to make a thumbnail of itself, as in:
UIImage *bigImage = [UIImage imageNamed:#"yourImage.png"];
UIImage *thumb = [bigImage makeThumbnailOfSize:CGSizeMake(50,50)];
Here is .h file:
#interface UIImage (PhoenixMaster)
- (UIImage *) makeThumbnailOfSize:(CGSize)size;
#end
and then in the NS-Extensions.m file:
#implementation UIImage (PhoenixMaster)
- (UIImage *) makeThumbnailOfSize:(CGSize)size
{
UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
// draw scaled image into thumbnail context
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *newThumbnail = UIGraphicsGetImageFromCurrentImageContext();
// pop the context
UIGraphicsEndImageContext();
if(newThumbnail == nil)
NSLog(#"could not scale image");
return newThumbnail;
}
#end
I cache a thumbnail version since using large images scaled down on the fly uses too much memory.
Here's my thumbnail code:
- (UIImage *)thumbnailOfSize:(CGSize)size {
if( self.previewThumbnail )
return self.previewThumbnail; // returned cached thumbnail
UIGraphicsBeginImageContext(size);
// draw scaled image into thumbnail context
[self.preview drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage *newThumbnail = UIGraphicsGetImageFromCurrentImageContext();
// pop the context
UIGraphicsEndImageContext();
if(newThumbnail == nil)
NSLog(#"could not scale image");
self.previewThumbnail = newThumbnail;
return self.previewThumbnail;
}
Just make sure you properly clear the cached thumbnail if you change your original image (self.preview in my case).
I have mine wrapped in a UIView and use this code:
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
[self addSubview:imageView];
imageView.frame = self.bounds;
(self is the wrapper UIView, with the dimensions I want - I use AsyncImageView).
I thought Ben Lachman's suggestion of generating thumbnails in advance rather than on the fly was smart, so I adapted his code so it could handle a whole array and to make it more portable (no hard-coded property names).
- (NSArray *)arrayOfThumbnailsOfSize:(CGSize)size fromArray:(NSArray*)original {
NSMutableArray *temp = [NSMutableArray arrayWithCapacity:[original count]];
for(UIImage *image in original){
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0,0,size.width,size.height)];
UIImage *thumb = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[temp addObject:thumb];
}
return [NSArray arrayWithArray:temp];
}
you might be able to use this?
yourTableViewController.rowImage = [UIImage imageNamed:#"yourImage.png"];
and/or
cell.image = yourTableViewController.rowImage;
and if your images are already 40x40 then you shouldn't have to worry about setting bounds and stuff... but, i'm also new to this, so, i wouldn't know, haven't played around with Table View row/cell images much
hope this helps.
I was able to make this work using interface builder and a tableviewcell. You can set the "Mode" properties for an image view to "Aspect Fit". I'm not sure how to do this programatically.
Try setting UIImageView.autoresizesSubviews and/or UIImageView.contentStretch.