Как приложение iBooks форматирует текст на отдельных страницах?

Глядя на приложение iBooks, мне было интересно, как оно форматирует текст (вероятно, очень простой текстовый файл), чтобы его НЕ ПРОКРУТИЛИ, а разделили на отдельные страницы.

Я хотел бы добиться того же, но только с редактируемым текстом.

С чего мне нужно начать? UITextView не работает при прокрутке. Даже если я установлю для свойства pagingEnabled значение YES, это не сработает.

Кажется, мне нужно ОГРАНИЧИТЬ количество текста, которое я могу поместить на определенную страницу. Есть ли функция ОГРАНИЧЕНИЯ ввода UITextView? Мне нужно было бы ограничить количество СТРОК (не символов), поскольку они будут отображаться на полном экране iPhone (я думаю, вы можете уместить около 20 строк, в зависимости от размера шрифта).

Любая помощь будет оценена по достоинству! Поскольку я новичок, я особенно ценю примеры, в которых используются подобные методы.

Спасибо!


person n.evermind    schedule 08.03.2011    source источник


Ответы (2)


Что вам нужно, так это собственный алгоритм компоновки, который берет текст, измеряет его размер и обрезает его до тех пор, пока он не уместится в виде текста на одной странице. Затем начните с остального текста, то же самое для следующего текстового представления и так далее... После этого (или внутри алгоритма) вы упорядочиваете все полученные текстовые представления в виде прокрутки (или в массиве, который вы позже пролистываете с анимацией листания - если вам это нравится). Я сделал нечто подобное, но с UILabels он также должен работать с текстовыми представлениями. Что вам нужно, это: NSString's - (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size и rangeOfString:@" " options:NSBackwardsSearch (ищет пробелы слов) и substringFromIndex: соответственно. substringToIndex:

Если вам нужна дополнительная информация, просто оставьте комментарий.

ИЗМЕНИТЬ:

Привет, следующий код не тестировался, но содержит большую часть того, что вам нужно (надеюсь), но может содержать некоторые ошибки, особенно когда дело доходит до рекурсии... Я исправил идею BackWardsSearch, потому что это может занять много времени, чтобы сократить длинный текст. То, что я полностью проигнорировал — и это может быть очень сложно, — это повторный рендеринг при редактировании. Но в любом случае, вот код. Предполагается, что это контроллер представления для старых 4 членов (заголовочный файл не опубликован):

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

А это сам контроллер:

//
//  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
person Kai Huppmann    schedule 08.03.2011
comment
Спасибо за быстрый ответ. Я понимаю, что вы имеете в виду (особенно алгоритм нарезки текста на маленькие кусочки), но я совершенно теряюсь, когда начинаю думать о том, с чего начать. - person n.evermind; 08.03.2011
comment
(1) Предположим, у меня есть алгоритм. Какие-то методы, которым я передаю массив, и методы нарезают его и сохраняют в другой массив. - person n.evermind; 08.03.2011
comment
(2) Каким будет мой следующий шаг? Предположим, что я хочу иметь для этого отдельный алгоритм/метод. Я помещаю массив в UITextView? - person n.evermind; 08.03.2011
comment
У вас есть пример кода, которым вы хотели бы поделиться? Это не должно быть полным, я просто полностью теряюсь, когда дело доходит до применения теории. Спасибо! - person n.evermind; 08.03.2011
comment
Большое спасибо, это действительно мило с вашей стороны! Это, надеюсь, поможет мне начать в правильном направлении. Как только я добился некоторого прогресса, вам интересно увидеть результаты? Если да, дайте мне знать, как я должен держать вас в курсе! Еще раз спасибо, я действительно очень ценю вашу помощь! - person n.evermind; 09.03.2011
comment
Да, пожалуйста, оставьте здесь несколько комментариев с вашим опытом. Особенно меня интересует, как вы управляете повторным рендерингом во время редактирования. - person Kai Huppmann; 09.03.2011

Я не видел исходный код, но готов гарантировать, что они используют CoreText framework для разбиения на страницы и рендеринга. Однако имейте в виду, что когда написано «низкий уровень», это означает именно это. Это фреймворк, который напрямую занимается рендерингом символов. Такие вещи, как выбор и вставка символов (которые вам, вероятно, понадобятся для редактируемого текста), довольно сложны с CoreText.

Я бы рекомендовал использовать NSString рисунок и методы определения размера, которые являются частью платформы UIKit. Но даже тогда вам придется проделать немало работы, чтобы получить этот функционал. Я не думаю, что UIKit предлагает средство редактирования текста, которое не прокручивается. Мы предполагаем, что если вы хотите отредактировать текст, он будет иметь произвольную длину, а это значит, что мы поместим его в прокручиваемый контейнер (чтобы он мог быть любой длины).

Короче говоря, если вы новичок, я бы порекомендовал заняться чем-то другим. То, о чем вы просите, не так просто сделать. :)

person Dave DeLong    schedule 08.03.2011
comment
Хм... так ты предлагаешь мне сдаться... Почему это так сложно? Должен быть какой-то простой способ сделать это. Забудем про анимацию и все такое. Предположим, у нас есть текстовый файл, который заполнил бы 2 1/2 экрана iphone (или просто «страницы»). Должен быть простой способ (с помощью PageControl?) запрограммировать приложение, которое заполняет первую страницу, затем понимает, что в нем больше нет места, открывает страницу 2, снова понимает, что в нем нет места, и, наконец, заполняет половина страницы 3. - person n.evermind; 08.03.2011
comment
Это ведь не может быть так уж сложно... не так ли? - person n.evermind; 08.03.2011
comment
@DanielNicolae: это не невозможно, и для опытного программиста это может быть даже не так сложно. но я также не назвал бы это проблемой начального уровня. жесткая, жесткая, жесткая часть — это редактируемый характер вашего текста. Сделать это для статического текста должно быть довольно легко. Делать это для редактируемого текста намного сложнее, потому что внезапно вам придется беспокоиться о клавиатуре, которая будет мешать. Что происходит, когда появляется клавиатура? ты все масштабируешь? вы вдруг разрешаете прокрутку? вы уменьшаете размер страницы? это не простая проблема - person Dave DeLong; 09.03.2011
comment
Хорошо, тогда позвольте мне сначала отсортировать статический текст. И даже после этого не вижу проблемы с клавиатурой. Я уже запрограммировал его так, что он не может скрыть текст. План состоял в том, чтобы затем разрешить прокрутку (на самом деле я не запрещал ее раньше - в этом просто не было необходимости, поскольку у меня было только ограниченное количество строк на экране). Итак, да, если появится клавиатура, мы изменим размер UITextView (или просто подтолкнем его вверх без изменения размера, но это около 50% будет скрыто). - person n.evermind; 09.03.2011
comment
В некотором смысле это похоже на наличие (1) приложения iBooks со СТАТИЧЕСКИМ текстом, который (2) внезапно превращается в приложение для заметок (со всеми тонкостями прокрутки), когда пользователь хочет отредактировать страницу. Ввод, однако, повлияет на форматирование остальной части (например, если он вставит другую страницу текста на страницу 2, например, нам нужно будет отправить этот контент на страницы 3 и 4 и т. д. - person n.evermind; 09.03.2011