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
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.
Coding Theory | Localization |
Logic | Object-Oriented Design |
Performance Optimization | Quality Control |
Reengineering | Robohelp |
Software Development | Software Reuse |
Structured Design | Testing |
Tools | UML |
Deep Learning with Python by François Chollet(11891)
Hello! Python by Anthony Briggs(9372)
OCA Java SE 8 Programmer I Certification Guide by Mala Gupta(9343)
The Mikado Method by Ola Ellnestam Daniel Brolund(9307)
Dependency Injection in .NET by Mark Seemann(8859)
Algorithms of the Intelligent Web by Haralambos Marmanis;Dmitry Babenko(7851)
Test-Driven iOS Development with Swift 4 by Dominik Hauser(7323)
Grails in Action by Glen Smith Peter Ledbrook(7295)
The Well-Grounded Java Developer by Benjamin J. Evans Martijn Verburg(7116)
Secrets of the JavaScript Ninja by John Resig Bear Bibeault(5957)
Kotlin in Action by Dmitry Jemerov(4637)
Practical Vim (for Kathryn Amaral) by Drew Neil(3728)
Cracking the GRE Premium Edition with 6 Practice Tests, 2015 (Graduate School Test Preparation) by Princeton Review(3596)
Linux Device Driver Development Cookbook by Rodolfo Giometti(3414)
Learn Windows PowerShell in a Month of Lunches by Don Jones(3240)
Learning Java by Patrick Niemeyer & Daniel Leuck(2871)
Learning React: Functional Web Development with React and Redux by Banks Alex & Porcello Eve(2835)
Becoming a Dynamics 365 Finance and Supply Chain Solution Architect by Brent Dawson(2670)
Mastering Java 9 by Dr. Edward Lavieri(2573)