Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Build a Photo Browser iPhone App UIKit Dynamics Extra Credit

Add "Done" UIButton in navigation bar programatically

Since I am experiencing issues trying to prioritize ui items from the view hierarchy in the Interface Builder, and that I am not getting any convincing answers from our community (see: https://teamtreehouse.com/forum/uibutton-not-showing-on-uicollectionviewcontroller ) I am looking to find a solution to add a simple "Done" button inside my navigation bar in a programmatic way.

To respect the MVC model, I created a subclass of UIView specifically to add this "Done" UIButton.

Here is my code:

#import "THUIControlsViewForTHFriendsViewController.h"


@interface THUIControlsViewForTHFriendsViewController ()

@property (nonatomic) UIButton *doneButton;

@end



@implementation THUIControlsViewForTHFriendsViewController

#pragma mark - Actions

- (void)done:(id)sender  {

  //done method should transition to THPhotosPostedByFriendsViewController UICollectionViewController

}



#pragma mark - UIView

- (instancetype)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        [self addSubview:self.doneButton];

    }
    return self;
}



#pragma mark - UIControls


- (UIButton *)doneButton {
    if (!_doneButton) {
        _doneButton = [[UIButton alloc] initWithFrame:CGRectMake(270.0f, 10.0f, 40.0f, 32.0f)];
        _doneButton.titleLabel.font = [UIFont boldSystemFontOfSize:14.0f];
        _doneButton.titleLabel.text = @"Done";
        _doneButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;

        UIColor *textColor = [[self class] magicTextColor];
        [_doneButton setTitleColor:textColor forState:UIControlStateNormal];
        [_doneButton setTitleColor:[textColor colorWithAlphaComponent:0.5f] forState:UIControlStateHighlighted];
        [_doneButton addTarget:self action:@selector(done:) forControlEvents:UIControlEventTouchUpInside];
    }
    return _doneButton;
}

#pragma mark - Private

+ (UIColor *)magicTextColor {
    return [UIColor colorWithRed:0 green:0.4 blue:1 alpha:0.6];
}


@end

Note that the done method is not coded yet. I'll work on it later. My question pertains to displaying the button only.

Here is the code I wrote (building on the photo bombers code) to try to implement the doneButton in my UICollectionView subclassed controller:

#import "THFriendsViewController.h"
#import "THFriendPhotoCell.h"
#import <SimpleAuth/SimpleAuth.h>
#import "THUIControlsViewForTHFriendsViewController.h"

@interface THFriendsViewController () <UIViewControllerTransitioningDelegate>
@property (nonatomic) NSString *accessToken;
@property (nonatomic) NSArray *friends;
@property (nonatomic) BOOL loading;
@property (nonatomic) THUIControlsViewForTHFriendsViewController *uiControls;

@end

@implementation THFriendsViewController


- (void)setLoading:(BOOL)loading{
    _loading = loading;

    self.navigationItem.rightBarButtonItem.enabled = !_loading;
}

- (instancetype)init {
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    layout.itemSize = CGSizeMake(106.0,106.0);
    layout.minimumInteritemSpacing = 1.0;
    layout.minimumLineSpacing = 1.0;

    return (self = [super initWithCollectionViewLayout:layout]);
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.title = @"Gistagram";

    // TODO: Add a refresh right bar button item

    [self.collectionView registerClass:[THFriendPhotoCell class] forCellWithReuseIdentifier:@"photo"];
    self.collectionView.backgroundColor = [UIColor whiteColor];

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    self.accessToken = [userDefaults objectForKey:@"accessToken"];

    if (self.accessToken == nil) {
        [SimpleAuth authorize:@"instagram" options:@{@"scope": @[@"likes"]} completion:^(NSDictionary *responseObject, NSError *error) {

            self.accessToken = responseObject[@"credentials"][@"token"];

            [userDefaults setObject:self.accessToken forKey:@"accessToken"];
            [userDefaults synchronize];

            [self refreshFriends];
        }];
    }else {
        [self refreshFriends];
    }

    self.uiControls = [[THUIControlsViewForTHFriendsViewController alloc] initWithFrame:CGRectMake(270.0f, 10.0f, 40.0f, 32.0f)];
    self.uiControls.alpha = 0.0f;
    [self.view addSubview:self.uiControls];
}

-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    [UIView animateWithDuration:0.3 animations:^{
        self.uiControls.alpha = 1.0f;
    } completion:nil];

}


#pragma mark - Helper Methods

- (void)refreshFriends {

    if (self.loading) {
        return;
    }

    self.loading = YES;


    NSURLSession *session = [NSURLSession sharedSession];
    NSString *urlString = [[NSString alloc] initWithFormat:@"https://api.instagram.com/v1/users/self/follows?count=-1&access_token=%@", self.accessToken]; //Get avatar pictures of follows from Instagram


    NSURL *url = [[NSURL alloc] initWithString:urlString];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {

        NSData *data = [[NSData alloc] initWithContentsOfURL:location];
        NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
        //NSLog(@"Response %@", responseDictionary);

        self.friends = [responseDictionary valueForKeyPath:@"data"];

        dispatch_async(dispatch_get_main_queue(), ^{
            [self.collectionView reloadData];
            self.loading = NO;
        });

        NSLog(@"Data on friends: %@", self.friends);
    }];
    [task resume];
}

- (NSInteger)collectionView:(UICollectionView *) collectionView numberOfItemsInSection:
(NSInteger)section {

    NSInteger mediaCount = [self.friends count];
    NSLog(@"Media count: %ld", (long)mediaCount);
    NSLog(@"Data on friends: %@", self.friends);
    return mediaCount;

}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                 cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    THFriendPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"photo" forIndexPath:indexPath];

    cell.backgroundColor = [UIColor colorWithRed:0 green:0.4 blue:1 alpha:0.4];
    cell.photo = self.friends[indexPath.row];

    return cell;

}

