cs193p – Assignment #3 Task #9

Please note, this blog entry is from a previous course. You might want to check out the current one.

Last week, there was an Extra Credit task to use a UISlider to show the history of the currently-being-played game. This week we’re going to make showing the history a Required Task, but instead of using a slider, you must invent yet another new MVC which displays the history in a UITextView and which is segued to inside a UINavigationController. It should show the cards involved in every match or mismatch as well as how many points were earned or lost as a result (you are already showing this information in a UILabel for each card choosing, so this should be rather straightforward to implement). Add a bar button item “History” on the right side of the navigation bar which performs the push segue. This feature must work for both the Playing Card game and the Set game.

In storyboard embed both game view controllers into a navigation view controller. Because the usable space decreases remove a row of cards and adjust the layout. Add titles and history buttons. Add a new view controller and click-drag from both history buttons to the new view controller. Set the segue identifier of both of the new segues to “Show History”. Set the title of the new view controller and add a text view.

cs193p – assignment #3 task #9 – history view
cs193p – assignment #3 task #9 – history view

Create a new view-controller class, link the new view controller in storyboard to to this class and create an outlet for the text view:

@property (weak, nonatomic) IBOutlet UITextView *historyTextView;

The new class needs a public property to hold an array of history strings or attributed strings:

@property (nonatomic, strong) NSArray *history; // of NSStrings || NSAttributedStrigns

When the array gets set and is on screen, or when the view appears on screen we will update the interface:

- (void)setHistory:(NSArray *)history
{
    _history = history;
    if (self.view.window) [self updateUI];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self updateUI];
}

Because the array could hold normal strings or attributed ones, it’s necessary to handle them differently. For a “normal” string just loop over all entries and create a new output string. It’s actually the same for attributed strings, with the difference that creating them is a little bit more cumbersome. … and at the end preserve and set the font, otherwise the view will end up with the default font and not the one you selected in storyboard:

- (void)updateUI
{
    if ([[self.history firstObject] isKindOfClass:[NSAttributedString class]]) {
        NSMutableAttributedString *historyText = [[NSMutableAttributedString alloc] init];
        int i = 1;
        for (NSAttributedString *line in self.history) {
            [historyText appendAttributedString:[[NSAttributedString alloc] 
                initWithString:[NSString stringWithFormat:@"%2d: ", i++]]];
            [historyText appendAttributedString:line];
            [historyText appendAttributedString:[[NSAttributedString alloc] 
                initWithString:@"\n\n" ]];
        }
        UIFont *font = [self.historyTextView.textStorage attribute:NSFontAttributeName 
                                                           atIndex:0 
                                                    effectiveRange:NULL];
        [historyText addAttribute:NSFontAttributeName value:font 
                                                      range:NSMakeRange(0, historyText.length)];
        self.historyTextView.attributedText = historyText;
    } else {
        NSString *historyText = @"";
        int i = 1;
        for (NSString *line in self.history) {
            historyText = [NSString stringWithFormat:@"%@%2d: %@\n\n", historyText, i++, line];
        }
        self.historyTextView.text = historyText;
    }
}
[/obj]

When segueing from the playing-card view to the history view, set the history array:


#import "HistoryViewController.h"
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue
                 sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"Show History"]) {
        if ([segue.destinationViewController isKindOfClass:[HistoryViewController class]]) {
            [segue.destinationViewController setHistory:self.flipHistory];
        }
    }
}

… the code above could be either in the playing-card-view-controller class or in its parent class …

For the set cards, we need to adjust the array and pass attributed strings to the history view controller. Because you need to access the flip history in the child class, make the property public (by putting it into the header file):

@property (strong, nonatomic) NSMutableArray *flipHistory; // of NSStrings

The current set-game-view-controller class knows already how to make “proper” attributed strings. But because the code is currently part of the interface-updating method, create a helper method:

- (NSAttributedString *)replaceCardDescriptionsInText:(NSAttributedString *)text
{
    NSMutableAttributedString *newText = ;    
    NSArray *setCards = [SetCard cardsFromText:text.string];
    if (setCards) {
        for (SetCard *setCard in setCards) {
            NSRange range = [newText.string rangeOfString:setCard.contents];
            if (range.location != NSNotFound) {
                [newText replaceCharactersInRange:range
                             withAttributedString:[self titleForCard:setCard]];
            }
        }
    }
    return newText;
}

- (void)updateUI
{
    [super updateUI];    
    self.flipDescription.attributedText = 
        [self replaceCardDescriptionsInText:self.flipDescription.attributedText];
}

… and use this method when segueing, creating a new array holding attributed strings:

- (void)prepareForSegue:(UIStoryboardSegue *)segue
                 sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"Show History"]) {
        if ([segue.destinationViewController isKindOfClass:[HistoryViewController class]]) {
            NSMutableArray *attributedHistory = [NSMutableArray array];
            for (NSString *flip in self.flipHistory) {
                NSAttributedString *attributedFlip = 
                    [[NSAttributedString alloc] initWithString:flip];
                [attributedHistory addObject:[self replaceCardDescriptionsInText:attributedFlip]];
            }
            [segue.destinationViewController setHistory:attributedHistory];
        }
    }
}

The complete code is available on github.

FacebooktwitterredditpinterestlinkedintumblrmailFacebooktwitterredditpinterestlinkedintumblrmail

2 thoughts on “cs193p – Assignment #3 Task #9”

  1. hi,, i’ve been watching this tutorial until this lesson,,but when i try to write code for attributor extra page which is used for counting attributed text my code been error in this

    – (void)viewWillAppear:(BOOL)animated
    {
    [super viewWillAppear:];
    [self updateUI];
    }

    it always shows expected expressions on [super viewwillappear] line,, im confused with that,,please help explain to me whats going on,,

    thank you

Leave a Reply

Your email address will not be published.