Blocks

Leave a comment

https://www.appcoda.com/objective-c-blocks-tutorial/

A block is a self-contained, autonomous code fragment.

They can be used instead of delegate methods, written just in one place and not spread to many files.

Blocks are objects, so they can be stored to NSArray or NSDictionary data structures, as well as to be returned from methods, even to be assigned to variables.

ReturnType (^blockName)(Parameters);
NSString *(^composeName)(NSString *, NSString *);

If you return a value, you need a return statement.

int (^howMany)(int, int) = ^(int a, int b){
    return a + b;
};

But if you return void no return statement if needed.

void (^justAMessage)(NSString *) = ^(NSString *message){
   NSLog(@"%@", message);
};

You can declare a block, and then define it later.

    // Declare a block variable.
    void (^xyz)(void);
    
    // Some other code...
    
    // Define the block.
    xyz = ^(void){
        NSLog(@"What's up, Doc?");
    };

You can also define it as a property.

@interface ViewController ()
@property (nonatomic, strong) NSString *(^blockAsAMemberVar)(void);
@end

And then formally define it later.

_blockAsAMemberVar = ^(void){
        return @"This block is declared as a member variable!";
};

You call a block just like a C function

int (^howMany)(int, int) = ^(int a, int b){
        return a + b;
};

    
NSLog(@"%d", howmany(5, 10));

Here’s another

    NSDate *(^today)(void);
    
    today = ^(void){
        return [NSDate date];
    };
    
    NSLog(@"%@", today());

And if you ever need to change a variable within a block, use the double underscore

-(void)testBlockStorageType{
    __block int someValue = 10;
    
    int (^myOperation)(void) = ^(void){
        someValue += 5;
        
        return someValue + 10;
    };
    
    NSLog(@"%d", myOperation());
}

Blocks as Completion Handlers

A completion handler is the way for implementing callbacks using blocks.
A completion handler is nothing more than a block declaration passed as a parameter.

andCompletionHandler:(void(^)())completionHandler;
andCompletionHandler:(void (^)(int result))completionHandler;

This flips the functionality of the paremeters from being one of inputs, to being one of outputs returned by the block.

andCompletionHandler:(void(^)(<any block params>))completionHandler{
    ...
    ...
 
    // When the callback should happen:
    completionHandler(<any required parameters>);
}

By passing it as a parameter, we can call it from within out method passing in the paremeters we want to return to the original calling method. We flip it around. So instead of using the parameters passed in, we set them and return them on when we call the block.

There is nothing magically asynchronous about blocks by themselves. They are called synchronously just like any other code.

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self addNumber:5 withNumber:7 andCompletionHandler:^(int result) {
        NSLog(@"The result is %d", result);
    }];
}

-(void)addNumber:(int)number1 withNumber:(int)number2 andCompletionHandler:(void (^)(int result))completionHandler{
    int result = number1 + number2;
    completionHandler(result);
}

But what’s cool about blocks. You can do asynchronous things by throwing them onto different threads.

NSLog(@"Preparing to run code in secondary thread...");
    dispatch_queue_t myQueue = dispatch_queue_create("My Queue", NULL);
    dispatch_async(myQueue, ^{
        NSLog(@"Running code in secondary thread...");
        
        int value = 0;
        for (int i=0; i<100; i++) {
            for (int j=0; j<100; j++) {
                for (int n=0; n<100; n++) {
                    value += j;
                }
            }
        }
        
        NSLog(@"From secondary thread: value = %d", value);
    });
    
    NSLog(@"This is main thread again...");

How to create custom UITableViewHeader programmatically using autolayout

Leave a comment

Create a new UITableViewCell for the header. Add your autolayout contraints. Then use in ViewController.

TrackHeaderCell.h

#import <UIKit/UIKit.h>

@interface TrackHeaderCell : UITableViewCell
@property (strong, nonatomic) UILabel *titleLabel;
@end

TrackHeaderCell.m

#import "TrackHeaderCell.h"

@interface TrackHeaderCell()

@end

@implementation TrackHeaderCell

#pragma mark - Creation and Layout

- (instancetype) init
{
    self = [super init];
    if (!self) return self;

    [self setupTitleLabel];

    return self;
}

- (void) updateConstraints
{
    [self addViewConstraints];
    [super updateConstraints];
}

- (void)addViewConstraints
{
    NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:self.titleLabel
                                                               attribute:NSLayoutAttributeCenterY
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self.contentView
                                                               attribute:NSLayoutAttributeCenterY
                                                              multiplier:1
                                                                constant:0];

    NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:self.titleLabel
                                                               attribute:NSLayoutAttributeCenterX
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self.contentView
                                                               attribute:NSLayoutAttributeCenterX
                                                              multiplier:1
                                                                constant:0];

    NSArray<NSLayoutConstraint *> *constraints = @[centerX, centerY];

    [NSLayoutConstraint activateConstraints:constraints];
}

- (void)setupTitleLabel
{
    self.titleLabel = [UILabel new];
    self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;

    [self.contentView addSubview:self.titleLabel];
}

@end

ViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];

    TrackHeaderCell *tableHeaderView = [TrackHeaderCell new];
    tableHeaderView.titleLabel.text = @"Results";
    self.tableView.tableHeaderView = tableHeaderView;
}

Screen Shot 2017-06-14 at 6.11.21 AM.png

How to deal with iMessage Navigation bar covering top of view bug

Leave a comment

iMessage has a bug where it doesn’t if you have something (like a search bar) pinned to the top of the view, it’s hides it when you transition from compact to expanded view.

imessage-navbar-bug.gif

The fix for now is to manually adjust the height on the top constraint so that when you are in expanded mode, you push it down beyond the navbar. Of course you will have to adjust this number slightly for older iPhones. But 88 seems standards for iPhone 6/7.

ViewController.m


@interface MessagesViewController ()
@property (nonatomic, strong) NSLayoutConstraint *topConstraint;
@end

-(void)willTransitionToPresentationStyle:(MSMessagesAppPresentationStyle)presentationStyle {
    // Called before the extension transitions to a new presentation style.
    
    // Use this method to prepare for the change in presentation style.

    switch (presentationStyle) {
        case MSMessagesAppPresentationStyleCompact: {
            self.topConstraint.constant = 0;
            [self.view setNeedsLayout];
            break;
        }
        case MSMessagesAppPresentationStyleExpanded: {
            self.topConstraint.constant = 88.0;
            [self.view setNeedsLayout];
            break;
        }

        default:
            break;
    }

}

How to set height programmatically autolayout

Leave a comment

You can set the height and width visually like this.

    NSArray *foo1 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[clearSearchButton(==searchBar)]" options:0 metrics:nil views:views];
    NSArray *foo2 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[clearSearchButton(==searchBar)]" options:0 metrics:nil views:views];

You can set it programmatically with a constraint like this

 NSLayoutConstraint *foo = [NSLayoutConstraint constraintWithItem:self.clearSearchButton
                                                                   attribute:NSLayoutAttributeHeight
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:self.searchBar
                                                                   attribute:NSLayoutAttributeHeight
                                                                  multiplier:1
                                                                    constant:0];

    [self.view addConstraint:foo];

Or You can set it to a specific height like this.

    [self.clearSearchButton addConstraint:[NSLayoutConstraint constraintWithItem:self.clearSearchButton
                                                     attribute:NSLayoutAttributeHeight
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:nil
                                                     attribute: NSLayoutAttributeNotAnAttribute
                                                    multiplier:1
                                                      constant:20]];

How to create a UITableViewCell and dequeue programatically

Leave a comment

If you ever need to create and dequeue a custom cell programatically (not use storyboards) do this

Screen Shot 2017-06-04 at 2.12.19 PM.png

Code

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellIdentifier = @"Cell";

    [tableView registerClass:[TrackCell class] forCellReuseIdentifier:cellIdentifier];
    TrackCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];


    Track *track = [self.tracks objectAtIndex:indexPath.row];

    cell.titleLabel.text = track.title;
    cell.artistLabel.text = track.artist;

    UIImage *albumImage = [UIImage imageNamed:track.albumImage];
    cell.imageView.image = albumImage;

    return cell;
}

How to UIStackView in UITableViewCell

Leave a comment

Create Single ViewController project. Add a UITableView.

Screen Shot 2017-06-04 at 8.31.52 AM.png

Pin it to the walls.

Screen Shot 2017-06-04 at 8.32.38 AM.png

Turn off the default separator.

Screen Shot 2017-06-04 at 8.33.14 AM.png

Drag out a UITableViewCell

Screen Shot 2017-06-04 at 8.34.10 AM.png

Add a UIImageView

Screen Shot 2017-06-04 at 8.35.22 AM.png

Set it’s height and width and position.

Screen Shot 2017-06-04 at 8.36.07 AM.png

