Skip to content

Instantly share code, notes, and snippets.

@keicoder
Created March 4, 2014 02:40
Show Gist options
  • Save keicoder/9339324 to your computer and use it in GitHub Desktop.
Save keicoder/9339324 to your computer and use it in GitHub Desktop.
objective-c : Textkit and TableView with note (ios 7)
//Textkit and TableView with note (ios 7)
//1. start with basic dummy data
//AppDelegate.h
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property NSMutableArray* notes;
@end
//AppDelegate.m
#import "Note.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (debug==1) {NSLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));}
// create some notes!
self.notes = [NSMutableArray arrayWithArray: @[
[Note noteWithText:@"Shopping List\r\r1. Cheese\r2. Biscuits\r3. Sausages\r4. IMPORTANT Cash for going out!\r5. -potatoes-\r6. A copy of iOS6 by tutorials\r7. A new iPhone\r8. A present for mum"],
[Note noteWithText:@"Meeting notes\rA long and drawn out meeting, it lasted hours and hours and hours!"],
[Note noteWithText:@"Perfection ... \n\nPerfection is achieved not when there is nothing left to add, but when there is nothing left to take away - Antoine de Saint-Exupery"],
[Note noteWithText:@"Notes on iOS7\nThis is a big change in the UI design, it's going to take a *lot* of getting used to!"],
[Note noteWithText:@"Meeting notes\rA dfferent meeting, just as long and boring"],
[Note noteWithText:@"A collection of thoughts\rWhy do birds sing? Why is the sky blue? Why is it so hard to create good test data?"]]];
return YES;
}
//Note.h
@interface Note : NSObject
@property NSString* contents;
@property NSDate* timestamp;
// an automatically generated not title, based on the first few words
@property (readonly) NSString* title;
+ (Note*) noteWithText:(NSString*)text;
@end
//Note.m
#import "Note.h"
@implementation Note
+ (Note *)noteWithText:(NSString *)text {
Note* note = [Note new];
note.contents = text;
note.timestamp = [NSDate date];
return note;
}
- (NSString *)title {
// split into lines
NSArray* lines = [self.contents componentsSeparatedByCharactersInSet: [NSCharacterSet newlineCharacterSet]];
// return the first
return lines[0];
}
@end
//NotesListViewController.h
#import <UIKit/UIKit.h>
@interface NotesListViewController : UITableViewController
@end
//NotesListViewController.m
#import "NotesListViewController.h"
#import "AppDelegate.h"
#import "Note.h"
@implementation NotesListViewController
- (NSMutableArray*) notes
{
if (debug==1) {NSLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));}
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
return appDelegate.notes;
}
- (void)viewDidAppear:(BOOL)animated {
if (debug==1) {NSLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));}
// Whenever this view controller appears, reload the table. This allows it to reflect any changes
// made whilst editing notes.
[self.tableView reloadData];
[super viewDidAppear:animated];
}
- (void)viewDidLoad
{
if (debug==1) {NSLog(@"Running %@ '%@'", self.class, NSStringFromSelector(_cmd));}
[super viewDidLoad];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self notes].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Note* note = [self notes][indexPath.row];
cell.textLabel.text = note.title;
return cell;
}
@end
//2. transfering data between views
//NoteEditorViewController.h
@class Note;
@interface NoteEditorViewController : UIViewController
@property Note* note;
@end
//NoteEditorViewController.m
#import "NoteEditorViewController.h"
#import "Note.h"
@interface NoteEditorViewController () <UITextViewDelegate>
@property (weak, nonatomic) IBOutlet UITextView *textView;
@end
@implementation NoteEditorViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.textView.text = self.note.contents;
self.textView.delegate = self;
}
#pragma mark - UITextView 델리게이트 메소드
- (void)textViewDidEndEditing:(UITextView *)textView //tells the delegate that editing of the specified text view has ended
{
// copy the updated note text to the underlying model.
self.note.contents = textView.text;
}
@end
//transfering data from tableview to detailview
//before implementing prepareForSegue 메소드
//hook up with TextView, in NotesListViewController add bar button to segue identifier "AddNewNote" and prototype cell to segue identifier "CellSelected"
//NotesListViewController.m
#import "NoteEditorViewController.h"
#pragma mark - Navigation
// In a story board-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NoteEditorViewController* editorVC = (NoteEditorViewController*)segue.destinationViewController;
if ([segue.identifier isEqualToString:@"CellSelected"]) { //CellSelected : for edit
// if the cell selected segue was fired, edit the selected note
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
editorVC.note = [self notes][path.row];
}
if ([segue.identifier isEqualToString:@"AddNewNote"]) { //AddNewNote : for new note
// if the add new note segue was fired, create a new note, and edit it
editorVC.note = [Note noteWithText:@" "];
// also, add this note to the collection
[[self notes] addObject:editorVC.note];
}
}
//3. Dynamic type
//for starter implementing Basic support
//NoteEditorViewController.m
//add the following code to the end of method
- (void)viewDidLoad
{
self.textView.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
// handle content size change notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(preferredContentSizeChanged:)
name:UIContentSizeCategoryDidChangeNotification
object:nil];
}
- (void)preferredContentSizeChanged:(NSNotification *)n {
self.textView.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
}
//NotesListViewController.m
//add the following code to the end of method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
}
- (void)viewDidLoad
{
// handle content size change notifications
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(preferredContentSizeChanged:)
name:UIContentSizeCategoryDidChangeNotification
object:nil];
}
- (void)preferredContentSizeChanged:(NSNotification *)n {
[self.tableView reloadData];
}
//change cell's height follow dynamic type font size
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// size the row based on the size required to render a label.
static UILabel* label;
if (!label) {
label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, FLT_MAX, FLT_MAX)];
label.text = @"test";
}
label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
[label sizeToFit];
return label.frame.size.height * 1.7;
}
//4. Letterpress effects
//NotesListViewController.m
//add folling code
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self notes].count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//...
//creates an attributed string for the title of a table cell using the letterpress style
UIColor* textColor = [UIColor colorWithRed:0.175f green:0.458f blue:0.831f alpha:1.0f];
NSDictionary *attrs = @{NSForegroundColorAttributeName : textColor,
NSFontAttributeName : font,
NSTextEffectAttributeName : NSTextEffectLetterpressStyle};
NSAttributedString* attrString = [[NSAttributedString alloc] initWithString:note.title
attributes:attrs];
cell.textLabel.attributedText = attrString;
return cell;
}
//5. Apply exclusion paths (flowing text around images or other objects)
//Adding the view
//NoteEditorViewController.m
#import "TimeIndicatorView.h"
//TimeIndicatorView.h
// a view that renders the time of a note within a circle
@interface TimeIndicatorView : UIView
- (id)init:(NSDate*)date;
// updates the size of this view to comfortably hold the current text. After this
// method is invoked the view will be located at the origin.
- (void)updateSize;
- (UIBezierPath *)curvePathWithOrigin:(CGPoint)origin;
@end
//TimeIndicatorView.m
#import "TimeIndicatorView.h"
@implementation TimeIndicatorView
{
UILabel* _label;
}
-(id)init:(NSDate *)date {
if (self = [super init]) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
self.clipsToBounds = NO;
_label = [[UILabel alloc] init];
_label.textAlignment = NSTextAlignmentRight;
// format and style the date
NSDateFormatter* format = [NSDateFormatter new];
[format setDateFormat:@"dd\rMMMM\ryyyy"];
NSString* formattedDate = [format stringFromDate:date];
_label.text = [formattedDate uppercaseString];
_label.textAlignment = NSTextAlignmentCenter;
_label.textColor = [UIColor whiteColor];
_label.numberOfLines = 0;
[self addSubview:_label];
}
return self;
}
- (void)updateSize {
// size the label based on the font
_label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];
_label.frame = CGRectMake(0, 0, FLT_MAX, FLT_MAX);
[_label sizeToFit];
// set the frame to be large enough to accomodate circle that surrounds the text
float radius = [self radiusToSurroundFrame:_label.frame];
self.frame = CGRectMake(0, 0, radius*2, radius*2);
// center the label within this circle
_label.center = self.center;
// offset the center of this view to ... erm ... can I just draw you a picture?
// You know the story - the designer provides a mock-up with some static data, leaving
// you to work out the complex calculatins required to accomodate the variability of real-world
// data. C'est la vie!
float padding = 5.0f;
self.center = CGPointMake(self.center.x + _label.frame.origin.x - padding,
self.center.y - _label.frame.origin.y + padding);
}
// calculates the radius of the circle that surrounds the label
- (float) radiusToSurroundFrame:(CGRect)frame {
return MAX(frame.size.width, frame.size.height) * 0.5 + 20.0f;
}
- (UIBezierPath *)curvePathWithOrigin:(CGPoint)origin {
return [UIBezierPath bezierPathWithArcCenter:origin
radius:[self radiusToSurroundFrame:_label.frame]
startAngle:-180.0f
endAngle:180.0f
clockwise:YES];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetShouldAntialias(ctx, YES);
UIBezierPath* path = [self curvePathWithOrigin:_label.center];
[[UIColor colorWithRed:0.329f green:0.584f blue:0.898f alpha:1.0f] setFill];
[path fill];
}
@end
//NoteEditorViewController.m
//add the following instance variable
@implementation NoteEditorViewController
{
TimeIndicatorView* _timeView;
}
- (void)viewDidLoad
{
//...
_timeView = [[TimeIndicatorView alloc] init:self.note.timestamp];
[self.view addSubview:_timeView];
}
- (void)viewDidLayoutSubviews { //viewDidLayoutSubviews: called to notify the view controller that its view has just laid out its subviews.
[self updateTimeIndicatorFrame];
}
//call updateSize to make TimeIndicatorView calculates its own size automatically
- (void) updateTimeIndicatorFrame {
[_timeView updateSize];
_timeView.frame = CGRectOffset(_timeView.frame,
self.view.frame.size.width - _timeView.frame.size.width, 0.0); //frame, x, y
}
//add following code
//when view controller receives notification that the size of the content has changed
- (void)preferredContentSizeChanged:(NSNotification *)n {
//...
[self updateTimeIndicatorFrame];
}
//TimeIndicatorView.h
//add the following method declaration:
- (UIBezierPath *)curvePathWithOrigin:(CGPoint)origin;
//NoteEditorViewController.m
//add the following code to the end of updateTimeIndicatorFrame:
/*below method creates an exclusion path based on the Bezier path created in your time indicator view, but with an origin and coordinates that are relative to the text view*/
- (void) updateTimeIndicatorFrame {
//...
//add an exclusion path for the time display
UIBezierPath* exclusionPath = [_timeView curvePathWithOrigin:_timeView.center];
_textView.textContainer.exclusionPaths = @[exclusionPath];
}
//6. Dynamic text formatting and storage
//make subclass of NSTextStorage
//SyntaxHighlightTextStorage.h
// a text storage subclass that performs syntax highlighting
@interface SyntaxHighlightTextStorage : NSTextStorage
@end
//SyntaxHighlightTextStorage.m
@implementation SyntaxHighlightTextStorage
{
//NSMutableAttributedString cause text storage subclass must provide its own ‘persistence’
NSMutableAttributedString *_backingStore;
}
- (id)init
{
if (self = [super init]) {
_backingStore = [NSMutableAttributedString new];
}
return self;
}
#pragma mark - NSTextStorage mandatory overrides
- (NSString *)string
{
return [_backingStore string];
}
- (NSDictionary *)attributesAtIndex:(NSUInteger)location
effectiveRange:(NSRangePointer)range
{
return [_backingStore attributesAtIndex:location
effectiveRange:range];
}
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
{
NSLog(@"replaceCharactersInRange:%@ withString:%@",
NSStringFromRange(range), str);
[self beginEditing];
[_backingStore replaceCharactersInRange:range withString:str];
[self edited:NSTextStorageEditedCharacters | NSTextStorageEditedAttributes
range:range changeInLength:str.length - range.length];
[self endEditing];
}
- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range
{
NSLog(@"setAttributes:%@ range:%@",
attrs, NSStringFromRange(range));
[self beginEditing];
[_backingStore setAttributes:attrs range:range];
[self edited:NSTextStorageEditedAttributes
range:range changeInLength:0];
[self endEditing];
}
//7. UITextView with a custom Text Kit stack
//create the UITextView and Text Kit stack programatically
//delete UITextView instance in storyboard, delete UITextView outlet in NoteEditorViewController.m
//NoteEditorControllerViewController.m
@implementation NoteEditorViewController
{
//...
SyntaxHighlightTextStorage* _textStorage;
UITextView* _textView;
}
//remove following lines from viewDidLoad in NoteEditorViewController.m
- (void)viewDidLoad
{
//...
//remove following code
self.textView.text = self.note.contents;
self.textView.delegate = self;
self.textView.font = [UIFont
preferredFontForTextStyle:UIFontTextStyleBody];
}
//create TextView programatically
//NoteEditorViewController.m
- (void)createTextView
{
// 1. Create the text storage that backs the editor
NSDictionary* attrs = @{NSFontAttributeName:
[UIFont preferredFontForTextStyle:UIFontTextStyleBody]};
NSAttributedString* attrString = [[NSAttributedString alloc] initWithString:_note.contents
attributes:attrs];
_textStorage = [SyntaxHighlightTextStorage new];
[_textStorage appendAttributedString:attrString];
CGRect newTextViewRect = self.view.bounds;
// 2. Create the layout manager
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
// 3. Create a text container
CGSize containerSize = CGSizeMake(newTextViewRect.size.width, CGFLOAT_MAX);
NSTextContainer *container = [[NSTextContainer alloc] initWithSize:containerSize];
container.widthTracksTextView = YES;
[layoutManager addTextContainer:container];
[_textStorage addLayoutManager:layoutManager];
// 4. Create a UITextView
_textView = [[UITextView alloc] initWithFrame:newTextViewRect
textContainer:container];
_textView.delegate = self;
[self.view addSubview:_textView];
// ensure that the text view is not editable initially
_textView.editable = NO;
_textView.dataDetectorTypes = UIDataDetectorTypeLink; //clickable URLs in the text view
}
//call createTextView method
- (void)viewDidLoad
{
//...
[self createTextView];
}
//replace the old outlet property with the new instance variable
- (void)preferredContentSizeChanged:(NSNotification *)n {
_textView.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
}
- (void)viewDidLayoutSubviews { //called to notify the view controller that view has just laid out subviews
//...
_textView.frame = _textViewFrame;
//custom view created in code doesn’t automatically inherit the layout constraints set in the storyboard
//therefore, the frame of your new view won’t resize when the device orientation changes
//need to explicitly set the frame yourself
}
//8. Dynamic formatting
//SyntaxHighlightTextStorage.m
-(void)processEditing { //Triggers post-editing operations
[self performReplacementsForRange:[self editedRange]];
[super processEditing];
}
- (void)performReplacementsForRange:(NSRange)changedRange {
//expands the range because changedRange typically indicates a single character
//lineRangeForRange extends that range to the entire line of text
NSRange extendedRange = NSUnionRange(changedRange, [[_backingStore string] lineRangeForRange:NSMakeRange(changedRange.location, 0)]);
extendedRange = NSUnionRange(extendedRange, [[_backingStore string] lineRangeForRange:NSMakeRange(NSMaxRange(changedRange), 0)]);
[self applyStylesToRange:extendedRange];
}
- (void)applyStylesToRange:(NSRange)searchRange {
// 1. create some fonts
UIFontDescriptor* fontDescriptor = [UIFontDescriptor
preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
UIFontDescriptor* boldFontDescriptor = [fontDescriptor
fontDescriptorWithSymbolicTraits:UIFontDescriptorTraitBold];
UIFont* boldFont = [UIFont fontWithDescriptor:boldFontDescriptor size: 0.0];
UIFont* normalFont = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
// 2. match items surrounded by asterisks
NSString* regexStr = @"(\\*\\w+(\\s\\w+)*\\*)\\s";
NSRegularExpression* regex = [NSRegularExpression
regularExpressionWithPattern:regexStr
options:0
error:nil];
NSDictionary* boldAttributes = @{ NSFontAttributeName : boldFont };
NSDictionary* normalAttributes = @{ NSFontAttributeName : normalFont };
// 3. iterate over each match, making the text bold
[regex enumerateMatchesInString:[_backingStore string] options:0 range:searchRange usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
NSRange matchRange = [match range];
[self addAttributes:boldAttributes range:matchRange];
// 4. reset the style to the original
if (NSMaxRange(matchRange)+1 < self.length) {
[self addAttributes:normalAttributes range:NSMakeRange(NSMaxRange(matchRange)+1, 1)];
}
}];
}
//9. Adding further styles
//basic principle of applying styles:
//*** use a regex to find and replace the string using applyStylesToRange
//Add an instance variable to
//SyntaxHighlightTextStorage.m
@implementation SyntaxHighlightTextStorage
{
//...
NSDictionary* _replacements;
}
- (void) createHighlightPatterns {
UIFontDescriptor *scriptFontDescriptor = [UIFontDescriptor
fontDescriptorWithFontAttributes:
@{UIFontDescriptorFamilyAttribute: @"Zapfino"}];
// 1. base our script font on the preferred body font size
UIFontDescriptor* bodyFontDescriptor = [UIFontDescriptor
preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
NSNumber* bodyFontSize = bodyFontDescriptor.fontAttributes[UIFontDescriptorSizeAttribute];
UIFont* scriptFont = [UIFont fontWithDescriptor:scriptFontDescriptor
size:[bodyFontSize floatValue]];
// 2. create the attributes
NSDictionary* boldAttributes = [self
createAttributesForFontStyle:UIFontTextStyleBody
withTrait:UIFontDescriptorTraitBold];
NSDictionary* italicAttributes = [self
createAttributesForFontStyle:UIFontTextStyleBody
withTrait:UIFontDescriptorTraitItalic];
NSDictionary* strikeThroughAttributes = @{ NSStrikethroughStyleAttributeName : @1};
NSDictionary* scriptAttributes = @{ NSFontAttributeName : scriptFont};
NSDictionary* redTextAttributes = @{ NSForegroundColorAttributeName : [UIColor redColor]};
// construct a dictionary of replacements based on regexes
_replacements = @{
@"(\\*\\w+(\\s\\w+)*\\*)\\s" : boldAttributes,
@"(_\\w+(\\s\\w+)*_)\\s" : italicAttributes,
@"([0-9]+\\.)\\s" : boldAttributes,
@"(-\\w+(\\s\\w+)*-)\\s" : strikeThroughAttributes,
@"(~\\w+(\\s\\w+)*~)\\s" : scriptAttributes,
@"\\s([A-Z]{2,})\\s" : redTextAttributes};
}
/*
deconstruct the regular expression
1. (\* - match an asterisk
2. \w+ - followed by one or more “word” characters
3. (\s\w+)* - followed by zero or more groups of spaces followed by “word”
characters
4. \*) - followed by an asterisk
5. \s - terminated by a space
http://www.raywenderlich.com/30288/nsregularexpression-tutorial-and-cheat-sheet
*/
//actually call createHighlightPatterns. Update init
- (id)init {
if (self = [super init]) {
//...
[self createHighlightPatterns];
}
return self;
}
//Add the following method
//below method applies the supplied font style to the body font.
//provides a zero size to return a size that matches the user’s current font size preferences
- (NSDictionary*)createAttributesForFontStyle:(NSString*)style
withTrait:(uint32_t)trait {
UIFontDescriptor *fontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
UIFontDescriptor *descriptorWithTrait = [fontDescriptor fontDescriptorWithSymbolicTraits:trait];
UIFont* font = [UIFont fontWithDescriptor: descriptorWithTrait
size: 0.0];
return @{ NSFontAttributeName : font };
}
//replace the existing applyStylesToRange method
//below method code does pretty much exactly what it did before
//but this time it iterates over the dictionary of regex matches and attributes
//and applies the specified style to the matched patterns
- (void)applyStylesToRange:(NSRange)searchRange
{
NSDictionary* normalAttrs = @{NSFontAttributeName:
[UIFont preferredFontForTextStyle:UIFontTextStyleBody]};
// iterate over each replacement
for (NSString* key in _replacements) {
NSRegularExpression *regex = [NSRegularExpression
regularExpressionWithPattern:key
options:0
error:nil];
NSDictionary* attributes = _replacements[key];
[regex enumerateMatchesInString:[_backingStore string]
options:0
range:searchRange
usingBlock:^(NSTextCheckingResult *match,
NSMatchingFlags flags,
BOOL *stop){
// apply the style
NSRange matchRange = [match range];
[self addAttributes:attributes range:matchRange];
// reset the style to the original
if (NSMaxRange(matchRange)+1 < self.length) {
[self addAttributes:normalAttrs
range:NSMakeRange(NSMaxRange(matchRange)+1, 1)];
}
}];
}
}
//10. Reviving dynamic type
//Problem :
//1. changed the orientation of your screen, app no longer responds to changing content size
//2. add a lot of text to a note, bottom of the text view is partially obscured by the keyboard
//SyntaxHighlightTextStorage.h
//add the following method declaration to the interface
@interface SyntaxHighlightTextStorage : NSTextStorage
- (void)update;
@end
//SyntaxHighlightTextStorage.m
-(void)update {
// update the highlight patterns
[self createHighlightPatterns];
// change the 'global' font
NSDictionary* bodyFont = @{NSFontAttributeName :
[UIFont preferredFontForTextStyle:UIFontTextStyleBody]};
[self addAttributes : bodyFont
range : NSMakeRange(0, self.length)];
// re-apply the regex matches
[self applyStylesToRange:NSMakeRange(0, self.length)];
}
//NoteEditorViewController.m
//update preferredContentSizeChanged: to invoke update:
- (void)preferredContentSizeChanged:(NSNotification *)n {
//...
[_textStorage update];
}
//11. Resizing text views
//Add the following instance variable to NoteEditorViewController.m
@implementation NoteEditorViewController
{
//...
CGRect _textViewFrame; //used to store the current text view frame
}
//update viewDidLayoutSubviews to make use of this newly added instance variable
- (void)viewDidLayoutSubviews
{
//called to notify the view controller that view has just laid out subviews
//...
_textView.frame = _textViewFrame;
}
//add the following to the bottom of viewDidLoad to set the initial frame value
- (void)viewDidLoad
{
//...
_textViewFrame = self.view.bounds;
//...
}
//update _textViewFrame variable when the keyboard is shown
#pragma mark - UITextView 델리게이트 메소드
- (void)textViewDidBeginEditing:(UITextView *)textView
{
//텍스트 뷰 사이즈 조정
[UIView animateWithDuration:0.3 animations:^{
_textViewFrame = self.view.bounds;
_textViewFrame.size.height -= 216.0f; //reduces the height of the on-screen keyboard
_textView.frame = _textViewFrame;
}];
}
//return the text view frame to its original size when editing finishes
- (void)textViewDidEndEditing:(UITextView *)textView
{
//copy the updated note text to the underlying model
self.note.contents = textView.text;
_textViewFrame = self.view.bounds;
_textView.frame = _textViewFrame;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment