Recent Updates Toggle Comment Threads | Keyboard Shortcuts

  • Jay 11:57 am on February 3, 2015 Permalink | Reply
    Tags:   

    How to implement context menus for cut/copy/paste in a UICollectionView 

    Screen Shot 2015-02-03 at 11.56.47

    By default a collection view cell is implementing a long press gesture recogniser. If activated the cell will bring up the familiar context menu for cut, copy and paste. It’s up to us to enable it, and it’s also up to us how to react to this menu. Here’s how we can do that.

    The following three methods are provided as commented stubs in the CollectionViewController template.

    Showing the Menu

    First we need to tell our class that the menu is to be shown:

    - (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    	return YES;
    }
    

    Returning YES here will show the menu for every cell, but since we also get an indexPath, we could make decisions based on which cell has been tapped and make the menu show up conditionally.

    Next we need to decide which items we want to show:

    - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
        
    	return YES;
    }
    

    Returning YES here will show cut, copy and paste. We can make this conditional too, based both in the indexPath for each cell, but also based on the action parameter. Here’s a variation which will only show cut and paste, but not copy:

    - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
        
        // no copy option please
        if ([NSStringFromSelector(action) isEqualToString:@"copy:"]) {
            return NO;
        }
        
    	return YES;
    }
    

    Menu Actions

    It’s up to us what happens when either of those menu items is selected by the user. We can either use the UIPasteboard to retrieve and store our values, or create a property and store out entire object in it. I only have an NSString value per cell and will use the UIPasteboard.

    This method is called when the user taps a menu item:

    - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
        
        // Cut
        if ([NSStringFromSelector(action) isEqualToString:@"cut:"]) {
            
            // grab our value
            NSString *value = [self.cellData objectAtIndex:indexPath.row];
            
            // copy object to pasteboard
            [UIPasteboard generalPasteboard].string = value;
            
            // remove item
            [self.cellData removeObjectAtIndex:indexPath.row];
        }
        
        // Copy
        if ([NSStringFromSelector(action) isEqualToString:@"copy:"]) {
    
            // grab our value
            NSString *value = [self.cellData objectAtIndex:indexPath.row];
            
            // copy object to pasteboard
            [UIPasteboard generalPasteboard].string = value;
    
        }
        
        // Paste
        if ([NSStringFromSelector(action) isEqualToString:@"paste:"]) {
            
            // grab our value
            NSString *value = [UIPasteboard generalPasteboard].string;
            
            // insert at current position
            [self.cellData insertObject:value atIndex:indexPath.row];
        }
        
        // then reload the collection view
        [self.collectionView reloadData];
    	
    }
    

    It looks more complex than it really is: we first procure the value of our cell, and then add it to (or remove it from) the underlying data source. Typically it’s a mutable array or Core Data. Once the data is updated, we ask the collection view to reload its data which then makes the changes appear.

    That’s it!





     
  • Jay 12:48 pm on February 2, 2015 Permalink | Reply
    Tags: ,   

    How to build a UICollectionView in iOS 8 

    In this video I’ll show you how to build a UICollectionView from scratch in Xcode 6. The class is available for both iPhone and iPad since iOS 6. If you know how to build a UITableView then building a UICollectionView will be familiar to you.

    I’ll start with a single view application, delete the ViewController class and start fresh with a UICollectionViewController. Next I’ll add a custom class for the UICollectionViewController and UICollectionViewCell and then we’ll hook it up in the storyboard.

    By the end we’ll have a simple Collection View App which allows multiple selections. I’m going to use this project to build on with other features in the future.

    Custom CollectionViewController Class

    The template provides a few good starting points, but they need to be changed to work. First there’s the cell’s reuse identifier, conveniently added as a static at the top of the implementation file. It’s there so we only need to change this once in the file. Replace it with your own, and remember to make the same change in the storyboard:

    static NSString * const reuseIdentifier = @"Cell";
    

    Screen Shot 2015-02-03 at 11.11.03

    Next up is the viewDidLoad method. To make dequeuing cells easier, Apple have provided a registerClass method. If you don’t add your custom cell here, nothing will appear when you run the app. I found that simply commenting out the line works just as well.

    The reason they provide this is so that the dequeueCellWithIdendifier method knows which custom cell class to instantiate (prior to iOS 6 it returned nil, but that check is no longer necessary).

    I’m also adding multiple selections here, something that cannot be done in the storyboard.

    - (void)viewDidLoad {
    
        [super viewDidLoad];
        
        // EVIL: Register your own cell class (or comment this out)
        [self.collectionView registerClass:[YourCustomCell class] forCellWithReuseIdentifier:reuseIdentifier];
        
        // allow multiple selections
        self.collectionView.allowsMultipleSelection = YES;
        self.collectionView.allowsSelection = YES;
        
    }
    

    UICollectionViewDataSource

    Much like with UITableViews, we need to provide the number of sections, as well as the number of items in each section:

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

    If you don’t provide the sections method, it is assumed that you have one section. Usually we’d have some data and would return a count of how many items we have rather than fixed values here.

    We also need to provide what’s in each cell. Here we can add data to labels, populate UIImageViews and many other things our collection view cells may need:

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
        
        MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
        cell.textLabel.text = [self.cellData objectAtIndex:indexPath.row];
    
        return cell;
    }
    

    UICollectionViewCell

    With UITableViews there were four styles of table cells we could choose from out of the box. A collection view cell on the other hand is completely blank, and we’re expected to provide everything¬†inside it. This means we need a custom UICollectionViewCell class for our project.

    Anything we drag into the prototype cell in the storyboard can be hooked up to that custom class and the configured in the above cellForItemAtIndexPath method. Make sure any outlets are defined in the cell’s header file.

    Cell Selections

    Collection view cells have several views layered on top of each other. At the bottom is the backgroundView and the selectedBackgroundView. These are not configured by default, but if we add our own views here, the cell knows how display selections.

    Above the background/selectedBackgroundView is the contentView, and on top of the contentView is where we can add out own outlets (like labels and images). If we leave the contentView’s background colour transparent, the views underneath will be visible, and hence selections are visible too.

    Here’s how to configure our custom cell with two colours for selection and deselection. I’m doing this in awakeFromNib, which is called as soon as our cell is instantiated:

    - (void)awakeFromNib {
        
        // standard background (deselected)
        UIView *bgView = [[UIView alloc]initWithFrame:self.bounds];
        self.backgroundView = bgView;
        self.backgroundView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"blue"]];
        
        // selected background
        UIView *selectedView = [[UIView alloc]initWithFrame:self.bounds];
        self.selectedBackgroundView = selectedView;
        self.selectedBackgroundView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"pink"]];
        
    }
    

    Demo Project

    The code I’m writing here is available as a demo project on GitHub:





     
  • Jay 12:40 pm on February 2, 2015 Permalink | Reply
    Tags:   

    How to use UIPasteboard to share data with iOS 

    Screen Shot 2015-02-02 at 11.56.41

    UIPasteboard allows you to copy data to iOS and paste it back into your own or other apps. Think of copying text from a Pages document and pasting it into an email. That’s UIPasteboard hard at work.

    The class supports NSString, UIImage, NSURL and UIColor out of the box. You can add your own items to the pasteboard like this:

    NSString *myString = @"Hello";
    [UIPasteboard generalPasteboard].string = myString;
    

    To retrieve the value:

    NSString *myString = [UIPasteboard generalPasteboard].string;
    NSLog(@"%@", myString);
    

    The principle is the same for the other data types.

    Copying and Pasting multiple items

    Single values aside, UIPasteboard also has the ability to store the above data types as an NSArray. IF you had multiple values you’d like to add, you can use this:

    NSString *myString = @"Hello";
    NSString *myOtherString = @"again";
    NSArray *stringArray = @[myString, myOtherString];
        
    [UIPasteboard generalPasteboard].strings = stringArray;
    

    This works with strings, images, URLs and colors.

    Binary Data

    UIPasteboard can also handle binary data from an NSData object. Here’s how to set data:

    NSData *myData = [NSData dataWithContentsOfFile:@"myFile"];
    [UIPasteboard generalPasteboard] setData:myData forPasteboardType:@"yourUTI"];
    

    and to retrieve your data

    NSData *moreData = [[UIPasteboard generalPasteboard]dataForPasteboardType:@"yourUTI"];
    

    The dataForPasteboardType parameter is an NSString of your choosing so that you can identify it as your data. Apple recommend to use the reverse DNS structure like com.yourdomain.yourapp.yourdata.

    Creating your own Pasteboards

    If you don’t want to share data with the general iOS Pasteboard, you can create your own:

    UIPasteboard *myPasteboard = [UIPasteboard pasteboardWithName:@"FunkyPasteboard" create:YES];
    

    To retrieve values from it in other classes, instantiate it again and pass NO as the create parameter.

    The Find Pasteboard Myth

    According to Apple’s documentation, there’s also the Find Pasteboard. Legend has it that it holds the latest string from Spotlight search. We should be able to access it like this:

    UIPasteboard *findPasteboard = [UIPasteboard pasteboardWithName:UIPasteboardNameFind create:NO];
        
    NSString *latestSearch = findPasteboard.string;
    NSLog(@"%@", latestSearch);
    

    which sadly doesn’t work. Even though the object exists and is valid, all of its properties are always nil, no matter how are we search for things. It’s therefore best to forget the Find Pasteboard exists and never mention it again.





     
  • Jay 3:47 pm on January 30, 2015 Permalink | Reply
    Tags: ,   

    How to set the Root View Controller in Xcode 6 

    Sometimes I delete the initial root view controller from a template and populate the storyboard with my own view controllers. As a result, that familiar arrow no longer exists.

    Usually you can drag this arrow to another view controller which then becomes the new “initial view controller” – but when you delete the controller associated with it, the arrow disappears in Xcode 6.

    In previous versions of Xcode the arrow just came back magically. I love “improvements” like this.

    Screen Shot 2015-01-30 at 15.41.47

    So how do we get it back?

    Head over to Interface Builder (i.e. select your storyboard) and find the Attributes Inspector (in the right pane, fourth icon from the left). Select the view controller you’d like to become the root and tick the box that reads Is Initial View Controller.

    Screen Shot 2015-01-30 at 15.40.49

    Hurray. The arrow is back.





     
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
l
go to login
h
show/hide help
shift + esc
cancel
Password Reset
Please enter your e-mail address. You will receive a new password via e-mail.