Showing posts with label obj c. Show all posts
Showing posts with label obj c. Show all posts

Thursday, 14 August 2014

iOS - iPhone Rotation, View Auto Resizing and Layout Handling

If you have ever developed an application using a user interface toolkit for a windowing based desktop system (be it Windows, Mac OS X, Linux, Qt or GTK+) you will likely be familiar with the need to design your user interfaces such that they resize appropriately when the user enlarges or reduces the size of a window. This generally involves deciding which visual components of a window move and which ones resize to accommodate the containing window.
Obviously iOS based devices such as the iPhone do not have windows that the user can resize in the conventional sense that we are used to in the world of desktop applications. The device is, however, sensitive to physical rotation, providing the options for a running application to be displayed in either portrait or landscape mode. Given this capability, it is essential that an iOS application user interface layout adapt accordingly should you decide to support different device orientations within your application.
In this chapter we will look at the mechanism for enabling rotation and modifying the layout of user interfaces within an application. In the course of this chapter we will be talking about concepts such as the view, superviews, subviews and view hierarchies. If these concepts are unfamiliar to you we recommend first reading the chapter entitled Understanding iOS 4 iPhone Views, Windows and the View Hierarchy before proceeding.

Setting up the Example

In the remainder of this chapter we will work through a self contained example. To set up this example, start Xcode and create a new iOS View-based iPhone project named layout.
From within Xcode, double click on the layoutViewController.xib file to launch Interface Builder and create a simple interface consisting of a single button object before saving the design and exiting from the tool.

Enabling Rotation

The iOS app templates provided by Xcode do not automatically assume that your application will sensibly support rotation. In fact, the opposite is assumed. In order to enable rotation, therefore, it is necessary to make a minor modification to the template code. Nor is it a given that every app should support rotation. If there is nothing to be gained in terms of the user experience by supporting both landscape and portrait layouts of your iPhone application’s user interface do not feel compelled to implement this capability.
For iPhone models with retina displays (starting with the iPhone 4) the overall window size is 960 pixels high by 640 pixels wide in portrait mode and 640 pixels high and 960 pixels wide in landscape. For older iPhone models the resolutions are 480 pixels high and 320 pixels wide for portrait and 320 pixels high by 480 pixels wide for landscape. Keep in mind that if the application displays the standard status bar the actual display space available will reduced accordingly.
When the device detects that it has been rotated to a new orientation a call is made to the shouldAutorotateToInterfaceOrientation method of the view controller instance. In order to implement rotation support it is necessary to override this method in the currently active view controller for your application (in the case of our example this is the layoutViewController class).
Fortunately, Xcode has placed a template method in our layoutViewController.m file which is currently commented out:
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
In order to use this method, begin by removing the comment open (/*) and close (*/) markers located the top and bottom of the method. The method takes as a single argument the new orientation value and returns either true or false depending on whether the application will allow the new orientation change to take place. In the template example above, the method only returns true if the orientation passed to the method is portrait (in other word it matches the value represented by UIInterfaceOrientationPortrait). The full range of supported orientations that can be passed through to this method are as follows:
  • UIInterfaceOrientationPortrait
  • UIInterfaceOrientationPortraitUpsideDown
  • UIInterfaceOrientationLandscapeRight
  • UIInterfaceOrientationLandscapeLeft
In order to support all orientations, and for the purposes of this example, simply modify the code in this method to return YES:
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    //return (interfaceOrientation == UIInterfaceOrientationPortrait);
        return YES;
}
Feel free to modify this method to meet your requirements in terms of the orientations you would like your own applications to support. Having made the change, build and run the application.

Testing Rotation Behavior

If you are testing your iOS applications natively on a physical iPhone to iPod Touch device, you can simply rotate the device to test the behavior of your application. If, on the other hand, you are running the application in the iOS Simulator, you can simulate rotation by selecting the Hardware -> Rotate Left and Hardware -> Rotate Right menu options.

Configuring View Autosizing