@end

When I run the code, the build suceeds. However, I still don't see my "Done" button in my navigation bar.

I am a beginner coder, in full iOS learning curve and would appreciate any possible help from the community.

Thanks!

2 Answers

Stone Preston
Stone Preston
42,016 Points

you should be able to add a simple barbuttonItem to your navigation bar like so:

UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donePressed)];
self.navigationItem.leftBarButtonItem = doneButton;

and implement the donePressed method to do whatever you need done.

have you tried doing something like that?

Stone: Thanks for your help.

Couple of things I'd still need your help on (excuse my limited skills):

1) Not so sure where to insert your code, I tried that but I get errors:

#import "THFriendsViewController.h"
#import "THFriendPhotoCell.h"
#import <SimpleAuth/SimpleAuth.h>
#import "THUIControlsViewForTHFriendsViewController.h"

@interface THFriendsViewController () <UIViewControllerTransitioningDelegate>
@property (nonatomic) NSString *accessToken;
@property (nonatomic) NSArray *friends;
@property (nonatomic) BOOL loading;
//@property (nonatomic) THUIControlsViewForTHFriendsViewController *uiControls;
@property (nonatomic) UIBarButtonItem *doneButton;

@end

@implementation THFriendsViewController


- (void)setLoading:(BOOL)loading{
    _loading = loading;

    self.navigationItem.rightBarButtonItem.enabled = !_loading;
}

- (instancetype)init {
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    layout.itemSize = CGSizeMake(106.0,106.0);
    layout.minimumInteritemSpacing = 1.0;
    layout.minimumLineSpacing = 1.0;

    return (self = [super initWithCollectionViewLayout:layout]);
}

- (void)viewDidLoad {
    [super viewDidLoad];

    self.title = @"Gistagram";

    // TODO: Add a refresh right bar button item

    [self.collectionView registerClass:[THFriendPhotoCell class] forCellWithReuseIdentifier:@"photo"];
    self.collectionView.backgroundColor = [UIColor whiteColor];

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    self.accessToken = [userDefaults objectForKey:@"accessToken"];

    if (self.accessToken == nil) {
        [SimpleAuth authorize:@"instagram" options:@{@"scope": @[@"likes"]} completion:^(NSDictionary *responseObject, NSError *error) {

            self.accessToken = responseObject[@"credentials"][@"token"];

            [userDefaults setObject:self.accessToken forKey:@"accessToken"];
            [userDefaults synchronize];

            [self refreshFriends];
            [self displayDoneButtonInNav];
        }];
    }else {
        [self refreshFriends];
        [self displayDoneButtonInNav];
    }

//    self.uiControls = [[THUIControlsViewForTHFriendsViewController alloc] initWithFrame:CGRectMake(270.0f, 10.0f, 40.0f, 32.0f)];
//    self.uiControls.alpha = 0.0f;
//    [self.view addSubview:self.uiControls];
//}
//
//-(void)viewDidAppear:(BOOL)animated {
//    [super viewDidAppear:animated];
//
//    [UIView animateWithDuration:0.3 animations:^{
//      self.uiControls.alpha = 1.0f;
//  } completion:nil];
//    
//}

#pragma mark - UI Controls

    -(void)displayDoneButtonInNav {

        _doneButton = doneButton;

        self._doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(donePressed)];
        self.navigationItem.rightBarButtonItem = doneButton;

    }

#pragma mark - Actions

    -(void)donePressed {
        //segue to other VC
    }


#pragma mark - Helper Methods

- (void)refreshFriends {

    if (self.loading) {
        return;
    }

    self.loading = YES;


    NSURLSession *session = [NSURLSession sharedSession];
    NSString *urlString = [[NSString alloc] initWithFormat:@"https://api.instagram.com/v1/users/self/follows?count=-1&access_token=%@", self.accessToken]; //Get avatar pictures of follows from Instagram


    NSURL *url = [[NSURL alloc] initWithString:urlString];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {

        NSData *data = [[NSData alloc] initWithContentsOfURL:location];
        NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
        //NSLog(@"Response %@", responseDictionary);

        self.friends = [responseDictionary valueForKeyPath:@"data"];

        dispatch_async(dispatch_get_main_queue(), ^{
            [self.collectionView reloadData];
            self.loading = NO;
        });

        NSLog(@"Data on friends: %@", self.friends);
    }];
    [task resume];
}

- (NSInteger)collectionView:(UICollectionView *) collectionView numberOfItemsInSection:
(NSInteger)section {

    NSInteger mediaCount = [self.friends count];
    NSLog(@"Media count: %ld", (long)mediaCount);
    NSLog(@"Data on friends: %@", self.friends);
    return mediaCount;

}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                 cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    THFriendPhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"photo" forIndexPath:indexPath];

    cell.backgroundColor = [UIColor colorWithRed:0 green:0.4 blue:1 alpha:0.4];
    cell.photo = self.friends[indexPath.row];

    return cell;

}

@end

2) The "donePressed" method that I want to implement is simply a segue to another VC. Can you please confirm the following:

a) You always need to use the storyboard (control/drag from "Done" button to destination VC). You cannot implement the segue solely programmatically. b) Even if you create the segue visually with the storyboard, and assign it an identifier E.g: donePressed in this case, do you still need to code the transition from the source VC to the destination VC with "prepareForSegue"?

Your help is much appreciated.