Fun with Cocoa Touch elements
Hey everyone, this is Mahdi!
We're going to have a little bit of fun with iOS elements today, so I'm going to show some tricks that I hope you'll like!
UIView with rounded corners and shadow
Now, this is a bit tricky. To make a view have both rounded corners and a shadow we need to nest it into another view.
This is because to make the corners rounded we need to enable masksToBounds
, which will hide anything outside the view, unfortunately including the shadow we need.
So, the correct way to do it is to create 2 views, one inside the other and we'll call them outsideView
and insideView
. (i.e. outsideView
is the superview of insideView
)
Then, we proceed to use the following code:
insideView.layer.cornerRadius = 15; insideView.layer.masksToBounds = YES; insideView.superview.layer.masksToBounds = NO; insideView.superview.layer.shadowOffset = CGSizeMake(0, 1); insideView.superview.layer.shadowColor = [UIColor blackColor].CGColor; insideView.superview.layer.shadowOpacity = 1; insideView.superview.layer.shadowRadius = 3.0; insideView.superview.opaque = YES; insideView.superview.layer.cornerRadius = 15;
The result:
UILabel (multi-line) with padding
This one requires a subclass of UILabel, we'll call ours PaddedLabel
:
PaddedLabel.h
#import <UIKit/UIKit.h> @interface PaddedLabel : UILabel @end
PaddedLabel.m
#import "PaddedLabel.h" #define PADDING 10 #define TOP_PADDING 8 @implementation PaddedLabel - (void)drawTextInRect:(CGRect)rect { CGRect newRect = UIEdgeInsetsInsetRect(rect, UIEdgeInsetsMake(TOP_PADDING, PADDING, 0, PADDING)); CGRect labelStringSize = [self.text boundingRectWithSize:CGSizeMake(newRect.size.width, 9999) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.font} context:nil]; [super drawTextInRect:CGRectMake(newRect.origin.x, newRect.origin.y, labelStringSize.size.width, labelStringSize.size.height)]; } - (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines { return CGRectInset([self.attributedText boundingRectWithSize:CGSizeMake(999, 999) options:NSStringDrawingUsesLineFragmentOrigin context:nil], -PADDING, 0); } @end
Changing the defined PADDING
and TOP_PADDING
and then using it like a simple UILabel should be easy enough!
Keeping the correct aspect ratio for images
Sometimes you get tired from coding the same thing again and again, so you'll just make a category out of it. This is one of those times, especially because I have to deal with lots of images right now!
(I'm going to miss Objective-C categories once Swift becomes the norm! :( )
UIImage+Convenience.h
#import <UIKit/UIKit.h> @interface UIImage (Convenience) - (CGFloat)heightForWidth:(CGFloat)width; + (CGFloat)heightForRatio:(CGFloat)ratio andWidth:(CGFloat)width; + (CGFloat)heightFor16_9RatioWithWidth:(CGFloat)width; + (CGFloat)heightFor16_10RatioWithWidth:(CGFloat)width; @end
UIImage+Convenience.m
#import "UIImage+Convenience.h" @implementation UIImage (Convenience) - (CGFloat)heightForWidth:(CGFloat)width { CGFloat scale = self.size.width / width; if (isnan(scale) || scale == 0) scale = 16.0/10.0; return self.size.height/scale; } + (CGFloat)heightForRatio:(CGFloat)ratio andWidth:(CGFloat)width { return width / ratio; } + (CGFloat)heightFor16_9RatioWithWidth:(CGFloat)width { return [self heightForRatio:16.0/9.0 andWidth:width]; } + (CGFloat)heightFor16_10RatioWithWidth:(CGFloat)width { return [self heightForRatio:16.0/10.0 andWidth:width]; } @end
Usage example: Resizing an UIImageView to fit inside its container and have a correct aspect ratio!
CGFloat maxWidth = containerView.frame.size.width; CGFloat newHeight = [anImageView.image heightForWidth:maxWidth]; if (newHeight == 0) { // The image view hasn't loaded the image yet! newHeight = [UIImage heightFor16_10RatioWithWidth:maxWidth]; } anImageView.frame = CGRectMake(0, 0, maxWidth, newHeight);
Instantly tappable links in UITextView
This one uses CCHLinkTextView which is a subclass of UITextView
allowing you to get instant response to taps on links.
Just set the linkDelegate
on your text view and implement the delegate method like this:
- (void)linkTextView:(CCHLinkTextView *)linkTextView didTapLinkWithValue:(id)value { if (value) { NSURL* url = [value isKindOfClass:[NSURL class]] ? value : [NSURL URLWithString:value]; [[UIApplication sharedApplication] openURL:url]; } }
After that, make sure that the attributedText
of your text view uses CCHLinkAttributeName
for links instead of NSLinkAttributeName
.
You can replace all occurrences this way:
NSMutableAttributedString* correct = [textView.attributedText mutableCopy]; [correct enumerateAttribute:NSLinkAttributeName inRange:NSMakeRange(0, correct.length) options:kNilOptions usingBlock:^(id value, NSRange range, BOOL *stop) { if (value) { [correct removeAttribute:NSLinkAttributeName range:range]; [correct addAttribute:CCHLinkAttributeName value:value range:range]; } }]; textView.attributedText = correct;
Cool projects
These are some cool projects that might be useful to you if you're making an iOS app:
MAThemeKit — Theming your app with one line of code. Having a consistent color scheme and UI elements!
FLKAutoLayout — A much easier way to create auto layout constraints
POP-MCAnimate — Offers a much easier syntax for animating elements using Facebook's POP.
PNChart — Creating animated charts with a few lines of code!
SCLAlertView — A rethinking of the conventional Alert View. This one is much more beautiful and has cool animations.
That's it! Hope you've found anything that interests you. Until next time!