Как разделить строки для UIScrollView/UITextView в зависимости от размера кадра

В настоящее время я пытаюсь читать в RSS-канал и разделить длинный текст с параграфом в UIScrollView с включенной подкачки страниц. Мне нужно разделить текст на разные страницы, которые соответствуют размеру каждой страницы, и отделить строку как таковую. Я не уверен, существует ли какой-либо стандарт, как это делать, и я предполагаю, что именно так большинство приложений для чтения RSS разделяют свою информацию на нескольких страницах. Кто-нибудь знает, как с этим бороться? Я не хотел смотреть по буквам, пока текст не уместился и продолжился.

редактировать:

Это хорошее начало, но пример кода в значительной степени сталкивается с проблемами, которые я пытался избежать, и не знаю, как обойти. Этот диапазон вычисляется неправильно для UITextView. Я меняю шрифт и такие как видно ниже. Все пытается вычисляться внутри - (NSRange)visibleRangeOfTextView:(UITextView *)textView. Этот метод вызывается параметром -(void) AdjustTextDisplay, который вызывается внешним классом после установки текста для UITextView. Я понятия не имею, почему установка размера контента на размер кадра экрана не ограничивает представление (как показано ниже), и я не знаю, почему этот метод возвращает полную длину строки в качестве диапазона.

2 ответа

Решение

Начиная с iOS 7, есть гораздо более элегантное решение с использованием TextKit, которое я включил в пример кода ниже. Идея состоит в том, чтобы менеджер компоновки TextKit обрабатывал разделение глифов и правильно все расставлял. Это предотвращает обрезание слов в середине пути и тонну гибкости:

class BookView: UIScrollView {

    var bookMarkup: NSAttributedString!
    private let layoutManager = NSLayoutManager()

    override func layoutSubviews() {
        super.layoutSubviews()

        if layoutManager.textContainers.count == 0 {
            buildFrames()
        }
    }

    func buildFrames() {
        let textStorage = NSTextStorage(attributedString: bookMarkup)

        textStorage.addLayoutManager(layoutManager)

        var range = NSMakeRange(0, 0)
        var containerIndex = 0

        while NSMaxRange(range) < layoutManager.numberOfGlyphs {
            let textViewRect = frameForViewAtIndex(containerIndex)
            let containerSize = CGSizeMake(CGRectGetWidth(textViewRect), CGRectGetHeight(textViewRect) - 16) //UITextView adds an 8 margin above and below the container so we take that into consideration here with the 16. heightTracksTextView causes a performance hit when adding multiple containers... so we don't do that instead
            let textContainer = NSTextContainer(size: containerSize)
            layoutManager.addTextContainer(textContainer)

            let textView = UITextView(frame: textViewRect, textContainer: textContainer)

            addSubview(textView)

            containerIndex++

            range = layoutManager.glyphRangeForTextContainer(textContainer)
        }

        contentSize = CGSize(width: CGRectGetWidth(bounds) / 2 * CGFloat(containerIndex), height: CGRectGetHeight(bounds))

        pagingEnabled = true
    }

    private func frameForViewAtIndex(index: Int) -> CGRect {

        var textViewRect = CGRect(origin: CGPointZero, size: CGSize(width: CGRectGetWidth(bounds)/2, height: CGRectGetHeight(bounds)))
        textViewRect = CGRectInset(textViewRect, 10, 20)
        textViewRect = CGRectOffset(textViewRect, CGRectGetWidth(bounds) / 2 * CGFloat(index), 0)
        return textViewRect
    }
}

Вместо тяжелых итеративных вычислений я бы установил полный (начиная с конца предыдущей страницы) текст в textView и получил бы последнюю отображаемую позицию символа. Тогда легко выполнить быстрый обратный поиск, чтобы усечь слово / предложение.


У меня есть следующее решение, немного хитрая часть пропуска последней частично отображаемой строки, чтобы избежать прокрутки и сделать ее лучше. Вам все еще нужно двигаться endCutIndex сделать это словом или предложением.

Базовый проект с пейджером, а не с текстовыми представлениями, взят здесь

- (void)viewDidLoad {
    [super viewDidLoad];

    NSString * fullText = @"Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.";

    pageControlBeingUsed = NO;


    int pageNumber = 0;
    UIFont * theFont = [UIFont boldSystemFontOfSize:30];
    const CGSize charSize = [@"A" sizeWithFont:theFont];

    while (fullText.length > 0) {
        CGRect frame;
        frame.origin.x = self.scrollView.frame.size.width * (pageNumber++);
        frame.origin.y = 0;
        frame.size = self.scrollView.frame.size;

        UIView *subview = [[UIView alloc] initWithFrame:frame];

        UITextView * textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
        textView.font = theFont;
        [subview addSubview:textView];
        [textView release];


        textView.text = fullText;

        CGRect bounds = textView.bounds;
        // - charSize.height to skip a partially visible line
        // - charSize.width*2 to skip annoying character still displayed at partially visible line
        CGPoint endPoint = CGPointMake(CGRectGetMaxX(bounds) - charSize.width*2, CGRectGetMaxY(bounds) - charSize.height);
        UITextPosition *start = [textView characterRangeAtPoint:bounds.origin].start;
        UITextPosition *end = [textView characterRangeAtPoint:endPoint].end;

        const int startCutIndex = [textView offsetFromPosition:textView.beginningOfDocument toPosition:start];
        const int endCutIndex =   [textView offsetFromPosition:textView.beginningOfDocument toPosition:end];

        NSString * cutText = [fullText substringToIndex:endCutIndex];
        textView.text = cutText;
        fullText = [fullText substringFromIndex:endCutIndex];

        [self.scrollView addSubview:subview];
        [subview release];

        NSLog(@"Page (1-total) %d, start text index %d, end text index %d \ntext:%@", pageNumber, startCutIndex, endCutIndex, cutText);
    }

    const int totalPages = pageNumber;

    self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * totalPages, self.scrollView.frame.size.height);

    self.pageControl.currentPage = 0;
    self.pageControl.numberOfPages = totalPages;
}

Вот немного файла.h:

@interface FCContentViewController : UIViewController <UIScrollViewDelegate, UITextViewDelegate>{
    UIPageControl *pageControl;   
    NSString *trunkedString;
    UITextView *bodyText;
}
@property (nonatomic, retain) UIScrollView *scrollView;
@property (nonatomic, retain) NSString *bodyTextString;
Другие вопросы по тегам