UIWebView Load PDF with Swift

Sometimes you just want to load a pdf in a UIWebView, so here’s an extension to help you. A little bit of encapsulation can make the world of difference in readability and maintainability.

extension UIWebView {
    func loadPDF(name:String!) {
        let termsPath:String? = NSBundle.mainBundle().pathForResource(name, ofType: "pdf")!
        let url = NSURL(fileURLWithPath: termsPath!)
        let pdfRequest = NSURLRequest(URL: url!)
        self.loadRequest(pdfRequest)
    }
}

Usage:

self.webView.loadPDF("Terms")

UILabel and UIButton Font Kerning with Swift

Here’s an extension to UILabel that kerns it’s text with a specified kerning value.

extension UILabel {
    func kern(kerningValue:CGFloat) {
        self.attributedText =  NSAttributedString(string: self.text ?? "", attributes: [NSKernAttributeName:kerningValue, NSFontAttributeName:font, NSForegroundColorAttributeName:self.textColor])
    }
}

Usage:

mylabel.kern(1.5)

This extension to UIButton kerns the text in it’s titleLabel property.

extension UIButton {
    func kern(kerningValue:CGFloat) {
        let attributedText =  NSAttributedString(string: self.titleLabel!.text!, attributes: [NSKernAttributeName:kerningValue, NSFontAttributeName:self.titleLabel!.font, NSForegroundColorAttributeName:self.titleLabel!.textColor])
        self.setAttributedTitle(attributedText, forState: UIControlState.Normal)
    }
}

Usage:

mybutton.kern(1.5)

Note, as of XCode 6.2, the UIButton type must be custom so that the UIButton is resized with the kerned text. Resizing behavior occurs without a call to sizeToFit() or invalidateIntrinsicContentSize().

iOS Font Kerning with Swift and NSAttributedString

From Wikipedia –

In typography, kerning (less commonly mortising) is the process of adjusting the spacing between characters in a proportional font, usually to achieve a visually pleasing result.

Today I received a PSD with kerned fonts, so I created a type level method in Swift that lets me create an attributed string by specifying a kerning value.

class func attributedString(string text:String, withFont font:UIFont!, kerning: CGFloat!, andColor color:UIColor!) -> NSAttributedString?  {
    return NSAttributedString(string: text, attributes: [NSKernAttributeName:kerning, NSFontAttributeName:font, NSForegroundColorAttributeName:color])
}

Now lets say this method was in a class called “AppearanceService”, this usage would be like this:

let attributedText:NSAttributedString? = AppearanceService.attributedString(string: "Kern it, Kern it good.", withFont: AppearanceService.mediumFont(size: 15), kerning: 1.5, andColor: UIColor.whiteColor())

Resize UIImage in Swift and iOS 8

Here is an extension to UIImage that adds a public method called resize(size:CGSize, completionHandler:(resizedImage:UIImage, data:NSData)->()). The image is resized on a global concurrent queue and the completion handler is called on the main queue with the new UIImage and image data. Good for resizing images before sending them to a server. The jpeg compression ratio should probably also be parameterized! Enjoy!


extension UIImage {
    public func resize(size:CGSize, completionHandler:(resizedImage:UIImage, data:NSData)->()) {
        dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), { () -> Void in
            var newSize:CGSize = size
            let rect = CGRectMake(0, 0, newSize.width, newSize.height)
            UIGraphicsBeginImageContextWithOptions(newSize, false, 1.0)
            self.drawInRect(rect)
            let newImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            let imageData = UIImageJPEGRepresentation(newImage, 0.5)
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                completionHandler(resizedImage: newImage, data:imageData)
            })
        })
    }
}

Open App Settings in iOS 8

Apple, Y is this so poorly documented?

1. Add a key to your app-info.plist file called UIApplicationOpenSettingsURLString

2. Set the value of this key to “app-settings:”

3. In your code, access this value as a global constant:

NSString *const UIApplicationOpenSettingsURLString

in Objective-C, and

let UIApplicationOpenSettingsURLString: NSString!

in Swift

4. Use this global constant like so :

[UIApplication sharedApplication] openURL:[NSURL URWithString: UIApplicationOpenSettingsURLString]]

in Objective C and

UIApplication.sharedApplication().openURL(NSURL(UIApplicationOpenSettingsURLString))

in Swift.

Use canOpenURL: to test if this is possible during run time. It is not possible on iOS 7. I also wonder if there are paths you can add after “app-settings:” to open specific app settings screens such as privacy. Please leave a comment if you have any information on this.

Dragging a CALayer

Put together a quick demo showing how to drag a CALAyer by capturing touch events and applying animations to the layer based on the position of the touches. Illustrates basic core animation concepts.

https://github.com/RTimal/CALayer-Drag

//
//  RTViewController.m
//  LayerTests
//
//  Created by Rajiev Timal on 3/7/14.
//  Copyright (c) 2014 Rajiev. All rights reserved.
//

#import "RTViewController.h"

typedef NS_ENUM(NSInteger, LayerState){
    LayerStateResting,
	LayerStatePickedUp,
};