In order to make the handling of orientation changes a little easier to implement, the UIView class from which visual components are derived provides a feature called Autosizing. Autosizing allows us to define on a view by view basis how a subview will be resized and positioned relative to the superview in which it is contained. These settings can be defined programmatically in code or from within the Size Inspector of the Interface Builder tool during the user interface design process.
In order to demonstrate autosizing, double click on the layoutViewController.xib file to load it into Interface Builder, add an additional button to the interface and reposition both buttons so that they appear as outlined in the following figure:

Image:iphone_autosize_2_buttons.jpg

In order to gain an appreciation of the importance of handling orientation changes, save the design and build and run the application. Once the simulator appears, rotate the device by 90⁰. Because we have enabled rotation, the view will rotate to the landscape orientation:

An iOS 4 iPhone app in landscape mode with no autosizing

Here we see a classic example of the implications of enabling rotation in an application without taking the additional steps to reformat the view to match the change in orientation. Having rotated the device, only the top button is visible and it is still centered assuming that the device is in portrait mode. The second button is similarly positioned, resulting in it being outside the viewable area of the screen.
In order to ensure the correct layout of the user interface it is necessary to make some changes to the autosize settings of the two buttons. The current settings for a view are accessed from within Interface Builder by selecting the component and displaying the Size Inspector (Tools -> Size Inspector or Command+3). The following figure shows the Size Inspector for the top button in our user interface:

The Interface Builder Size Inspector window
The section of this window that is of interest to us is the area entitled Autosizing and in particular the white box on the left. The outer box represents the superview and the inner box represents the subview that is contained within the superview. Any autosizing settings that are configured on a subview are relative to the superview. Within the subview box are two intersecting lines with arrows at each end. These vertical and horizontal lines indicate whether the height and width of the selected view object should increase or decrease in proportion to size changes in the corresponding superview. A faint dotted line indicates that the currently selected view object is not to resize in relation to the superview. A solid red line, on the other hand, indicates that it should resize proportionally to the parent view. The graphic to the right of the white box provides a visual representation of how the view will behave based on the current settings. The red box represents the subview and the white box represents the containing superview. Click on the horizontal line to change it to solid red and see how the width of the red box representing the button grows and shrinks as the containing superview size changes. Similarly, enable resizing for the height of the button and observe how the height of the button also now changes proportionally to the superview:

The Interface Builder Size Inspector Autosizing section

These two settings correspond to the UIViewAutoResizingFlexibleHeight and UIViewAutoResizingFlexibleWidth autoresizing mask properties.
The lines separating the four sides of the inner subview and the outer superview boxes are referred to as struts and control what happens to the margin between each side of the subview and the corresponding sides of the superview when the latter resizes as the result of an orientation change. When a strut is enabled (indicated by the solid red lines) the corresponding margin remains fixed when the superview resizes. In the above example the top and left hand margins are fixed which explains why the position of the top left hand corner of the button does not move in the graphical representation when the superview resizes. When the strut is turned off (symbolized by the faint dotted lines) the margin changes proportionally to the change in size of the superview. Doing so with our example button will cause the subview to remain centered in the superview as illustrated in the following figure:

The Interface Builder Size Inspector Autosizing section

These struts correspond to the following autoresizing mask values:
  • UIViewAutoResizingFlexibleLeftMargin
  • UIViewAutoResizingFlexibleRightMargin
  • UIViewAutoResizingFlexibleTopMargin
  • UIViewAutoResizingFlexibleBottomMargin
When multiple autosize attributes are set on a single axis (for example flexible left and right margins combined with flexible width) the resizing is distributed evenly over each dimension.
In order to modify the resize behavior of the buttons in our example application we want to make sure that both buttons remain centered relative to the width of the superview, so we need to turn off all struts on both buttons. This will also have the effect of making the position of the bottom button relative to the bottom of the superview flexible so that it will be visible after a switch to landscape mode. Finally we want the width of the buttons to change in proportion to the width of the superview so need to turn on horizontal resizing for both buttons and vertical resizing off. With these changes made, compiling and running the application should result in the correct layout appearing when the device is rotated:

