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!