Objective-C/UICatalog/AAPLTextViewController.m

/*
 Copyright (C) 2015 Apple Inc. All Rights Reserved.
 See LICENSE.txt for this sample’s licensing information
 
 Abstract:
 A view controller that demonstrates how to use UITextView.
 */
 
#import "AAPLTextViewController.h"
 
@interface AAPLTextViewController()<UITextViewDelegate>
 
@property (nonatomic, weak) IBOutlet UITextView *textView;
 
// Used to adjust the text view's height when the keyboard hides and shows.
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *textViewBottomLayoutGuideConstraint;
 
@end
 
 
#pragma mark -
 
@implementation AAPLTextViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    [self configureTextView];
}
 
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    // Listen for changes to keyboard visibility so that we can adjust the text view accordingly.
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleKeyboardNotification:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleKeyboardNotification:) name:UIKeyboardWillHideNotification object:nil];
}
 
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
 
 
#pragma mark - Keyboard Event Notifications
 
- (void)handleKeyboardNotification:(NSNotification *)notification {
    NSDictionary *userInfo = notification.userInfo;
    
    NSTimeInterval animationDuration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
    
    // Convert the keyboard frame from screen to view coordinates.
    CGRect keyboardScreenBeginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect keyboardScreenEndFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
 
    CGRect keyboardViewBeginFrame = [self.view convertRect:keyboardScreenBeginFrame fromView:self.view.window];
    CGRect keyboardViewEndFrame = [self.view convertRect:keyboardScreenEndFrame fromView:self.view.window];
    CGFloat originDelta = keyboardViewEndFrame.origin.y - keyboardViewBeginFrame.origin.y;
    
    // The text view should be adjusted, update the constant for this constraint.
    self.textViewBottomLayoutGuideConstraint.constant -= originDelta;
 
    [self.view setNeedsUpdateConstraints];
    
    [UIView animateWithDuration:animationDuration delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
        [self.view layoutIfNeeded];
    } completion:nil];
 
    // Scroll to the selected text once the keyboard frame changes.
    NSRange selectedRange = self.textView.selectedRange;
    [self.textView scrollRangeToVisible:selectedRange];
}
 
 
#pragma mark - Configuration
 
- (void)configureTextView {
    UIFontDescriptor *bodyFontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
    self.textView.font = [UIFont fontWithDescriptor:bodyFontDescriptor size:0];
 
    self.textView.textColor = [UIColor blackColor];
    self.textView.backgroundColor = [UIColor whiteColor];
    self.textView.scrollEnabled = YES;
 
    // Let's modify some of the attributes of the attributed string.
    // You can modify these attributes yourself to get a better feel for what they do.
    // Note that the initial text is visible in the storyboard.
    NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithAttributedString:self.textView.attributedText];
 
    NSString *text = self.textView.text;
 
    // Find the range of each element to modify.
    NSRange boldRange = [text rangeOfString:NSLocalizedString(@"bold", nil)];
    NSRange highlightedRange = [text rangeOfString:NSLocalizedString(@"highlighted", nil)];
    NSRange underlinedRange = [text rangeOfString:NSLocalizedString(@"underlined", nil)];
    NSRange tintedRange = [text rangeOfString:NSLocalizedString(@"tinted", nil)];
 
    // Add bold.
    UIFontDescriptor *boldFontDescriptor = [self.textView.font.fontDescriptor fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
    UIFont *boldFont = [UIFont fontWithDescriptor:boldFontDescriptor size:0];
    [attributedText addAttribute:NSFontAttributeName value:boldFont range:boldRange];
 
    // Add highlight.
    [attributedText addAttribute:NSBackgroundColorAttributeName value:[UIColor aapl_applicationGreenColor] range:highlightedRange];
 
    // Add underline.
    [attributedText addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:underlinedRange];
 
    // Add tint.
    [attributedText addAttribute:NSForegroundColorAttributeName value:[UIColor aapl_applicationBlueColor] range:tintedRange];
    
    // Add an image attachment.
    NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
    UIImage *image = [UIImage imageNamed:@"text_view_attachment"];
    textAttachment.image = image;
    textAttachment.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
 
    NSAttributedString *textAttachmentString = [NSAttributedString attributedStringWithAttachment:textAttachment];
    [attributedText appendAttributedString:textAttachmentString];
    
    self.textView.attributedText = attributedText;
}
 
 
#pragma mark - UITextViewDelegate
 
- (void)textViewDidBeginEditing:(UITextView *)textView {
    // Provide a "Done" button for the user to select to signify completion with writing text in the text view.
    UIBarButtonItem *doneBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneBarButtonItemClicked)];
 
    [self.navigationItem setRightBarButtonItem:doneBarButtonItem animated:YES];
}
 
 
#pragma mark - Actions
 
- (void)doneBarButtonItemClicked {
    // Dismiss the keyboard by removing it as the first responder.
    [self.textView resignFirstResponder];
 
    [self.navigationItem setRightBarButtonItem:nil animated:YES];
}
 
@end