Monday, February 24, 2014

Tuan's tips

1. Animation with Button
    UIButton *button = sender;
    CGRect frame = button.frame;
    frame.origin.x = arc4random() % (int)self.view.frame.size.width;
    frame.origin.y = arc4random() % (int)self.view.frame.size.height;
    NSLog(@"%f - %f", frame.origin.x, frame.origin.y);
    
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:0.5];
    button.frame = frame;

    [UIView commitAnimations];

2. Random number
arc4random() % NUMBER;

3. Hide the top status bar
In Plist, add the following properties
Status bar is initially hidden = YES
View controller-based status bar appearance = NO

For Storyboard view, set Status Bar property in Attribute List to None

4. Using UITableViewController
At header file, we need to extend UITableViewDelegate

Reimplement delegate methods:
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView{}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{}

-(UITableViewCell *)tableView(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{}

Connect view controller with Controller's delegate and dataSource

Check if Cell Identifier is correct

Check if cell is nil

We can customise UITableViewCell by subclassing it

5. Uknown class TT in Interface Builder file
This is normally caused by referencing a class in Sotryboard but it does not exist. Normally caused by mistakenly type into a view or subview instead of the view controller.

6. Max and Min
Use fmax and fmin

7. Segue and handlings
Here is something very useful about Segue and handlings
At this moment, I know of 2 types of Segues: Modal and Push

a. Modal is a nice and simple navigation mechanism, we just need to control-drag controller to another and select (Manual) Modal. Give that segue a name @"modalSegue" then call it anywhere we want.
[self performSegueWithIdentifier:@"showScore" sender:self];

Note that, the downside for Model Segue is that we can't pass values along this segue. And that's what Push Segue is for.

b. Push segue requires a little more effort to set up, that is (2 clicks away), we need a NavigationController sitting at the bottom level (of the stack).
Use the same mechanism to connect: from NavController to first view controller using: control-drag and select Root View controller. Then second to third and anywhere else follows the same method.

Pushing one controller to another we can use:
[self performSegueWithIdentifier:@"showSecond" sender:sender];
passing with data using:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifierisEqualToString:@"showSecond"])
    {
        TTSecondViewController *translationQuizAssociateVC = [segue destinationViewController];
        translationQuizAssociateVC.text = @"Hello Segue"//--pass nodeID from ViewNodeViewController
    }
}

Popping back to other view controller using controller collections:
- (IBAction)backRoot:(id)sender {
    [self.navigationController popToRootViewControllerAnimated:YES];
}

- (IBAction)backOne:(id)sender {
//    [self.navigationController popViewControllerAnimated:YES];
    NSArray *navs =  [self.navigationController viewControllers];
    TTSecondViewController *controller = navs[navs.count - 2];
    controller.text = @"Count Me";
    [self.navigationController popToViewController:controller animated:NO];
}

That's all for now.