@interface RTViewController ()

@property (nonatomic , strong) CALayer *topLayer;
@property (nonatomic , strong) CALayer *topLayer2;
@property (nonatomic, assign) LayerState currentLayerState;
@property (nonatomic, assign) CGPoint oldPoint;

@end

@implementation RTViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
	self.topLayer = [CALayer layer];
	UIImage *appleImage = [UIImage imageNamed:@"applications-internet_full.png"];
	self.topLayer.contents = (__bridge id)([appleImage CGImage]);
	CATransform3D perspectiveTransform = CATransform3DIdentity;
	perspectiveTransform.m34 = -1/500.f;
	self.view.layer.sublayerTransform = perspectiveTransform;
	self.topLayer.position = CGPointMake(130.f,250.f);
	self.topLayer.bounds = CGRectMake(0.f, 0.f, 200.f,200.f);
	[self.view.layer insertSublayer:self.topLayer above:self.view.layer];


	self.topLayer2 = [CALayer layer];
	UIImage *appleImage2 = [UIImage imageNamed:@"applications-internet_full.png"];
	self.topLayer2.contents = (__bridge id)([appleImage CGImage]);
	CATransform3D perspectiveTransform2 = CATransform3DIdentity;
	perspectiveTransform2.m34 = -1/500.f;
	self.view.layer.sublayerTransform = perspectiveTransform2;
	self.topLayer2.position = CGPointMake(130.f,250.f);
	self.topLayer2.bounds = CGRectMake(0.f, 0.f, 200.f,200.f);
	[self.view.layer insertSublayer:self.topLayer2 above:self.view.layer];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
	[super touchesBegan:touches withEvent:event];
	CGPoint location = [[touches anyObject] locationInView:self.view];
	self.oldPoint = location;

	if(CGRectContainsPoint(self.topLayer.frame, location))
	{
		self.currentLayerState = LayerStatePickedUp;
		[CATransaction begin];
		[CATransaction setAnimationDuration:.5f];
		self.topLayer.zPosition = 40.f;
		self.topLayer.transform = CATransform3DRotate(self.topLayer.transform, .1, 0.f, 1.f,0.f);
		[CATransaction setCompletionBlock:^{
			self.topLayer.zPosition = 40.f;
			self.topLayer.transform = CATransform3DRotate(self.topLayer.transform, .1, 0.f, 1.f,0.f);
		}];

		[CATransaction commit];

		CABasicAnimation *spinningAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
		[spinningAnimation setFromValue:@0];
		[spinningAnimation setToValue:@30];

		CABasicAnimation *spinningAnimation2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
		[spinningAnimation2 setFromValue:@0];
		[spinningAnimation2 setToValue:@30];

		CABasicAnimation *spinningAnimation3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
		[spinningAnimation2 setFromValue:@0];
		[spinningAnimation2 setToValue:@30];

		CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
		[animationGroup setAnimations:@[spinningAnimation,spinningAnimation2, spinningAnimation3]];
		animationGroup.duration = 5.f;
		animationGroup.repeatCount = 1000000;
		[self.topLayer addAnimation:animationGroup forKey:@"spinXandYZ"];
	}
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	[super touchesEnded:touches withEvent:event];
	if(self.currentLayerState == LayerStatePickedUp)
	{
		[CATransaction begin];
		[CATransaction setAnimationDuration:.5f];
		self.topLayer.zPosition = -40.f;
		[CATransaction setCompletionBlock:^{
			self.topLayer.zPosition = -40.f;
			self.topLayer.transform = CATransform3DIdentity;
		}];
		[CATransaction commit];
	}
	self.currentLayerState = LayerStateResting;
	[self.topLayer removeAllAnimations];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
	[super touchesEnded:touches withEvent:event];
	CGPoint location = [[touches anyObject] locationInView:self.view];
	if((self.currentLayerState == LayerStatePickedUp) && CGRectContainsPoint(self.topLayer.frame, location))
	{
		[CATransaction begin];
		[CATransaction setAnimationDuration:0.f];
		self.topLayer.position =  CGPointMake(self.topLayer.position.x + (location.x - self.oldPoint.x), self.topLayer.position.y + (location.y - self.oldPoint.y));
		[CATransaction commit];
		self.oldPoint = location;
	}
}

@end

Memory Tiles – iPad app

Simple high score iOS memory game for iPad – a sequence of tiles are played and the user has to repeat them for points.  It’s meant to improve short term memory and visuospatial memory. Not sure what to do with it so made it open source!  There are 6 levels, each level has 10 sequences and as the sequence number goes up the sequences get longer. You could find yourself memorizing up to 54 tiles in a row in the last level. The colors are randomly generated and scores are saved across instances of the application using NSUserDefaults. I think it could actually have some implications in memory enhancement and Alzheimer’s. If anyone wants to pursue that, please feel free.

Technologies: iOS 6, Xcode, PaintCode, core graphics, core animation, UIKit, FXLabel(https://github.com/nicklockwood/FXLabel)

https://github.com/RTimal/Memory-Tiles

photo