Подтвердить что ты не робот

Quartz 2D метод drawRect (iPhone)

У меня есть 4 разных iPhone/Cocoa/Core Animation/Objective-C книги передо мной, а также многочисленные примеры кода из Интернета. Но почему-то я все еще чувствую, что мне не хватает фундаментального понимания того, как рисунок работает в Quartz 2D.

Является ли drawRect() просто крючком для выполнения вашего кода чертежа? Или этот метод должен также перерисовывать регионы, которые "повреждены", и нуждаются в перекраске? Могу ли я просто нарисовать свой материал один раз, а затем он "прилипает", или я должен перерисовать всю сцену в любое время через drawRect()? Объект Java Graphics2D работает таким образом - вы должны нарисовать весь свой "образ" каждый раз, когда вызывается paint(), поэтому вы должны быть готовы его перестроить в любое время (или кэшировать).

Как бы вы реализовали простую программу рисования? Вам нужно было "запомнить" каждую строку/точку/ход, которые пользователь рисовал, и реплицировать каждый раз, когда вызывается drawRect()? Как насчет "закадрового" рендеринга; можете ли вы сделать весь свой чертеж, а затем вызвать [self setNeedsDisplay], чтобы ваши записи покраснели на экран?

Скажем, что в ответ на прикосновение пользователя я хочу поставить "X" на экран, где он коснулся. X должен оставаться там, и каждый новый контакт создает другой X. Должен ли я запомнить все эти координаты касания, а затем нарисовать их все в drawRect()?

EDIT:

Если я не понял, ответы на joconor и Hector Ramos ниже противоречат друг другу. И это хорошая демонстрация моей путаницы в отношении этой темы.: -)

4b9b3361

Ответ 1

Некоторая путаница между различными ссылками Cocoa исходит из введения в Leopard видов с поддержкой слоев. На iPhone все UIViews поддерживаются с помощью слоя, где в представлениях Leopard необходимо вручную включить поддержку слоев.

Для просмотра с поддержкой слоя содержимое выводится один раз с использованием того, что вы указали в drawRect(), но затем буферизуется в этот слой. Слой действует как прямоугольная текстура, поэтому, когда вы перемещаете представление с поддержкой слоя или закрываете его, перерисовать не нужно, текстура просто перемещается в это место с помощью графического процессора. Если вы не установили needsDisplayOnBoundsChange property на YES для слоя, изменение размера слоя (или его содержимого) просто масштабирует содержимое. Это может привести к размытой графике в вашем представлении или слое, поэтому в этом случае вам может потребоваться перерисовать. setNeedsDisplay вызовет ручную перерисовку содержимого вида или слоя и последующее удаление этого содержимого в слое.

Для обеспечения оптимальной производительности рекомендуется избегать частых звонков на drawRect, поскольку рисование и извлечение кварца на слое являются дорогостоящими операциями. Лучше всего попытаться сделать анимацию, используя отдельные слои, которые вы можете перемещать или масштабировать.

Ссылки на Cocoa, которые вы видели, относящиеся к рабочему столу, могут предполагать представления, не поддерживающие слои, которые вызывают drawRect: в любое время, когда представление необходимо обновить, будь то движение, масштабирование или с закрытой точкой зрения. Как я уже сказал, все UIViews поддерживаются с помощью слоя, так что это не так на iPhone.

Тем не менее, для вашего приложения рисования одним из способов сделать это было бы поддержание массива рисованных объектов и вызов drawRect: каждый раз, когда пользователь добавляет что-то новое, повторяется по каждому из ранее нарисованных объектов по порядку. Я мог бы предложить альтернативный подход, когда вы создаете новый UIView или CALayer для каждой операции рисования. Содержимое этой операции рисования (строка, дуга, X и т.д.) Будет отображаться отдельным представлением или слоем. Таким образом, вам не придется перерисовывать все на новом прикосновении, и вы могли бы сделать некоторое аккуратное редактирование в векторном стиле, перемещая каждый из рисованных элементов независимо от других. Для сложных чертежей в этом может быть немного компромиссов с памятью, но я бы поставил на то, что он будет иметь гораздо лучшую производительность рисования (минимальное использование процессора и мерцание).

Ответ 2