One more thing, if you want to hide the navigation controller, use this:
[[self navigationControllersetNavigationBarHidden:YES];

Yes that's all.

7. Using NSUserDefaults to store temporary data
This is useful for storing data on the device without any cloud server.

Use it like any other storing, set/get values like a dictionary and save with synchronise
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
    BOOL initiated = [[defaults objectForKey:@"initiated"boolValue];
    
    if(!initiated){
        [defaults setObject:[NSNumber numberWithInt:0forKey:@"bestScore"];
        [defaults setObject:[NSNumber numberWithInt:0forKey:@"bestQuestions"];
        [defaults setBool:YES forKey:@"initiated"];
        [defaults synchronize];
    }
    else{
        NSLog(@"Game initiated");

    }

Then values can be retrieved with 
bestScore = [[defaults objectForKey:@"bestScore"intValue];

8. Launch Image and Icons
Firstly, use Paint.Net, very good and free tool to edit png images (ios uses this format)

For Icons, there are many good icons on:
http://pixabay.com/

For Launch image, follow this guide to provide 3 png images http://www.idev101.com/code/User_Interface/launchImages.html
Default.png                320x480
Default@2x.png         640x960
Default-568h@2x.png 640x1136

For Portrait mode, it's quite straightforward, add these 3 images in, use either Assets Catalogue or manually adding them in.

For showing launch image in Landscape mode is a bit tricky. ios only allow Portrait launch image on iphone (on ipad both are allowed).
So  the trick is to first only allow Portrait orientations for the app. But then, as soon as the launch image finish its job, you can go ahead and change orientation to Landscape. In your delegate.m, add this:

- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
    return UIInterfaceOrientationMaskLandscape;
}

For any View Controller that you want specific Orientation to be displayed, add this:

- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}

That's all for now.

9. Add sound to app
NSString *path = [[NSBundle mainBundle]pathForResource:@"NameOfSoundfile" ofType:@"mp3"];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[audioPlayer play];
10. How to upload iOS App to AppStore
a. Create Certificates for Developer and Distribution for the App (in iOS Developer Member center)
b. Create App ID for the App, using
c. Create Provisioning Profile for Developer and Distribution for Certificate and App ID


d. Go to iTunes Store to create new App, using the App ID created before
https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/wo/2.0.9.25.3.0.0.1.1.3
e. Sync provisioning profile with Xcode:
Xcode -> Preferences -> Accounts -> Apple IDs -> Login and View Details
f. Codesign in Targets -> Build Settings -> Code Signing
Select Provisioning Profile
Code Signing Identity: Debug -> iOS Developer Tuan Hue Thi, Release -> Distribution
g. Projects -> Skip Install = YES
Targets -> Skip Install = NO
h. No architectures to compile for (ARCHS=armv7, VALID_ARCHS=arm6 arm7). warning: all apps should include an armv7 architecture (current ARCHS = "").
Targets -> Architecture -> Build Target Architecture Only -> Debug, Release = NO
i. Choose Device, not Simulator -> Click Archive
j. Go to Organiser -> Archive -> Validate + Distribute

That's it for now.

11. Set NavigationBar to be hidden
In ViewController.m
[self.navigationController setNavigationBarHidden:YES];
In Storyboards, Atribute Properties, set Top Bar to None

12. Read/Write files in Xcode

Read and Write Collections to File

The process is straightforward:
- Create the NSArray and NSDictionary objects
- Obtain the path to write/read the files (application Documents directory)
- Append filenames to path for each collection type
- Use the ‘writeToFile:’ method of each object to save contents to file
NSString  *arrayPath;
NSString  *dictPath;
NSArray *array = @[@"IPA", @"Pilsner", @"Stout"];
 
NSDictionary *dictionary = @{@"key1" : array,
                             @"key2" : @"Hops",
                             @"key3" : @"Malt",
                             @"key4" : @"Yeast" };
 
// Get path to documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, 
  NSUserDomainMask, YES);
 
if ([paths count] > 0)
{
  // Path to save array data
  arrayPath = [[paths objectAtIndex:0] 
      stringByAppendingPathComponent:@"array.out"];
 
  // Path to save dictionary
  dictPath = [[paths objectAtIndex:0] 
      stringByAppendingPathComponent:@"dict.out"];
 
  // Write array
  [array writeToFile:arrayPath atomically:YES];
 
  // Write dictionary
  [dictionary writeToFile:dictPath atomically:YES];
}

Read NSArray NSDictionary NSSet From File

The code to read back from the collections is equally as simple, using the methods arrayWithContentsOfFile: and dictionaryWithContentsOfFile: read from the file paths created previously to read the save data:
// Read both back into new NSArray and NSDictionary object
NSArray *arrayFromFile = [NSArray arrayWithContentsOfFile:arrayPath];
NSDictionary *dictFromFile = [NSDictionary dictionaryWithContentsOfFile:dictPath];
 
// Print the contents
for (NSString *element in arrayFromFile) 
  NSLog(@"Beer: %@", element);
 
for (NSString *key in dictFromFile) 
  NSLog(@"%@ : %@", key, [dictionary valueForKey:key]);
The output in the console window is below:
Beer: IPA
Beer: Pilsner
Beer: Stout
key1 : (
    IPA,
    Pilsner,
    Stout
)
key2 : Hops
key3 : Malt
key4 : Yeast
13. Class and constructors
http://rypress.com/tutorials/objective-c/classes.html

14.