Learn iOS App Development by James Bucanek

Learn iOS App Development by James Bucanek

Author:James Bucanek
Language: eng
Format: epub
Publisher: Apress, Berkeley, CA


The Paginator

The code for WLPaginator.h is in Listing 12-1 and the code for WLPaginator.m is in Listing 12-2. If you want to copy and paste the solution, you’ll find the source files for the finished code in the Learn iOS Development Projects ➤ Ch 12 ➤ Wonderland project folder.

Listing 12-1. WLPaginator.h

#import <Foundation/Foundation.h>

@interface WLPaginator : NSObject

@property (strong,nonatomic) NSString *bookText;

@property (strong,nonatomic) UIFont *font;

@property (readonly,nonatomic) NSDictionary *fontAttrs;

@property (nonatomic) CGSize viewSize;

@property (readonly,nonatomic) NSUInteger lastKnownPage;

- (BOOL)availablePage:(NSUInteger)page;

- (NSString*)textForPage:(NSUInteger)page;

@end

Listing 12-2. WLPaginator.m

#import "WLPaginator.h"

@interface WLPaginator ()

{

NSMutableArray *ranges;

NSUInteger lastPageWithContent;

NSDictionary *fontAttrs;

}

- (NSRange)rangeOfTextForPage:(NSUInteger)page;

@end

@implementation WLPaginator

- (void)resetPageData

{

ranges = [NSMutableArray array];

lastPageWithContent = 1;

}

- (void)setBookText:(NSString *)bookData

{

_bookText = bookData;

[self resetPageData];

}

- (void)setFont:(UIFont *)font

{

if ([_font isEqual:font])

return;

_font = font;

_fontAttrs = nil;

[self resetPageData];

}

- (NSDictionary*)fontAttrs

{

if (fontAttrs==nil)

{

NSMutableParagraphStyle *style = [NSMutableParagraphStyle new];

style.lineBreakMode = NSLineBreakByWordWrapping;

fontAttrs = @{

NSFontAttributeName: self.font,

NSParagraphStyleAttributeName: style

};

}

return fontAttrs;

}

- (void)setViewSize:(CGSize)viewSize

{

if (CGSizeEqualToSize(_viewSize,viewSize))

return;

_viewSize = viewSize;

[self resetPageData];

}

- (NSUInteger)lastKnownPage

{

return lastPageWithContent;

}

#define SpanRange(LOCATION,LENGTH) \

({ NSUInteger loc_=(LOCATION); NSMakeRange(loc_,(LENGTH)-loc_); })

- (NSRange)rangeOfTextForPage:(NSUInteger)page

{

if (ranges.count>=page)

return [ranges[page-1] rangeValue];

CGSize constraintSize = _viewSize;

CGFloat targetHeight = constraintSize.height;

constraintSize.height = 32000;

NSRange textRange = NSMakeRange(0,0);

if (page!=1)

textRange.location = NSMaxRange([self rangeOfTextForPage:page-1]);

NSCharacterSet *wordBreakCharSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];

while (textRange.location<_bookText.length &&

[wordBreakCharSet characterIsMember:[_bookText characterAtIndex:textRange.location]])

{

textRange.location += 1;

}

CGSize textSize = CGSizeMake(0,0);

CGRect textBounds;

NSCharacterSet *paraCharSet = [NSCharacterSet characterSetWithCharactersInString:@"\r"];

while (textSize.height<targetHeight)

{

NSRange paraRange = [_bookText rangeOfCharacterFromSet:paraCharSet

options:NSLiteralSearch

range:SpanRange(NSMaxRange(textRange),_bookText.length)];

if (paraRange.location==NSNotFound)

break;

textRange.length = NSMaxRange(paraRange)-textRange.location;

NSString *testText = [_bookText substringWithRange:textRange];

textBounds = [testText boundingRectWithSize:constraintSize

options:NSStringDrawingUsesLineFragmentOrigin

attributes:self.fontAttrs

context:[NSStringDrawingContext new]];

textSize = textBounds.size;

}

while (textSize.height>targetHeight)

{

NSRange wordRange = [_bookText rangeOfCharacterFromSet:wordBreakCharSet

options:NSBackwardsSearch

range:textRange];

if (wordRange.location==NSNotFound)

break;

textRange.length = wordRange.location-textRange.location;

NSString *testText = [_bookText substringWithRange:textRange];

textBounds = [testText boundingRectWithSize:constraintSize

options:NSStringDrawingUsesLineFragmentOrigin

attributes:self.fontAttrs

context:[NSStringDrawingContext new]];

textSize = textBounds.size;

}

if (textRange.length!=0)

lastPageWithContent = page;

[ranges addObject:[NSValue valueWithRange:textRange]];

return textRange;

}

- (BOOL)availablePage:(NSUInteger)page

{

if (page==1)

return YES;

NSRange textRange = [self rangeOfTextForPage:page];

return (textRange.length!=0);

}

- (NSString*)textForPage:(NSUInteger)page

{

return [_bookText substringWithRange:[self rangeOfTextForPage:page]];

}

@end

The details of how WLPaginator works isn’t important to this chapter, but if you’re curious read the comments in the finished project. Conceptually, it’s straightforward. The paginator object is configured with three pieces of information: the complete text of the book, the font it will be drawn in, and the size of the text view that displays a page. The object then splits up the text of the book into ranges, each range filling one page. Any view controller object can then ask the paginator for the text that fits on its page.

Note

This is hardly the most sophisticated way of implementing the paginator, but it’s sufficient for this app.



Download



Copyright Disclaimer:
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.