Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the iBooks App format the text on separate pages?

Looking at the iBooks App, I was wondering how it accomplishes to format text (probably a very simple txt file) so that it is NOT SCROLLABLE but divided on separate pages.

I'd like to achieve the same, but only with editable text.

Where would I need to start? UITextView doesn't work as it scrolls. Even if I set the pagingEnabled property to YES, it won't do the job.

It seems as if I need to LIMIT the amount of text I can put on a certain page. Is there a function to LIMIT the input of a UITextView? I would need to limit the number of LINES (not characters) as they would be shown on a full iPhone screen (I guess you can fit 20 or so lines, depending on the font size).

Any help would be appreciated! As I'm a beginner, I especially appreciate samples in which similar methods are put to use.

Thanks!

like image 518
n.evermind Avatar asked Mar 08 '11 17:03

n.evermind


People also ask

What book format does iBooks use?

Apple accepts two different types of book files: EPUBs and Multi-Touch books.

How do I change the page format in iBooks?

Choose Show Layouts from the View pop-up menu in the toolbar. In the Layouts pane, select a layout you want to start from.

What format does Books use on iPhone?

The EPUB format is an open digital book (eBook) standard from the W3C. With Pages, you can export your documents in the EPUB format so they can be read using the Books app on your iPhone, iPad, iPod touch, Mac, or with third-party EPUB readers. This article provides in-depth instructions for creating a book in Pages.

How do I get accurate page numbers in iBooks?

With iBooks open tap the two A's at the top of the page. If you don't have the A's tap the page. Turn scrolling view on. Using the two A's in the window reduce or enlarge the print till what I assume is the real page number appears at the bottom of the page.


1 Answers

What you need is an own layout algorithm that takes the text, measuring its size and cuts it until it fits into a single page text view. Then start with the rest of the text, same thing for a next text view and so on... After that (or inside the algorithm) you arrange all the resulting text views on scroll view (or in an array, you later flip through with paging animations - if you like it cheesy). I did a similar thing, but with UILabels, it should also work with textviews. What you need is: NSString's - (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size and rangeOfString:@" " options:NSBackwardsSearch (looking for word spaces) and substringFromIndex: resp. substringToIndex:

If you need more information, just post a comment.

EDIT:

Hi following code is not tested but contains most of what you need (hopefully), but may hold some bugs, especially when it comes to the recursion... I corrected the BackWardsSearch idea, because it could take a looong time to cut a long text. What I totally ignored - and that could be really tricky - is re-rendering while editing. But anyway, here's the code. It's a view controller assumed to old 4 members (header file not posted):

  UIView *editableBook;
  NSMutableArray *content;
  int currentPage;
  UIFont *font;

And this is the Controller itself:

//
//  EditableBookController.m
//
//  Created by Kai on 09.03.11.
//

#import "EditableBookController.h"


@implementation EditableBookController

-(id)initWithText:(NSString *)text
{
  if (self=[super init]) 
  {
    font = [UIFont fontWithName:@"SomeFont" size:12];
    content = [[NSMutableArray alloc]init];
    [self cutInPages:text];
    currentPage = 0;  
  }
  return self;
}

- (void)loadView 
{
  self.view = [[UIView alloc] initWithFrame:CGRectMake(.0, .0, 768., 1024.)];//assuming portrait only ...
  editableBook = [[UIView alloc]initWithFrame:self.view.bounds];//could be a scroll view in alternate approach
  UISwipeGestureRecognizer *flipper = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(nextOrPrevious:)];
  [editableBook addGestureRecognizer:flipper];
  [flipper release];
  [self.view addSubview:editableBook];
  UITextView *textView = [[UITextView alloc]initWithText:[content objectAtIndex:currentPage]];
  textView.frame = editableBook.bounds;
  textView.font = font;
  textView.tag = 23;
  [editableBook addSubview:textView];
  [textView release];
}

-(void)nextOrPrevious:(id)sender
{
  UISwipeGestureRecognizer *flipper = (UISwipeGestureRecognizer*)sender;
  if(flipper.direction == UISwipeGestureRecognizerDirectionLeft)
  {
    [self next];
  }
  else if(flipper.direction == UISwipeGestureRecognizerDirectionRight)
  {
    [self previous];
  }
}

-(void)next
{
  if(currentPage == content.count - 1)
  {
    return;
  }
  currentPage++;
  UIView *fromView = [editableBook viewWithTag:23];
  UIView *toView =  [[UITextView alloc]initWithText:[content objectAtIndex:currentPage]];
  toView.frame = editableBook.bounds;
  toView.tag = 23;
  [UIView transitionWithView:editableBook duration:0.2 options:UIViewAnimationTransitionFlipFromRight
   animations:^
   {
     [fromView removeFromSuperview];
     [editableBook addSubview:toView];
   }
   completion:NULL];
}

-(void)previous
{
  if(currentPage == 0)
  {
    return;
  }
  currentPage--;
  UIView *fromView = [editableBook viewWithTag:23];
  UIView *toView =  [[UITextView alloc]initWithText:[content objectAtIndex:currentPage]];
  toView.frame = editableBook.bounds;
  toView.tag = 23;
  [UIView transitionWithView:editableBook duration:0.2 options:UIViewAnimationTransitionFlipFromLeft
   animations:^
   {
     [fromView removeFromSuperview];
     [editableBook addSubview:toView];
   }
   completion:NULL];
}

-(void)cutInPages:(NSString *)text
{
  NSRange whereToCut = whereToCut = [text rangeOfString:@" "];
  NSString *pageText = [text substringToIndex:whereToCut.location];
  NSString *rest = [text substringFromIndex:whereToCut.location];;
  CGFloat height = 0;
  while (height<1024.) 
  {
    NSRange whereToCut = [rest rangeOfString:@" "];
    NSString *wordOfRest = [rest substringToIndex:whereToCut.location];
    pageText = [NSString stringWithFormat:@"%@%@", pageText, wordOfRest];
    rest = [rest substringFromIndex:whereToCut.location];;
    CGSize size = [pageText sizeWithFont:font
                      constrainedToSize:CGSizeMake(768., 10000) 
                          lineBreakMode:UILineBreakModeWordWrap];
    height = size.height;
  }
  if(height>1024.)
  {
    //TODO cut the last word of pageText and prepend to the eest
  }
  [content addObject:pageText];
  if([rest length] > 0)
  {
    [self cutInPages:rest];
  }
}

- (void)dealloc 
{
  [editableBook release];
  [content release];
  [super dealloc];
}


@end
like image 192
Kai Huppmann Avatar answered Oct 28 '22 16:10

Kai Huppmann