Screen Shot 2017-06-04 at 8.36.20 AM.png

Now add x2 labels to the side.

Screen Shot 2017-06-04 at 8.37.08 AM.png

Now for the stackView. Select the two labels and then embed them in the stackView using the button at the bottom.

Screen Shot 2017-06-04 at 8.38.19 AM.png

Screen Shot 2017-06-04 at 8.38.36 AM.png

Now select the UIImageView and the new stackView and embed those in a stackview.

Screen Shot 2017-06-04 at 8.39.31 AM.png

Now here is the important bit. Right now you have constraint violations.

Screen Shot 2017-06-04 at 8.42.34 AM.png

The way to fix these is to pin the outermost UIStackView to the x4 edges.

Screen Shot 2017-06-04 at 8.43.25 AM.png

Screen Shot 2017-06-04 at 8.43.49 AM.png

This will line everything up nicely.

Now there is still a constraint violation. But it has nothing to do with stackview. It’s a hugging issue.

Screen Shot 2017-06-04 at 8.44.51 AM.png

Here it doesn’t know what priority to give vertical hugging for the x2 labels. Which one should expand to fill the height of the cell. They are both the same.

The way to resolve this is to change the hugging priority of one of the labels. Make the bottom one hug more than the top.

Select artist and set it’s vertical hug to 252.

Screen Shot 2017-06-04 at 8.49.10 AM.png

And voila! A stackview in a tableviewcell.

Screen Shot 2017-06-04 at 8.49.40 AM.png

Also you can give your view a 1:1 aspect ratio if you like.

Screen Shot 2017-06-04 at 8.56.06 AM.png

Links that help
https://developer.apple.com/videos/play/wwdc2015/407/
the 5min marks show you how to do this

How to UICollectionView

Leave a comment

Drag out Single View Controller. Add a CollectionView.

Screen Shot 2017-06-04 at 6.03.52 AM.png

Pin it to the walls.

Screen Shot 2017-06-04 at 6.04.36 AM.png

Implement the interfaces

Screen Shot 2017-06-04 at 6.05.36 AM.png

The create an data struct and fill it with data.

Screen Shot 2017-06-04 at 6.08.33 AM.png

Create a UICollectionViewCell.

Screen Shot 2017-06-04 at 6.10.25 AM.png

Add a label to the cell in the story board.

Screen Shot 2017-06-04 at 6.11.52 AM.png

Connect the cell in the storyboard to the UICollectionViewCell we just created.

Screen Shot 2017-06-04 at 6.14.36 AM.png

Give it an id.

Screen Shot 2017-06-04 at 6.14.50 AM.png

Hook it up to an IBOutlet.

Screen Shot 2017-06-04 at 6.15.47 AM.png

Instantiate and populate the cell in the delegate methods.

Screen Shot 2017-06-04 at 6.20.11 AM.png

Add the CollectionView as a property.

Screen Shot 2017-06-04 at 6.23.15 AM.png

Make yourself the delegate.

Screen Shot 2017-06-04 at 6.24.53 AM.png

Set yourself as the DataSource by control dragging from CollectionView to yellow ViewController in storyboard.

Screen Shot 2017-06-04 at 6.28.49 AM.png

Should now run and populate.

Screen Shot 2017-06-04 at 6.29.53 AM.png

To make the flow horizontal change the flow.

Screen Shot 2017-06-04 at 6.30.40 AM.png

And to make rows selectable implement.

Screen Shot 2017-06-04 at 6.34.40 AM.png

Which will print out.

Screen Shot 2017-06-04 at 6.32.43 AM.png

Optionally you can also implement these following methods for more layout control.

Screen Shot 2017-06-04 at 6.33.27 AM.png

Code

TrackCell.h

#import

@interface TrackCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UILabel *name;
@end

TrackCell.m

#import "TrackCell.h"
@implementation TrackCell
@end

ViewController.m

#import "ViewController.h"
#import "TrackCell.h"

@interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (nonatomic, strong) NSArray *tracks;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tracks = [NSArray arrayWithObjects:@"Egg Benedict", @"Mushroom Risotto", @"Full Breakfast", nil];

    self.collectionView.delegate = self;
}

#pragma mark - UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return self.tracks.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"TrackCell";

    TrackCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    cell.name.text = (NSString *)self.tracks[indexPath.row];
    return cell;
}

#pragma mark - UICollectionViewDelegate

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"%@", self.tracks[indexPath.row]);
}

@end

Older Entries

%d bloggers like this: