+++28 Урок марта Apple3. Работа с Xcode.ppt
- Количество слайдов: 84
Работа с Xcode Проектирование интерфейса Шаблоны проектирования
Storyboard — это инструмент проектирования интерфейсов. Возможно, вы не понимаете, что делает это приложение, но четко видно, какие у него есть экраны и как они связаны. В этом сила storyboard.
• Если у вас есть приложение со множеством экранов, то storyboard может значительно уменьшить количество кода, который нужен для связей между экранами. • Вместо nib файла для каждого view controller, ваше приложение использует единый storyboard который включает в себя дизайн всех view controller и связей между ними. Storyboard имеет массу преимуществ перед nib-ами: • Storyboard — лучшее понимание концепта работы всех экранов и связей. Проще следить когда все в одном, чем открывать множество nib. • Storyboard описывает переходы между разными экранами. Эти переходы называются “segue” (сегвей) и вы создаете их путем ctrlперетаскивания с одного view controller на другой. Спасибо segue — теперь требуется меньше кода для написания UI. • Storyboards делают работу с table view много легче, с новыми фичами prototype cell and static cell. Вы можете проектировать table view почти полностью в storyboard editor, что значительно сокращает требуемый код.
у storyboard есть ограничения: • Storyboard Editor пока не такой мощный как Interface Builder, есть кое-что IB может, а Storyboard Editor нет. Также вам потребуется большой монитор, особенно если пишете i. Pad приложения! Если вы тот, кто ненавидит Interface Builder и хочет создавать весь UI в коде, тогда storyboard скорее всего не для вас. Хотите использовать nib — используйте, но знайте, что вы можете комбинировать storyboard и nib. Это не ситуация “или -или”.
Запускаем Xcode и создадим новый проект. Мы выберем шаблон Single View Application как базу и на этой базе построим свой проект.
Заполните поля в шаблоне: Product Name: Ratings Company Identifier: ЕNU Class Prefix: пусто Device Family: i. Phone Use Storyboard: поставьте √“галку” Use Automatic Reference Counting: поставьте √ “галку” Include Unit Tests: пусто
После создания проекта в Xcode, основное окно Xcode выглядит так:
Наш новый проект состоит из двух классов, App. Delegate и View. Controller, и Main. Storyboard. storyboard файл. Нет никаких. xib файлов в проекте, ни даже Main. Window. xib. Кликнем Main. Storyboard. storyboard файл в Project Navigator, чтобы открылся Storyboard Editor:
Storyboard Editor выглядит и работает почти как Interface Builder. Можно таскать контролы из Object Library (см. правый нижний угол) в ваш view controller, чтобы спроектировать его макет. Разница в том, что storyboard содержит не один view controller, а все. Официально в storyboard для этого есть термин “scene”, но scene это ничто иное, как view controller. Ранее нужен был nib для каждой сцены / view controller, но сейчас все они находятся в одном storyboard. На айфоне только одна из сцен может быть показана, но на айпэде может быть несколько, например основная и детальная панели в split-view, или контент в popover.
Чтобы открыть окно раскадровки необходимо. . . Выбрать Main. Storyboard. storyboard в навигаторе проекта. Xcode откроет раскадровку в окне редактора. Область позади объектов раскадровки, та, которая выглядит как линованная бумага, называется canvas. При открытии раскадровки окно рабочего пространства будет выглядеть следующим образом:
Раскадровка содержит сцены и переходы. • Сцена представляет собой view controller, а переход является переходом между двумя сценами. • Поскольку шаблон Single View предоставляет только один view controller, в раскадровке вашего приложения есть одна сцена, но ни одного перехода. Стрелка, которая указывает на левую часть сцены на canvas, является индикатором начальной сцены, она идентифицирует сцену, загружаемую при запуске приложения в первую очередь (как правило начальная сцена совпадает с inital view controller). • Сцена, которую вы видите на canvas, называется Hello World View Controller по причине того, что она управляется объектом Hello. World. View. Controller. • Сцена Hello World View Controller состоит из нескольких элементов, которые отображаются в Xcode outline view (панель между canvas и навигатором проекта).
Область на canvas под сценой называется scene dock. Он может содержать название контроллера или иконки, представляющие first responder и объект view controller. Чтобы посмотреть как работает editor, перетащите пару контролов во view controller:
В разделе View. Controller инспектора атрибутов вы увидите выбранную функцию Initial. Scene. Обратите внимание, что если вы отключите эту опцию, индикатор initialscene исчезнет из области canvas. Для дальнейшей работы урока проверьте еще раз, что выбрана опция Initial. Scene.
Добавление элементов пользовательского интерфейса Вы добавляете элементы пользовательского интерфейса (далее UIэлементы), перетаскивая их из библиотеки объектов к элементу view в области canvas. После того как UI-элементы добавлены в представление, вы можете менять их положение и размер. • • Чтобы добавить UI-элементы во view и расположить их соответствующим образом вам необходимо. . . При необходимости кликните на Main. Storyboard. storyboard в навигаторе проекта, чтобы отобразить сцену в поле canvas. При необходимости откройте библиотеку объектов. Библиотека объектов появится в нижней части области утилит. Если вы все еще не видите библиотеку объектов, кликните на третью кнопку слева на панели выбора библиотеки:
Изучение контроллера и его представления • VIEW CONTROLLER отвечает за управление сценой, которая представляет одну область контента. Контент, который вы видите в этой области, определяется с помощью представления этого элемента. Использование Inspector для исследования View Controller • • При запуске приложения загружается основной файл storyboard и создается экземпляр initial view controller. Initial view controller управляет первой сценой, которую видит пользователь при запуске приложения. В используемом шаблоне Single View представлен только один view controller, по этой причине автоматически устанавливается initial view controller. Вы можете подтвердить статус view controller и узнать подробнее о нем с помощью Xcode inspector.
В разделе View. Controller инспектора атрибутов вы увидите выбранную функцию Initial. Scene. Обратите внимание, что если вы отключите эту опцию, индикатор initialscene исчезнет из области canvas. Для дальнейшей работы урока проверьте еще раз, что выбрана опция Initial. Scene.
Панель слева это Document Outline: В Interface Builder эта область показывает компоненты nib-а, но в Storyboard Editor показано содержимое всех view controller. Сейчас у нас только один view controller в нашей storyboard, но позже мы добавим еще несколько.
мини версия Document Outline снизу сцены, называется Dock: Dock показывает высокоуровниваемые объекты в сцене. У каждой сцены есть хоть один First Responder объект и View Controller объект, но могут быть и другие высокоуровниваемые объекты. Dock удобен для создания связей. Если что-то нужно связать с view controller, просто потяните на его иконку в Dock-е.
Примечание: Скорее всего вы не будете часто пользоваться First Responder-ом. Объект-заполнитель first responder (на картинке обозначен кубиком оранжевого цвета). First responder — динамический заполнитель, представляющий объект, который при работе приложения первым будет получать различные события. К ним относятся события со смещением фокуса (к примеру, при тапе на текстовое поле для вызова клавиатуры), события движения (встряхивание устройства) и события сообщений (например, сообщение, отправляемое при нажатии кнопки). First Responder- это заместитель объекта, он ссылается на любой объект со статусом first responder. Он был еще в Interface Builder и скорее всего, вы никогда им не пользовались.
Запустите приложение, и оно будет выглядет так, как вы его спроектировали:
Если вы когда-либо делали приложение на базе nib, тогда у вас всегда был Main. Window. xib файл. Этот nib содержал объект высокого уровня UIWindow, ссылку на App Delegate, и один или несколько контроллеров. Когда вы поместили ваш UI в storyboard, Main. Window. xib больше не используется.
Так как же storyboard загружается приложением, если больше нет Main. Window. xib? Давайте взглянем на делегат приложения. Откройте App. Delegate. h и увидите: #import < UIKit/UIKit. h> @interface App. Delegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end Это - требование использовать storyboard, которое ваш делегат приложения наследует от UIResponder (ранее прямо внедрялся как NSObject) и у которого есть свойство UIWindow (теперь не IBOutlet).
Если вы заглянете в App. Delegate. m, вы увидите что он вообще ничего не делает, все методы практически пустые. Даже application: did. Finish. Launching. With. Options: попросту возвращает YES. Ранее, либо вид основного view controller устанавливался как window или же window root. View. Controller свойство, но ничего похоже сейчас. Секрет в Info. plist файле. Кликните на Ratings-Info. plist (находится в Supporting Files группе) и увидите:
nib-проектах был ключ в Info. plist по имени NSMain. Nib. File, или “Main nib file base name”, который указывал UIApplication загружать Main. Window. xib и привязывать его к приложению. Наш Info. plist больше не содержит такой записи. Вместо, приложения со storyboard содержат ключ UIMain. Storyboard. File, или “Main storyboard file base name”, указывающий имя storyboard, которая загружается при старте приложения. Если такой ключ есть, UIApplication загрузит файл Main. Storyboard. storyboard и автоматом инициирует первый view controller из storyboard приложит его view на новый объект UIWindow. И никакого кода.
Вы также увидите это в Target Summary: Здесь есть новая секция i. Phone/i. Pod Deployment Info в которой можно выбрать storyboard или nib файл.
И чтобы совсем все, также откройте main. m: #import < UIKit/UIKit. h> #import "App. Delegate. h" int main(int argc, char *argv[]) { @autoreleasepool { return UIApplication. Main(argc, argv, nil, NSString. From. Class([App. Delegate class])); } } Ранее, последний параметр UIApplication. Main() был nil, а сейчас NSString. From. Class([App. Delegate class]). Большая разница в том, что имея Main. Window. xib делегат приложения не является частью storyboard. Поскольку делегат приложения более не загружается из nib (или из storyboard), нужно указать UIApplication. Main какое конкретно имя у делегата приложения, или же он его не найдет.
У нашего приложения Ratings интерфейс с двумя закладками-табами. С помощью storyboard реально легко создавать табы. Переключитесь на Main. Storyboard. storyboard, и перетащите Tab Bar Controller из Object Library на канву. Tab Bar Controller идет в комплекте с двумя view controller и понадобится некоторое пространство для маневра.
Новый Tab Bar Controller идет в комплекте с двумя view controllers, по контроллеру на таб. UITab. Bar. Controller это так называемый контейнерный view controller, поскольку содержит один или несколько view controller. Два других наиболее известных контейнера это Navigation Controller и Split View Controller. Связь контейнера показана в Storyboard Editor стрелкой между Tab Bar controller и view controller-ами, которые он содержит.
Если надо передвинуть Tab Bar controller и связанные с ним view controller-ы группой, кликните Cmdклик чтобы выбрать сцены и передвинуть их. (Выбранные сцены будут выделяться толстой синей линией вокруг. ) Перетащите label на первый view controller и напишите текст “First Tab”. Также перетащите label на второй контроллер и напечатайте “Second Tab”. Это позволит увидеть, что в действительности происходит. Выберите Tab Bar Controller и перейдите к Attributes Inspector. Поставьте галку на Is Initial View Controller.
Стрелка на канве теперь показывает на Tab Bar Controller: Это значит, что когда вы запустите UIApplication он сделает Tab Bar Controller основным экраном приложения. Storyboard всегда имеет view controller, который указан как начальный view controller, и является начальной позицией в storyboard.
Запустим и посмотрим. У приложения есть tab bar и можно попереключаться между view controllers нажимая на табы: Xcode вообще-то поставляется с шаблоном для создания табприложения ( шаблон назван Tabbed Application) которое мы могли бы использовать, Можно удалить view controller который был добавлен из шаблона и не пользоваться им. Storyboard содержит tab bar и две сцены для табов. Если к Tab Bar Controller подсоединить больше пяти сцен, автоматом появится таб More…, когда вы запустите приложения.
Добавляем Table View Controller Две сцены, которые прикручены к Tab Bar Controller это самые обычные UIView. Controllers. Замените первую сцену на UITable. View. Controller. Кликните на первый View Controller и удалите его. Из Object Library перетащите новый Table View Controller на канву на место, где был первый контроллер:
С выбранным Table View Controller, выберите Editor -Embed In. Navigation Controller из меню Xcode. Это добавит view controller на канву:
Можно было перетащить Navigation Controller из Object Library, но Embed In команда проще. Поскольку Navigation Controller также контейнер (как и Tab Bar Controller), у него есть стрелка со связью с Table View Controller. Эти связи можно увидеть и в Document Outline:
Заметьте, что Table View Controller получил navigation bar. Storyboard Editor автоматом добавил ее, поскольку сцена находится внутри Navigation Controller. Это не настоящий объект UINavigation. Bar, а симулируемый. Если посмотреть в Attributes Inspector у Table View Controller, увидите в секции Simulated metrics наверху:
“Inferred” настройка для storyboard и подразумевает что сцена показывает navigation bar, когда сцена находится внутри navigation controller, tab bar когда внутри tab bar controller, и т. п. Можно перезаписать эти настройки, если захотите, но они здесь только для того, чтобы помочь с проектировкой. Simulated Metrics не используется во время рантайма, это просто подсказка к визуальному дизайну, на что будет похож ваш интерфейс после. Давайте свяжем две сцены и Tab Bar Controller. Ctrl-перетаскивание с Tab Bar Controller на Navigation Controller:
Когда отпустите, появится всплывающее меню: Выберите Relationship – view. Controllers. Это создаст новую связывающую стрелку:
У Tab Bar Controller две таких связи, по одной на таб. Navigation Controller сам имеет связь с Table View Controller. Также есть другой тип стрелки, segue, поговорим о ней позже. Когда мы сделали эту связь, новый tab был добавлен в Tab Bar Controller, попросту названный “Item”. Я хочу сделать эту сцену первым табом, так что перетаскиваю таб, меняя порядок:
Запустите приложение. Первый таб содержит table view внутри navigation controller. Переименуем первый таб в “Players” и второй в “Gestures”. В Tab Bar Controller это не изменить, но в соответствующих view controller -ах которые связаны с этими табами. Как только подсоедините view controller к Tab Bar Controller, он получает объект Tab Bar Item. Вы используете Tab Bar Item для конфигурирования заголовка таба и его картинки.
Выберите Tab Bar Item внутри Navigation Controller и в Attributes Inspector установите заголовок Title в “Players”:
Переименуйте Tab Bar Item для view controller со второго таба в “Gestures”. Также надо добавить картинки к табам. Добавьте папку с рисунками в проект. В Attributes Inspector для Players Tab Bar Item, выберите одну картинку, для Gestures item другую картинку Похожим образом, view controller внутри Navigation Controller содержит Navigation Item для конфигурации navigation bar. Выберите Navigation Item для Table View Controller и поменяйте заголовок в Attributes Inspector на “Players”. Либо, двойной клик на navigation bar и впишите название. (Заметка: Двойной клик на симулируемой navigation bar в Table View Controller, не настоящей Navigation Bar в Navigation Controller. ) Разместите 2 рисунки на этих табах в image View
Запустите приложение и подивитесь на замечательную tab bar, и все без единой строчки кода!
Prototype cells Вы наверняка уже заметили, что как мы добавили Table View Controller, Xcode ругается: Предупреждающее сообщение, “Конфигурация не поддерживается: прототипы табличных яцеек должны иметь идентификаторы повторного использования”. Когда вы добавили Table View Controller на storyboard, он хочет использовать prototype cells по умолчанию, а у нас их нет, отсюда и предупреждение. Prototype cells одна из клевых штук, которые есть storyboard, в отличие от nib. Ранее, если вы хотели использовать table view cell со своим дизайном, либо приходилось добавлять свои виды программно, либо делать отдельный nib специально для ячейки и загружать его из другого nib-a. Это все еще возможно, но prototype cell облегчает жизнь.
Теперь можно изменять дизайн ячеек в storyboard editor. Table View Controller идет с пустой prototype cell. Кликните по ней, и в Attributes Inspector установите Style на Subtitle. Это сразу изменит ее вид на другой, с двумя лейблами. С prototype cell вы можете выбрать один из встроенных стилей, либо создать свой собственный дизайн (мы так и сделаем).
Установите атрибут Accessory на Disclosure Indicator впишите Reuse Identifier “Player. Cell”. Теперь Xcode больше не ругается. Все prototype cells все еще обычные UITable. View. Cell объекты и потому должны иметь reuse identifier. Xcode должен быть уверен, что мы не забыли (для тех, кто обращает внимание на эти предупреждения). Запустим, и… ничего не изменилось. Не так уж и странно, ведь нам нужно иметь источник данных для таблицы. Добавьте новый файл в проект. Выберите подкласс UIView. Controller шаблон. Назовите класс Players. View. Controller и сделайте его подклассом UITable. View. Controller. With XIB должна быть не отмечена, поскольку мы уже проектируем view controller в storyboard. Никаких nib!
Вернемся в Storyboard Editor и выберем Table View Controller. В Identity Inspector, установим Class на Players. View. Controller. Это необходимый шаг для связывания сцены storyboard со своим подклассом view controller. Не забудьте это сделать или же ваш класс не будет использован!
Отныне когда вы запускаете приложение table view controller из storyboard является instance нашего класса Players. View. Controller. Добавьте свойство mutable array в Players. View. Controller. h: #import < UIKit/UIKit. h> @interface Players. View. Controller : UITable. View. Controller @property (nonatomic, strong) NSMutable. Array *players; @end Этот массив будет содержать основной массив данных. Он содержит объекты Player. Сделаем класс Player. Добавьте новый файл в проект используя шаблон класса Objective-C. Назовите его Player, подкласс NSObject.
Измените Player. h на следующее: @interface Player : NSObject @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *game; @property (nonatomic, assign) int rating; @end И измените Player. m на: #import "Player. h" @implementation Player @synthesize name; @synthesize game; @synthesize rating; @end
Player обычный контейнер объект для: имени игрока, игры, в которую он режется, и рейтинга (от 1 до 5 звезд). Сделаем массив и несколько тестовых объектов Player в нашем App Delegate и затем присвоим их к свойств players Players. View. Controller. В App. Delegate. m, добавьте #import для Player и Players. View. Controller классы в начало файла, и добавьте instance variable по имени players: #import "App. Delegate. h" #import "Players. View. Controller. h" @implementation App. Delegate { NSMutable. Array *players; } // Rest of file. . .
После измените did. Finish. Launching. With. Options метод: - (BOOL)application: (UIApplication *)application did. Finish. Launching. With. Options: (NSDictionary *)launch. Options { players = [NSMutable. Array array. With. Capacity: 20]; Player *player = [[Player alloc] init]; player. name = @“Marat"; player. game = @"Tic-Tac-Toe"; player. rating = 4; [players add. Object: player]; player = [[Player alloc] init]; player. name = @“Asel"; player. game = @“Tetris"; player. rating = 5; [players add. Object: player]; player = [[Player alloc] init]; player. name = @“Nurlan"; player. game = @"Poker"; player. rating = 2; [players add. Object: player]; UITab. Bar. Controller *tab. Bar. Controller = (UITab. Bar. Controller *)self. window. root. View. Controller; UINavigation. Controller *navigation. Controller = [[tab. Bar. Controller view. Controllers] object. At. Index: 0]; Players. View. Controller *players. View. Controller = [[navigation. Controller view. Controllers] object. At. Index: 0]; players. View. Controller. players = players; return YES; }
Просто создаем несколько объектов Player и добавляем из в массив players. А после делаем следующее: UITab. Bar. Controller *tab. Bar. Controller = (UITab. Bar. Controller *) self. window. root. View. Controller; UINavigation. Controller *navigation. Controller = [[tab. Bar. Controller view. Controllers] object. At. Index: 0]; Players. View. Controller *players. View. Controller = [[navigation. Controller view. Controllers] object. At. Index: 0]; players. View. Controller. players = players; Мы хотим назначить массив players свойству players от Players. View. Controller, чтобы использовать этот массив, как источник данных. Но делегат приложения не знает ничего о Players. View. Controller, так что он будет продираться через storyboard чтобы найти. Это одно из ограничений storyboard
С Interface Builder у вас всегда была ссылка на App Delegate в Main. Window. xib и всегда можно было делать связи с контроллеров высокого уровня в outlets App Delegate. Сейчас это невозможно для storyboard. Нельзя сделать ссылки на app delegate view controller-ов высокого уровня. Но всегда можно сделать эти ссылки в коде. UITab. Bar. Controller *tab. Bar. Controller = (UITab. Bar. Controller *) self. window. root. View. Controller; Мы знаем, что для storyboard начальный view controller это Tab Bar Controller, так что мы можем взять window root. View. Controller и указать его.
Players. View. Controller находится внутри navigation controller в первом табе, так что укажем это объект UINavigation. Controller: UINavigation. Controller *navigation. Controller = [[tab. Bar. Controller view. Controllers] object. At. Index: 0]; и затем запросим его исходный view controller, которым является Players. View. Controller: Players. View. Controller *players. View. Controller = [[navigation. Controller view. Controllers] object. At. Index: 0];
у UINavigation. Controller нет свойства root. View. Controller, так что мы достанем его из массива view. Controllers. (Есть свойство top. View. Controller, но оно указывает на контроллер на вершине стека, а мы ищем последний, который на дне. Приложение только что было запущено, поэтому технически мы могли бы использовать top. View. Controller, но это не всегда так. ) Теперь, когда у нас полный массив объектов Player, мы продолжим делать источник данных для Players. View. Controller.
Откройте Players. View. Controller. m, и измените методы table view data source на: - (NSInteger)number. Of. Sections. In. Table. View: (UITable. View *)table. View { return 1; } - (NSInteger)table. View: (UITable. View *)table. View number. Of. Rows. In. Section: (NSInteger)section { return [self. players count]; }
Настоящая работа происходит в cell. For. Row. At. Index. Path. Версия из шаблона Xcode: - (UITable. View. Cell *)table. View: (UITable. View *)table. View cell. For. Row. At. Index. Path: (NSIndex. Path *)index. Path { static NSString *Cell. Identifier = @"Cell"; UITable. View. Cell *cell = [table. View dequeue. Reusable. Cell. With. Identifier: Cell. Identifier ]; if (cell == nil) { cell = [[UITable. View. Cell alloc] init. With. Style: UITable. View. Cell. Style. Default reuse. Identifier: Cell. Identifier]; } // Configure the cell. . . return cell; }
Это, код, который приходилось писать ранее. Не теперь! Замените метод на - (UITable. View. Cell *)table. View: (UITable. View *)table. View cell. For. Row. At. Index. Path: (NSIndex. Path *)index. Path { UITable. View. Cell *cell = [table. View dequeue. Reusable. Cell. With. Identifier: @"Player. Cell"]; Player *player = [self. players object. At. Index: index. Path. row]; cell. text. Label. text = player. name; cell. detail. Text. Label. text = player. game; return cell; }: Последнее, что нужно сделать, чтобы создать новую ячейку: ITable. View. Cell *cell = [table. View dequeue. Reusable. Cell. With. Identifier: @"Player. Cell"];
Если ячейки, которую можно использовать, не существует, автоматом создастся prototype cell. Все, что нужно сделать, указать reuse identifier который вы указали в prototype cell в storyboard editor, в нашем случае “Player. Cell”. Не забудьте установить этот идентификатор, или эта схема не сработает! Поскольку класс не знает ничего от объектах Player, нужно импортировать заголовок #import в начале файла: #import "Player. h " Также надо не забыть синтезировать свойство: @synthesize players;
Теперь, когда запустим приложение, в нем есть игроки:
Заметка: В этом приложении мы используем только одну prototype cell, но если в вашей таблице нужно использовать разные виды ячеек, просто добавьте prototype cells в storyboard. Можно сделать копии существующей ячейки чтобы создать новые, либо увеличить число в аттрибуте Table View’s Prototype Cells. Задайте каждой ячейке свой неповторимый identifier
Проектируем Prototype Cells Использовать стандартный стиль ячейки нормально для большинства приложений, но мы хотим добавить картинку на правую сторону ячейки, которая показывает рейтинг игрока (количество звезд). Image view не поддерживается стандартными стилями, так что мы создадим свой собственный дизайн. Вернемся в Main. Storyboard. storyboard, выберем prototype cell в table view, установим его аттрибут Style в Custom. Лейблы по умолчанию — исчезли. Сперва сделаем ячейку уже. Перетащите ее границу снизу или измените значение Row Height в Size Inspector, чтобы сделать ячейку высотой в 55 пунктов. Перетащите два объекта Label с Objects Library на ячейку и расставьте их приблизительно там, где были предыдущие. Измените шрифт и цвета. Не выставляйте Highlighted color обоих лейблов белым. Будет куда лучше, когда юзер нажмет на ячейку и ее фон станет синим. .
Перетащите Image View на ячейку чуть правее, рядом с disclosure indicator. Сделайте его ширину 81 пункт, высота не так важна. Установите его Mode на Center (под View в Attributes Inspector) так, чтобы картинка, которую мы присвоим этому виду не растягивалась. Сделайте лейблы 210 пунктов в ширину, для того, чтобы они не заслоняли image view. Конечный дизайн prototype cell выглядит как-то так:
Поскольку ячейка с нестандартным дизайном, мы более не можем использовать UITable. View. Cell свойства text. Label и detail. Text. Label, чтобы задать текст этим лейблам. Эти свойства относятся к лейблам, которые больше не показываются на нашей ячейке. Вместо этого, будем использовать тэги, чтобы найти нужные лейблы. Задайте для Name label тэг 100, для Game label тэг 101 и для Image View тэг 102. Это можно сделать в Attributes Inspector.
Теперь откроем Players. View. Controller. m и заменим cell. For. Row. At. Index. Path из Players. View. Controller на: - (UITable. View. Cell *)table. View: (UITable. View *)table. View cell. For. Row. At. Index. Path: (NSIndex. Path *)index. Path { UITable. View. Cell *cell = [table. View dequeue. Reusable. Cell. With. Identifier: @"Player. Cell"]; Player *player = [self. players object. At. Index: index. Path. row]; UILabel *name. Label = (UILabel *)[cell view. With. Tag: 100]; name. Label. text = player. name; UILabel *game. Label = (UILabel *)[cell view. With. Tag: 101]; game. Label. text = player. name; UIImage. View * rating. Image. View = (UIImage. View *) [cell view. With. Tag: 102]; rating. Image. View. image = [self image. For. Rating: player. rating]; return cell; }
десь используется новый метод, image. For. Rating. Добавьте этот метод поверх cell. For. Row. At. Index. Path: - (UIImage *)image. For. Rating: (int)rating { switch (rating) { case 1: return [UIImage image. Named: @"1 Star. Small. png"]; case 2: return [UIImage image. Named: @"2 Stars. Small. png"]; case 3: return [UIImage image. Named: @"3 Stars. Small. png"]; case 4: return [UIImage image. Named: @"4 Stars. Small. png"]; case 5: return [UIImage image. Named: @"5 Stars. Small. png"]; } return nil; }
Теперь, все как надо. Запустите приложение снова.
Мы изменили высоту prototype cell, но table view не обратила на это внимание. Два варианта исправить это: 1. можно изменить table view аттрибут Row Height или 2. сделать метод height. For. Row. At. Index. Path. Первое намного проще, так что так и сделаем. Примечание: Можно использовать height. For. Row. At. Index. Path если вы пока что не знаете, какая высота должна быть у ячеек, либо если используются разные ряды с разными высотами. Вернемся к Main. Storyboard. storyboard, в Size Inspector у Table View, установите Row Height на 55:
Кстати, если вместо того, чтобы изменить высоту заданием значения, вы потянули саму ячейку, тогда у table view свойство Row Height автоматом поменялось тоже. Если вы запустите приложение теперь, то оно смотрится гораздо лучше!
Используем подкласс Prototype Cell Наш table view уже достаточно неплохо работает, но я вот не фанат обращаться через тэги к лейблам и другим видам у prototype cell. Будет приятней, если мы свяжем лейблы с аутлетами и будем обращаться к ним, через соответствующие свойства. Да, можно и так. Добавьте новый файл в проект, с шаблоном класса Objective-C. Назовите его Player. Cell и сделайте подклассом UITable. View. Cell. Измените Player. Cell. h на: @interface Player. Cell : UITable. View. Cell @property (nonatomic, strong) IBOutlet UILabel *name. Label; @property (nonatomic, strong) IBOutlet UILabel *game. Label; @property (nonatomic, strong) IBOutlet UIImage. View *rating. Image. View; @end
Замените содержимое Player. Cell. m на: #import "Player. Cell. h" @implementation Player. Cell @synthesize name. Label; @synthesize game. Label; @synthesize rating. Image. View; @end Сам класс ничего не делает, просто добавляет свойства для name. Label, game. Label и rating. Image. View. Вернемся в Main. Storyboard. storyboard, выберем prototype cell и изменим ее Class на “Player. Cell” в Identity Inspector. Теперь, когда бы table view не запросила новую ячейку с dequeue. Reusable. Cell. With. Identifier, вернется экземпляр Player. Cell вместо обычной UITable. View. Cell.
я назвал класс так же, как и reuse identifier — они оба Player. Cell. Имя класса и reuse identifier не имеют ничего общего друг с другом, так что, если очень хочется, можно их назвать и по-разному. Теперь можно подсоединить лейблы и image view к этим аутлетам. Либо выберите лейбл и тяните из его Connections Inspector на ячейку table view, либо ctrlперетаскивание с ячейки table view на лейбл:
Важно: Вы должны привязать контролы к ячейке table view cell, а не к контроллеру! Видите ли, в то время, как источник данных запрашивает у table view новую ячейку при помощи dequeue. Reusable. Cell. With. Identifier, table view дает не prototype cell, а *копию* (либо одну из ранее использованных ячеек). Так что, в одно и то же время может быть несколько экземпляров Player. Cell. Если связать лейбл ячейки с аутлетом контроллера, тогда несколько копий лейбла будут пытаться использовать один аутлет. Это значит самому вырыть себе яму. (С другой стороны, связывать prototype cell и actions контроллера очень даже нормально. Это можно сделать с добавленными кнопками или другими UIControl вашей ячейки. )
Теперь, когда свойства подсоединены, можно еще больше упростить источник данных: - (UITable. View. Cell *)table. View: (UITable. View *)table. View cell. For. Row. At. Index. Path: (NSIndex. Path *)index. Path { Player. Cell *cell = (Player. Cell *)[table. View dequeue. Reusable. Cell. With. Identifier: @"Player. Cell"]; Player *player = [self. players object. At. Index: index. Path. row]; cell. name. Label. text = player. name; cell. game. Label. text = player. game; cell. rating. Image. View. image = [self image. For. Rating: player. rating]; return cell; }
Теперь можно преобразовать объект, полученный из dequeue. Reusable. Cell. With. Identifier в Player. Cell, и после просто использовать свойста, привязанные к лейблам и image view. Очень нравится как prototype cell делают table view наименее запутанным! Теперь нужно импортировать класс Player. Cell, чтобы все заработало: #import "Player. Cell. h " Запустите приложение еще раз. Теперь, когда приложение запущено оно выглядит так же, как и до того, но за сценой мы используем подкласс ячейки table view!
Советы. Есть пара вещей, которые нужно знать перед разработкой собственных ячеек table vie. Сперва, нужно установить выделенный цвет лейблов так, чтобы они хорошо смотрелись когда юзер нажмет на них:
Второе, содержимое меняется в зависимости от изменения размеров ячейки. Например, ячейки будут менять свой размер, если вы добавите возможность добавлять или удалять ряды ячеек. Добавьте этот метод в Players. View. Controller. m: - (void)table. View: (UITable. View *)table. View commit. Editing. Style: (UITable. View. Cell. Editing. Style)editi ng. Style for. Row. At. Index. Path: (NSIndex. Path *)index. Path { if (editing. Style == UITable. View. Cell. Editing. Style. Delete) { [self. players remove. Object. At. Index: index. Path. row]; [table. View delete. Rows. At. Index. Paths: [NSArray array. With. Object: index. Path] with. Row. Animation: UITable. View. Row. Animation. Fade]; } }
Когда указан такой метод, провести-пальцем-чтобыудалить будет у таблицы включен. Запустите приложение и проведите пальцем по ряду, чтобы посмотреть, что происходит.
Кнопка Удалить скользит по ячейке и слегка наезжает на картинку со звездами. Что происходит, так это ячейка меняет свой размер, чтобы предоставить пространство для кнопки Удалить, но image view за ней не следует. Исправим это, откройте Main. Story. Board. storyboard, выберите image view в ячейке table view, и в Size Inspector измените значение Autosizing, что бы оно прилипало к виду-родителю справа:
После таких изменений, кнопка Удалить будет выталкивать картинку:
+++28 Урок марта Apple3. Работа с Xcode.ppt