CustomBadge rendering "grabage" around badge - ios

I am using the CustomBadge library discussed in this question: How can I add a badge to a standard UIButton?
Here is my code:
CustomBadge* customBadge =
[CustomBadge customBadgeWithString:#"$2.99"
withStringColor:[UIColor whiteColor]
withInsetColor:[UIColor redColor]
withBadgeFrame:YES
withBadgeFrameColor:[UIColor whiteColor]
withScale:1.0
withShining:NO];
customBadge.frame = CGRectMake(self.btnUpgrade.frame.size.width - customBadge.frame.size.width + 15, -15, customBadge.frame.size.width, customBadge.frame.size.height);
[self.btnUpgrade addSubview:customBadge];
I get what I'm expecting:
I am using a NavigationController. When I segue into another ViewController and come back, my badge gets a "tail":
If I get rid of the customBadge.frame line, the badge renders correctly (at the wrong position though).
The code is executing on viewWillAppear:
Any clue what might be causing this?
The CustomBadge code is at: https://github.com/ckteebe/CustomBadge/blob/master/Classes/CustomBadge.m

I guess the problem is with paths. So, to resolve it I would:
Check that you start path from the point you want and not from a random one. That means you need to call CGContextMoveToPoint before calling CGContextBeginPath
Close (CGContextClosePath) each path before using it.