iPhone iOS 4 app buttons laid out correctly after using Autosizing

Coding Layout and Size Changes

Whilst the autosizing features will work for many situations there are some limitations. It is quite possible for example that you will want to change the entire layout of your user interface depending on whether the device is in portrait or landscape orientation. This might involve not only changing the size of view elements but also moving them to specific locations on the screen. Clearly this is beyond the capabilities of autosizing. A useful approach to this situation involves intercepting the rotation of the device and changing the position and dimensions of the user interface components before the screen is redrawn. In order to achieve this we will need to override the willAnimateRotationToInterfaceOrientation method of the UIViewController class. This is the method that gets called just before the user interface begins to rotate.
In order to demonstrate this technique in action we will use our existing layout project. Within Xcode, select the layoutViewController.h file and add the following outlets so that we can access our two buttons from the controller code:
#import <UIKit/UIKit.h>

@interface layoutViewController : UIViewController {
        UIButton *firstButton;
        UIButton *secondButton;
}
@property (nonatomic, retain) IBOutlet UIButton *firstButton;
@property (nonatomic, retain) IBOutlet UIButton *secondButton;
@end
Next, the layoutViewContoller.m implementation file needs to be edited to synthesize accesss to the outlets and to override the willAnimateRotationToInterfaceOrientation method:
#import "layoutViewController.h"

@implementation layoutViewController
@synthesize firstButton;
@synthesize secondButton;

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
        if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
                toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)
        {
                firstButton.frame = CGRectMake(20, 20, 210, 260);
                secondButton.frame = CGRectMake(250, 20, 210, 260);
        }
        else
        {
                firstButton.frame = CGRectMake(20, 20, 280, 205);
                secondButton.frame = CGRectMake(20, 233, 280, 207);
        }
}
.
.
- (void)viewDidUnload {
        // Release any retained subviews of the main view.
        // e.g. self.myOutlet = nil;
        self.firstButton = nil;
        self.secondButton = nil;
}
- (void)dealloc {
        [firstButton release];
        [secondButton release];
        [super dealloc];
}
@end
As mentioned previously, the willAnimateRotationToInterfaceOrientation method gets called just before the user interface rotation takes place. Passed through as an argument to this method is the new orientation of the device in the form of the toInterfaceOrientation variable. Within the body of the method we identify if the device has rotated to a landscape or portrait orientation. Using the CGRectMake method (which takes x, y, width and height as arguments) we create a CGRect structure containing the new co-ordinates and dimensions for each button and assign them to the frame property of the button instances. When the method returns, the system will then proceed to draw the buttons using the new frame information that was configured in the overridden method.
With the appropriate code changes made, return to the Interface Builder session for the layoutViewController.xib file and modify the user interface such that appears as follows:

iOs 4 iPhone layout in Interface Builder with two large buttons

The size and co-ordinates that we specified in our method are designed to position the buttons side by side when the device rotates into a landscape orientation and then back to the above layout when it returns to portrait. Before we try this out, however, we need to set up the connections from the buttons to the two outlets we created in the view controller. To achieve this, hold down the control key and click and drag from the File’s Owner icon to the top button. Release at this point and select firstButton from the resulting menu. Repeat this for the other button, selecting secondButton from the menu. Save the file and return to Xcode where you can build and run the application. Once the application is running, rotate the simulator window by selecting the Hardware -> Rotate Left menu option. The device window should rotate and the buttons appear side by side:

Buttons moved and resized in code after iPhone rotation

Rotating the device back to portrait orientation should trigger the second part of the if .. else .. construct in the overridden method, thereby returning the buttons to the original layout.

Wednesday, 13 August 2014

Protecting resources in iOS apps

If you are distributing an iPhone or iPad app you may be giving away more than you realize. Resources that you embed in your application bundle can be extracted very easily by anyone who has downloaded the app with iTunes or has the app on their iOS device. This is not such a problem if those resources are just parts of your UI or the odd NIB file, but once you realise that your high-value resources, such as embedded PDFs, audio files, proprietary datasets and high-resolution images are just as accessible then you might want to consider how to protect those resources from prying eyes or unauthorised use outside your application.
To see just how accessible your resources are, use a tool such as iPhone Explorer to connect to your device. This tool lets you browse all the app bundles on your device and copy any resource to your Mac or PC. Alternatively, just locate the ipa file that is downloaded when you purchase an app with iTunes, change the extension to ‘.zip’ then unzip it. All your resources are on display.