drawRect() будет рисовать в буфер вне экрана. Вам не нужно перерисовывать в любое время, когда регионы "повреждены", так как iPhone OS заботится об обработке слоев. Вы просто пишете один раз в буфер и позволяете ОС обрабатывать все остальное. Это не похоже на другие среды программирования, где вам нужно постоянно перерисовывать, когда что-то проходит над вашим представлением.

Ответ 3

Всегда будьте готовы рисовать соответствующую область вашего вида, когда вызывается drawRect:.

Хотя система может буферизовать ваше представление, это приведет к тому, что drawRect: не будет вызван. Если по какой-то причине система должна аннулировать буфер, ваш метод drawRect: может быть снова вызван. Кроме того, drawRect: будет вызываться для разных областей вашего представления, поскольку они становятся видимыми в результате прокрутки и других операций, которые влияют на видимость областей вашего представления.

Ответ 4

Является ли drawRect() просто крючком для выполнения вашего кода чертежа?

Предполагается перерисовать область (rect), которая передается вам, используя текущий стек контекста графики. Больше.

Или этот метод должен также перерисовывать регионы, которые "повреждены", и нужно перекрасить?

Нет. Если грязные области не перекрываются, вы можете получить несколько вызовов drawRect: с разными адресами, переданными вам. Ректы аннулируются с помощью setNeedsDisplayInRect:. Если только часть поверхности вашего изображения должна быть перерисована, вам будет предложено нарисовать эту часть, когда пришло время рисовать.

Могу ли я просто нарисовать свой материал один раз, а затем он "прилипает", или я должен перерисовать всю сцену в любое время через drawRect()?

Он не "прилипает". Ректы становятся недействительными во время выполнения вашего приложения, и вам предлагается перерисовать эти прямоугольники, когда системе просмотра необходимо обновить экран. Вы перерисовываете только исправленный запрос.

В некоторых случаях простая реализация может (довольно лениво) аннулировать представление всего прямоугольника всякий раз, когда часть недействительна. Это обычно плохо, потому что обычно требуется больше рисования, чем это необходимо, и особенно расточительно, когда представления не непрозрачны.

Объект Java Graphics2D работает таким образом - вы должны нарисовать свое "изображение" каждый раз, когда вызывается paint(), поэтому вы должны быть готовы к его повторной конструкции в любое время (или кэшировать его).

Не так с AppKit или UIKit.

Как бы вы реализовали простую программу рисования? Вам нужно "запомнить" каждую строку/точку/ход, которые пользователь нарисовал, и повторить, что каждый раз, когда вызывается drawRect()?

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

Для сложного рендеринга может быть проще или эффективнее рисовать внешний буфер (например, растровое изображение), а затем использовать это предварительное представление растрового изображения, чтобы получить информацию на экране, находясь в drawRect:. (см. также ответ Брэда для слоев)

Как насчет рендеринга "вне экрана"; можете ли вы сделать весь свой чертеж, а затем вызвать [self setNeedsDisplay], чтобы ваши записи покраснели на экран?

Да, вы можете это сделать. В частности, вы должны визуализировать внешний буфер (например, растровое изображение), когда вы закончите рендеринг в растровое изображение, сделайте недействительным прямоугольник, который хотите нарисовать, затем нарисуйте на экране, используя данные в растровом изображении, когда вызывается drawRect:.

Скажем, что в ответ на прикосновение пользователя я хочу поставить "X" на экран, где он коснулся. X должен оставаться там, и каждый новый контакт создает другой X. Нужно ли мне помнить все эти координаты касания, а затем нарисовать их все в drawRect()?

Ну, у вас есть несколько вариантов. Ваше предложение - одно (предполагая, что вы нарисовали в пределах прямоугольника, переданного вам). Другим было бы создать представление "X" и просто запомнить точки, необходимые для восстановления представления, если вам нужны эти Xs для продолжения запусков. Во многих случаях вы можете легко разделить сложные задачи на слои (простая 2D-игра):

  • 1) Фоновое изображение с горизонтом.
  • 2) Некоторые вещи на переднем плане часто не меняются.
  • 3) Персонаж игрока, которого пользователь использует для навигации по игре.

Таким образом, большинство проблем можно легко разделить, поэтому вам не нужно постоянно делать все. Это снижает сложность и улучшает производительность, если все сделано хорошо. Если это будет сделано плохо, это может быть несколько хуже.