It looks like CustomBadge's drawRect uses a rect of a different size when I change the customBadge's frame in my code above. Maybe it has to do with clipToBounds vs maskToBounds.
I added this quick fix:
CustomBadge* customBadge =
[CustomBadge customBadgeWithString:#"$2.99"
withStringColor:[UIColor whiteColor]
withInsetColor:[UIColor redColor]
withBadgeFrame:YES
withBadgeFrameColor:[UIColor whiteColor]
withScale:1.0
withShining:NO];
// copy the graphics of the badge into a UIImage
UIGraphicsBeginImageContext(customBadge.bounds.size);
[customBadge.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// position the UIImageView at the top right hand side of the button
UIImageView* badgeHolder = [[UIImageView alloc] initWithFrame:CGRectMake(self.btnUpgrade.frame.size.width - customBadge.frame.size.width + 15, -15, customBadge.frame.size.width, customBadge.frame.size.height)];
// put the badge's image in a UIImageView
[badgeHolder setImage:viewImage];
[self.btnUpgrade addSubview:badgeHolder];

Related

UIVibrancyEffect On iOS 7

So I've been playing around with the iOS 8 beta and implementing the new UIEffectViews in the places that my app needed them. Now I've run into the issue that I still want to have backwards compatibility for iOS 7, but maintain the vibrancy effect because it really helps readability. I've used UIToolbars in the past for a blur effect, and they work great, but not for vibrancy. I thought I'd subclass UIView and add a toolbar subview and then do some clever rendering to sort of achieve the vibrancy effect which would look like this:
1. render the toolbar to a UIImage
2. render the vibrant content to a UIImage
3. mask the toolbar image to the vibrant content image mask
4. mess with the saturation and brightness
5. have a subview of the UIView display the final result over the toolbar
I've tried doing this in drawRect: of the UIView but it doesn't want to redraw every frame, and setting a timer really messes with animation, even though the render time isn't very high. If anyone can point me to sample code or a open source library, it would be much appreciated.
Thanks.
So I never posted an answer, but I did figure it out.
The brute force approach I tried was to use Core Image effects. I would render the superview to a UIImage, blur it, then overlay it on a toolbar with the dark style. This looked great, but even on a GPU context on my 5S, it was pretty slow, so theres no way it would work on other devices. This is the best I could get it to look, and would work great for static content, but is not practical for real-time.
I was able to achieve a real time version, but it doesn't look quite as good. Basically what I do is render all the vibrant content to a image and use it for a mask for a view. Then I make the view barely visible (like .2 alpha), and then put it over a toolbar. It doesn't look quite as vibrant as iOS8, or the original CI version, but it works great and preforms well.
Heres a bit of code you can just copy and paste if you really want:
-(instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.backgroundColor = [UIColor colorWithWhite:1 alpha:0.2];
maskingContents = [[UIView alloc] initWithFrame:self.bounds];
[self addSubview:maskingContents];
}
return self;
}
-(void)addSubview:(UIView *)view
{
if (![view isEqual:maskingContents])
{
[maskingContents setHidden:NO];
[maskingContents addSubview:view];
//now we need to mask it
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
[maskingContents.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//apply the mask
CALayer* maskLayer = [CALayer layer];
maskLayer.frame = self.bounds;
[maskLayer setContents:(id)mask.CGImage];
[self.layer setMask:maskLayer];
[maskingContents setHidden:YES];
} else [super addSubview:view];
}
-(void)forceVibrancyUpdate
{
[maskingContents setHidden:NO];
//now we need to mask it
UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
[maskingContents.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* mask = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//apply the mask
CALayer* maskLayer = [CALayer layer];
maskLayer.frame = self.bounds;
[maskLayer setContents:(id)mask.CGImage];
[self.layer setMask:maskLayer];
[maskingContents setHidden:YES];
}
#end
If you want to dynamically update the content inside the vibrancy view, you would call forceVibrancyUpdate, as that would re-render the mask and apply it. Hope this helped everyone.

Customize uiswitch image properly?

I have an UISwitch in my iOS 6 app, that's on and off image is customised.
self.testSwitch.onImage = [UIImage imageNamed:#"on"];
self.testSwitch.offImage = [UIImage imageNamed:#"off"];
I use 77 points wide and 22 points tall image for this purpose (154x44 in retina), as it is stated in the documentation. But my image does not fit to my uiswitch, it seems ugly, the default style hides my one, like on the attached image.
What should I set to make it work in a proper way?
Here is code from my book. This is not exactly what you want to do, but it shows the technique and will get you started! Notice that I'm using 79 by 27 (not sure where you got your numbers from):
UIGraphicsBeginImageContextWithOptions(CGSizeMake(79,27), NO, 0);
[[UIColor blackColor] setFill];
UIBezierPath* p = [UIBezierPath bezierPathWithRect:CGRectMake(0,0,79,27)];
[p fill];
NSMutableParagraphStyle* para = [NSMutableParagraphStyle new];
para.alignment = NSTextAlignmentCenter;
NSAttributedString* att =
[[NSAttributedString alloc] initWithString:#"YES" attributes:
#{
NSFontAttributeName:[UIFont fontWithName:#"GillSans-Bold" size:16],
NSForegroundColorAttributeName:[UIColor whiteColor],
NSParagraphStyleAttributeName:para
}];
[att drawInRect:CGRectMake(0,5,79,22)];
UIImage* im = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.sw2.onImage = im;
Looks like this:
Apple doesn't have an Appearance API for UISwitch. You can set a tint color property (onTintColor). But that's not what you want, I guess. The problem about customizing UISwitch is that there's the opportunity that Apple will reject your app.
But there are some APIs for a custom switch such as RCSwitch(https://github.com/robertchin/rcswitch) or TTSwitch. A good tutorial and example of how to use RCSwitch can be found here: http://www.raywenderlich.com/23424/photoshop-for-developers-creating-a-custom-uiswitch.

How to set the imageView property in a UITableCellView to show only a background color?

I have a custom class inheriting from UITableViewCell class that shows either an image (left to the title) or a generic dark-colored square if the image is not available). The following code shows a dark square on a light-colored cell background:
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(11, 6, 40, 40)];
[imageView setBackgroundColor:kBackgroundGreyColour];
[cell.contentView addSubview:imageView];
However, instead of creating a custom subview in each table cell I would rather like to use the existing imageView property of the generic UITableViewCell class and modify it somehow to show the square as the code above does. This is what I am trying at this moment:
UIImageView* iv = [[UIImageView alloc] initWithFrame:CGRectMake(11, 6, 40, 40)];
[iv setBackgroundColor:[UIColor redColor]];
self.imageView.hidden = NO;
self.imageView.opaque = iv.opaque;
self.imageView.alpha = iv.alpha;
self.imageView.image = iv.image;
[self bringSubviewToFront:self.imageView];
[self.imageView setBackgroundColor:[UIColor redColor]];
I added all those lines to set as many of the existing UIImageView properties to the same values as the created UIImageView instance in the first code snippet, and yet the second code snippet doesn't show any dark square. It just doesn't show anything at all and the cell looks like there is just the light background and no image view visible. But I see that the imageView property is not nil so executing all those lines of code in the second snippet should show something?
However, as soon as I assign a new image to the imageView property (e.g. self.imageView.image = [[UIImage alloc] init...], the square shows the assigned image without problems.
Edit: Just a note that in the second case I am setting the frame of the imageView in layoutSubview function, e.g.:
-(void)layoutSubviews
{
[super layoutSubviews];
self.imageView.frame = CGRectMake(11, 6, 40, 40);
}
So my questions are:
1. Which properties of the existing imageView property I would need to set and to what values so that the code will show a square filled with a specific color (like the first snippet of code does)?
Is there a way of creating the UIImage programatically so that it shows only a background color without any image associated with it (and which I could use to set the imageView.image property to show that color).
Is it possible to replace the existing imageView property in a UITableViewCell class with a custom view without adding a custom subview (like the first code snippet did), so that I can show a placeholder UIView with a background color when the image is not available?
The reason why your code doesn't work, is as you guessed; Because when you set the background colour of an imageview, it doesn't create anything on the image property.
And, you've figured out that you can't directly set the imageview property of the cell either.
I'd say your best bet, is the former option; To create a UIImage programmatically.
Although, I'd highly suggest simply creating one in your favourite image editing software then including it in the bundle. It makes for easy replacement later, for when you may get a better image, and next to no code and effort required to replace.
But if you still wish to do it all programmatically, it's not as simple as you'd hope.
CGRect rect = CGRectMake(11, 6, 40, 40);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [kBackgroundGreyColour CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.imageview.image = image;
Should do the trick.
This defines the image size, creates a graphics context (think of it as a canvas), picks your grey colour to use, paints the canvas with it, then scans it into your computer into the small little size you wanted.
The little green imp does it all behind the screen (Sorry, too much Terry Pratchett).

Center [UIColor colorWithPatternImage]?

I couldn't find anything on how you can center the 'image' when you use (maybe you can't):
[self setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"background"]]];
Setting my [self setContentMode:UIContentModeCenter]; doesnt help.
Do I have to manually draw the image (either in drawRect or set the content of the CALayer? Which one if preferable?
I think you're on the wrong path here: you create a UIColor from a pattern (pattern already implies this is a repeating image). All in all- you can't have your pattern not repeat and centered.
If you just want simple image as background of your UIView, just add it as a subview and center it.
UIImage* img = [UIImage imageNamed:#"yourfile.png"];
UIImageView* imgView = [[UIImageView alloc]initWithImage: img];
[yourUIView addSubview: imgView];
imgView.center = CGPointMake(yourUIView.frame.size.width/2, yourUIView.frame.size.height/2);
Now - add more subviews to your "yourUIView" view and they'll show on top of the image - thus the image becoming the background.
So... no need to draw anything yourself.

Rounded Corners on UIImage

I'm trying to draw images on the iPhone using with rounded corners, a la the contact images in the Contacts app. I've got code that generally work, but it occasionally crashes inside of the UIImage drawing routines with an EXEC_BAD_ACCESS - KERN_INVALID_ADDRESS. I thought this might be related to the cropping question I asked a few weeks back, but I believe I'm setting up the clipping path correctly.
Here's the code I'm using - when it doesn't crash, the result looks fine and anybody looking to get a similar look is free to borrow the code.
- (UIImage *)borderedImageWithRect: (CGRect)dstRect radius:(CGFloat)radius {
UIImage *maskedImage = nil;
radius = MIN(radius, .5 * MIN(CGRectGetWidth(dstRect), CGRectGetHeight(dstRect)));
CGRect interiorRect = CGRectInset(dstRect, radius, radius);
UIGraphicsBeginImageContext(dstRect.size);
CGContextRef maskedContextRef = UIGraphicsGetCurrentContext();
CGContextSaveGState(maskedContextRef);
CGMutablePathRef borderPath = CGPathCreateMutable();
CGPathAddArc(borderPath, NULL, CGRectGetMinX(interiorRect), CGRectGetMinY(interiorRect), radius, PNDegreeToRadian(180), PNDegreeToRadian(270), NO);
CGPathAddArc(borderPath, NULL, CGRectGetMaxX(interiorRect), CGRectGetMinY(interiorRect), radius, PNDegreeToRadian(270.0), PNDegreeToRadian(360.0), NO);
CGPathAddArc(borderPath, NULL, CGRectGetMaxX(interiorRect), CGRectGetMaxY(interiorRect), radius, PNDegreeToRadian(0.0), PNDegreeToRadian(90.0), NO);
CGPathAddArc(borderPath, NULL, CGRectGetMinX(interiorRect), CGRectGetMaxY(interiorRect), radius, PNDegreeToRadian(90.0), PNDegreeToRadian(180.0), NO);
CGContextBeginPath(maskedContextRef);
CGContextAddPath(maskedContextRef, borderPath);
CGContextClosePath(maskedContextRef);
CGContextClip(maskedContextRef);
[self drawInRect: dstRect];
maskedImage = UIGraphicsGetImageFromCurrentImageContext();
CGContextRestoreGState(maskedContextRef);
UIGraphicsEndImageContext();
return maskedImage;
}
and here's the crash log. It looks the same whenever I get one of these crashes
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x6e2e6181
Crashed Thread: 0
Thread 0 Crashed:
0 com.apple.CoreGraphics 0x30fe56d8 CGGStateGetRenderingIntent + 4
1 libRIP.A.dylib 0x33c4a7d8 ripc_RenderImage + 104
2 libRIP.A.dylib 0x33c51868 ripc_DrawImage + 3860
3 com.apple.CoreGraphics 0x30fecad4 CGContextDelegateDrawImage + 80
4 com.apple.CoreGraphics 0x30feca40 CGContextDrawImage + 368
5 UIKit 0x30a6a708 -[UIImage drawInRect:blendMode:alpha:] + 1460
6 UIKit 0x30a66904 -[UIImage drawInRect:] + 72
7 MyApp 0x0003f8a8 -[UIImage(PNAdditions) borderedImageWithRect:radius:] (UIImage+PNAdditions.m:187)
Here is an even easier method that is available in iPhone 3.0 and up. Every View-based object has an associated layer. Each layer can have a corner radius set, this will give you just what you want:
UIImageView * roundedView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:#"wood.jpg"]];
// Get the Layer of any view
CALayer * l = [roundedView layer];
[l setMasksToBounds:YES];
[l setCornerRadius:10.0];
// You can even add a border
[l setBorderWidth:4.0];
[l setBorderColor:[[UIColor blueColor] CGColor]];
I'm gonna go ahead here and actually answer the question in the title.
Try this category.
UIImage+additions.h
#import <UIKit/UIKit.h>
#interface UIImage (additions)
-(UIImage*)makeRoundCornersWithRadius:(const CGFloat)RADIUS;
#end
UIImage+additions.m
#import "UIImage+additions.h"
#implementation UIImage (additions)
-(UIImage*)makeRoundCornersWithRadius:(const CGFloat)RADIUS {
UIImage *image = self;
// Begin a new image that will be the new image with the rounded corners
// (here with the size of an UIImageView)
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
const CGRect RECT = CGRectMake(0, 0, image.size.width, image.size.height);
// Add a clip before drawing anything, in the shape of an rounded rect
[[UIBezierPath bezierPathWithRoundedRect:RECT cornerRadius:RADIUS] addClip];
// Draw your image
[image drawInRect:RECT];
// Get the image, here setting the UIImageView image
//imageView.image
UIImage* imageNew = UIGraphicsGetImageFromCurrentImageContext();
// Lets forget about that we were drawing
UIGraphicsEndImageContext();
return imageNew;
}
#end
If appIconImage is an UIImageView, then:
appIconImage.image = [UIImage imageWithContentsOfFile:#"image.png"];
appIconImage.layer.masksToBounds = YES;
appIconImage.layer.cornerRadius = 10.0;
appIconImage.layer.borderWidth = 1.0;
appIconImage.layer.borderColor = [[UIColor grayColor] CGColor];
And also remember:
#import <QuartzCore/QuartzCore.h>
I cant offer any insight into your crash, but I thought I would offer another option for rounding the corners. I had a similar problem arise in an application i was working on. Rather than write any code I am overlaying another image which masks off the corners.
If you are calling your method (borderedImageWithRect) in a background thread, crashes might occur since UIGraphics-functions are not thread-safe. In such a case, you must create a context using CGBitmapContextCreate() - see the "Reflection" sample code from the SDK.
The easiest way is to embed a disabled[!] round-rect [not custom!] button in your view (can even do it all in the Interface Builder) and associate your image with it. The image-setting message is different for UIButton (compared to UIImageView), but the overall kludge works like a charm. Use setImage:forState: if you want a centered icon or setBackgroundImage:forState: if you want the whole image with corners cut (like Contacts). Of course if you want to display lots of these images in your drawRect this isn't the right approach, but more likely an embedded view is exactly what you needed anyway...
I would reiterate fjoachim's answer: be cautious when attempting to draw while running on a separate thread, or you may get EXC_BAD_ACCESS errors.
My workaround went something like this:
UIImage *originalImage = [UIImage imageNamed:#"OriginalImage.png"]
[self performSelectorOnMainThread:#selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];
(In my case I was resizing / scaling UIImages.)
I actually had a chance to talk about this with somebody from Apple at the iPhone Tech Talk in New York. When we talked about it, he was pretty sure it wasn't a threading issued. Instead, he thought that I needed to retain the graphics context that was generated when calling UIGraphicsBeginImageContext. This seems to violate the general rules dealing with retain rules and naming schemes, but this fellow was pretty sure he'd seen the issue previously.
If the memory was getting scribbled, perhaps by another thread, that would certainly explain why I was only seeing the issue occasionally.
I haven't had time to revisit the code and test out the fix, but PCheese's comment made me realize I hadn't posted the info here.
...unless I wrote that down wrong and UIGraphicsBeginImageContext should've been CGBitmapContextCreate...
If it only crashes some of the time, figure out what the crash cases have in common. Is dstRect the same every time? Are the images ever a different size?
Also, you need to CGPathRelease(borderPath), although I doubt that leak is causing your problem.
UIImage *originalImage = [UIImage imageNamed:#"OriginalImage.png"]
[self performSelectorOnMainThread:#selector(displayImageWithRoundedCorners:) withObject:originalImage waitUntilDone:YES];
In Swift 4.2 and Xcode 10.1
let imgView = UIImageView()
imgView.frame = CGRect(x: 200, y: 200, width: 200, height: 200)
imgView.image = UIImage(named: "yourimagename")
imgView.imgViewCorners()
//If you want complete round shape
//imgView.imgViewCorners(width: imgView.frame.width)//Pass ImageView width
view.addSubview(imgView)
extension UIImageView {
//If you want only round corners
func imgViewCorners() {
layer.cornerRadius = 10
layer.borderWidth = 1.0
layer.borderColor = UIColor.red.cgColor
layer.masksToBounds = true
}
//If you want complete round shape
func imgViewCorners(width:CGFloat) {
layer.cornerRadius = width/2
layer.borderWidth = 1.0
layer.borderColor = UIColor.red.cgColor
layer.masksToBounds = true
}
Set the Image in xib or storyboard (image width and height 41x41).
FirstViewController.h
#interface....
IBOutlet UIImageView *testImg;
#end
FirstViewController.m
-(void)viewDidLoad{
testImg.layer.backgroundColor=[[UIColor clearColor] CGColor];
testImg.layer.cornerRadius=20;
testImg.layer.masksToBounds = YES;
}

Resources