Protection through encryption

In this article I’m going to walk through one approach to protecting your resources from unauthorised use outside your application. During the build phase, your high-value resources will be encrypted before being embedded in the app bundle. These resources can then be decrypted on-the-fly when required in your app. I had a number of goals in mind when developing the solution presented in this article:
  • Adding new resources to be encrypted should be quick and easy.
  • The encryption process should be completely automatic during the build phase.
  • Encrypted PDFs or HTML files must be useable in an embedded UIWebView
  • Decryption must be in-memory and not create temporary unprotected files
I also did not want to complicate the submission process by introducing encryption that might be subject to export restrictions, so we are limiting ourselves to using the CommonCrypto API, which is already distributed as part of iOS. The usual caveats apply when using any sort of encryption approach – given enough time and determination someone willbypass any protection scheme you can come up with. However, make it difficult enough to crack the protection compared to the value of the protected resources and it becomes a question of economics. If you are working with client-supplied resources then it becomes even more important to use a scheme such as the one described here to protectyourself from charges of negligence should those resources turn up on some torrent site.

An encryption command-line tool

So, down to the details. We’re going to create a simple command-line tool that will be run as part of the build process to encrypt resources using the AES256 symmetric cipher. An Xcode project to build the tool can be found at the bottom of this article. The tool takes command-line arguments specifying a 256-bit key, the input file path and an output file path. We will use a custom build step to call the command-line tool for each of our resources that we want to be encrypted.

Setting up our project


XCode project showing EncryptedResources folder and custom build step
Now we have a tool to perform the encryption, we can turn to an example project that makes use of the encryption to protect embedded resources. You can find an example Xcode project at the end of this article. This sample simply displays a UIWebView when run and populates it with protected HTML and image files, which are embedded as encrypted resources in the app.
The first step is to create a sub-folder under the project folder to contain the original resources that we want to protect. In the example we have called this sub-folder ‘EncryptedResources’. One of our goals was to make adding new resources as quick and easy as possible and when we have finished setting up the project we will be able to add a new protected resource simply by dragging it into this folder within Finder. As a cosmetic convenience I also added the folder to the Xcode project as a folder reference (by checking the ‘Create Folder References for any added folders’ checkbox) but please remember to deselect any target membership on the ‘Targets’ tab of the Info dialog for this folder or the unencrypted resources will be added to the app bundle by the regular ‘Copy Bundle Resources’ build step.

Adding a custom build step

To process the files in the EncryptedResources folder, we add a custom build step by selecting the ‘Project > New Build Phase > New Run Script Build Phase’ menu item in Xcode. In the example we have moved this build step to the start of the project target (under the Targets > EncryptedResourceDemo item in the ‘Groups and Files’ list) so that it is run as the first step in the build process. The shell script associated with this step can be viewed by selecting the ‘File/Get Info’ menu item when the step is selected:
DIRNAME=EncryptedResources
ENC_KEY="abcdefghijklmnopqrstuvwxyz123456"
 
INDIR=$PROJECT_DIR/$DIRNAME
OUTDIR=$TARGET_BUILD_DIR/$CONTENTS_FOLDER_PATH/$DIRNAME
 
if [ ! -d "$OUTDIR" ]; then
  mkdir -p "$OUTDIR"
fi
 
for file in "$INDIR"/*
do
  echo "Encrypting $file"
  "$PROJECT_DIR/crypt" -e -k $ENC_KEY -i "$file" -o "$OUTDIR/`basename "$file"`"
done
The shell script iterates over every file in the ‘EncryptedResources’ folder and calls our ‘crypt’ tool for each file, placing the encrypted output in the application bundle. Note that the script expects the ‘crypt’ tool to be present in the base of the project directory. As an alternative, you could place the ‘crypt’ tool somewhere on your path and modify the script accordingly. Note also, that it is in the build script where we specify the key to use for encryption. The 256-bit key is specified as 32 x 8-bit characters – you should change it from the default given here.

Using protected resources in applications

So now we have a built app bundle containing our protected resources. The resources can still be viewed with tools such as iPhone Explorer or extracted from an iTunes ipa file but are useless without the correct decryption key. We now turn to how these resources can be used legitimately by your app. In creating a decryption framework, I wanted a scheme that would be as flexible as possible and not place an unnecessary burden on the developer every time she wanted to use a protected resource. I also needed a scheme that decrypted in memory and did not create temporary files (which could be viewed with iTunes Explorer while an app was running). I chose to create a custom URL protocol and extend the URL loading system. This allows us to use encrypted resources anywhere that a URL can be specified and, thanks to the widespread use of URLs in the iOS frameworks, we get a lot of bang for our buck with relatively little new code including:
  • Loading encrypted resources into memory with [NSData dataWithContentsOfURL:]
  • Viewing encrypted HTML files and images in UIWebView

Custom URL protocols

If you haven’t come across implementing custom URL protocols before and you are interested in finding out more then check out the iPhone Reference Library. By subclassing NSURLProtocol and implementing a few required methods we can grant the URL loading system the ability to load other types of resources beyond the standard built-in schemes (http:https:ftp: and file:).
Our NSURLProtocol subclass is called EncryptedFileURLProtocol and the implementation can be found under the EncryptedFileURLProtocol group in the example project. It implements a new URL scheme (encrypted-file:) that works like the standard file: scheme. A URL using this scheme specifies a file on the local system just like file:, however, the files will be decrypted on-the-fly when the resource is loaded.
A custom NSURLProtocol subclass must override the canInitWithRequest:. The URL loading system uses this method to determine which protocol can handle a particular request. The loading system asks each registered protocol in turn whether it can handle the specified request. The first protocol to return YES gets to handle the request. The implementation of our canInitWithRequest: method is shown below and will return YES if the requested URL scheme is ‘encrypted-file:’.
33
34
35
36
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    return ([[[request URL] scheme] isEqualToString:ENCRYPTED_FILE_SCHEME_NAME]);
}
A custom NSURLProtocol must also override two additional methods, startLoading and stopLoading. These are called by the URL loading system at appropriate points when handling a request. Our startLoading method initializes the cryptographic engine and opens an NSInputStream to load the resource. The decryption key is also specified at this point. It is shared between all instances of our custom NSURLProtocol and needs to match the key used for encryption during the build process. The default key value is specified on line 20 of EncryptedFileURLProtocol.m and can be modfied using the class-level key property.
58
59
60
61
62
63
64
65
66
67
68
- (void)startLoading
{
    inBuffer = malloc(BUFFER_LENGTH);
    outBuffer = malloc(BUFFER_LENGTH);
    CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, [sharedKey cStringUsingEncoding:NSISOLatin1StringEncoding], kCCKeySizeAES256, NULL, &cryptoRef);
 
    inStream = [[NSInputStream alloc] initWithFileAtPath:[[self.request URL] path]];
    [inStream setDelegate:self];
    [inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inStream open];
}
The stopLoading method is called when the request ends, either due to normal completion or an error condition. In our implementation we clean up the input stream, cryptographic engine and buffers:
71
72
73
74
75
76
77
78
79
80
- (void)stopLoading
{
    [inStream close];
    [inStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [inStream release];
    inStream = nil;
    CCCryptorRelease(cryptoRef);
    free(inBuffer);
    free(outBuffer);
}
Because we have scheduled our input stream on the current run loop and specified our custom instance of NSURLProtocol as the stream delegate, we will be called periodically with chunks of data to process. Below is an extract from the stream event handler where a chunk of data read from the input stream is decrypted and passed on to the URL loading system:
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  switch(streamEvent) {
        case NSStreamEventHasBytesAvailable:
        {
            size_t len = 0;
            len = [(NSInputStream *)inStream read:inBuffer maxLength:BUFFER_LENGTH];
            if (len)
            {
                // Decrypt read bytes
                if (kCCSuccess != CCCryptorUpdate(cryptoRef, inBuffer, len, outBuffer, BUFFER_LENGTH, &len))
                {
                    [self.client URLProtocol:self didFailWithError:[NSError errorWithDomain:ERROR_DOMAIN code:DECRYPTION_ERROR_CODE userInfo:nil]];
                    return;
                }
 
                // Pass decrypted bytes on to URL loading system
                NSData *data = [NSData dataWithBytesNoCopy:outBuffer length:len freeWhenDone:NO];
                [self.client URLProtocol:self didLoadData:data];
            }
            break;
        }
        ...
Implementing these four methods is nearly all we need to do to leverage the power of the URL loading system. The final piece of the puzzle is to register our custom NSURLProtocol subclass with the system. This is done in theapplication:didFinishLaunchingWithOptions: method of our application delegate (in EncryptedResourceDemoAppDelegate.m):
30
31
    // Register the custom URL protocol with the URL loading system
    [NSURLProtocol registerClass:[EncryptedFileURLProtocol class]];

Using the encrypted-file: URL scheme

Now that we have implemented our custom NSURLProtocol subclass and registered it with the URL loading system we can start using URLs that use the encrypted-file: scheme. The example application demonstrates one way to do this – that is to use the scheme to load protected resources into a UIWebView instance. This is actually not very different from using a regular file: URL to load and view an embedded HTML resource. The code from the viewDidLoadmethod of our main view controller is shown below (from EncryptedResourceDemoViewController.m):
21
22
23
24
25
26
27
28
29
30
31
- (void)viewDidLoad {
    [super viewDidLoad];
 
    webView.scalesPageToFit = YES;
 
    NSString *indexPath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"EncryptedResources"];
 
    NSURL *url = [NSURL encryptedFileURLWithPath:indexPath];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];
}
The app bundle is queried for the path to ‘index.html’ in the EncryptedResources sub-folder. This is then used to construct an NSURL. An NSURLRequest is created from the URL and a loadRequest: message is sent to the web view. If you are familiar with NSURL and its class methods then you may have spotted the unfamiliarencryptedFileURLWithPath: method. I have extended NSURL using a category to add this method as a convenience. It works just like fileURLWithPath: but creates a URL using the encrypted-file: scheme rather than the regular file: scheme. One cool benefit of extending the URL loading system is that any relative URLs referenced in the HTML, such as the src parameter of IMG elements, will also use the encrypted-file: protocol and will be decrypted on-the-fly.
As mentioned above, URLs are used in many places in the iOS frameworks. To load an encrypted resource into memory you can do the following:
    NSString *indexPath = [[NSBundle mainBundle] pathForResource:@"..." ofType:@"..." inDirectory:@"EncryptedResources"];
 
    NSURL *url = [NSURL encryptedFileURLWithPath:indexPath];
    NSData *data = [NSData dataWithContentsOfURL:url];
This could used to create a UIImage from an encrypted image file using [UIImage initWithData:] or you could go one step further by extending UIImage using categories to implement a initWithContentsOfEncryptedFile:method.

Next steps

In this article I have presented a scheme for protecting the resources that you embed in your iPhone and iPad applications. Along the way we have also learned about how to use the CommonCrypto API and how to implement a custom URL protocol. The example project demonstrates how to use the scheme and you are free to use the ‘crypt’ command-line tool and EncryptedFileURLProtocol source in your own projects. Something you might want to think about (depending on the value of the resources you are trying to protect and your level of paranoia) is a mechanism for obfuscating the decryption key. With the current scheme the key is compiled into the application binary as a plain string and could be extracted by anyone with a hex editor, a little patience and a little knowledge. Of course, this assumes that they have worked out that the resources are encrypted using AES256.
We’d love to hear if you have found this article useful or even used it in your own project.

203 Favorite JavaScript Utilities

https://1loc.dev/