Вы можете установить значения отступа для контента ячейки через поля Level (свойство indentationLevel) и Width (свойство indentationWidth) секции Indentation. Значение Width применяется для каждого уровня отступа. Вы можете указать использовать отступ в режиме редактирования, выбрав опцию Indent While Editing (свойство shouldIndentWhileEditing).
Выбор опции Shows Re-order Controls (свойство showsReorderControl) приведет к тому, что ячейка выведет элемент управления, позволяющий изменить порядок ее в таблице, находящейся в режиме редактирования. Однако вы должны также реализовать метод tableView:moveRowAtIndexPath:toIndexPath:, UITableViewDataSource протокола, и возвращать YES из метода tableView:canMoveRowAtIndexPath:. Эта часть должна быть выполнена программно.
Чтобы осведомить таблицу о заголовке и нижнем колонтитуле, вам нужно зарегистрировать их. Вы должны сделать это с помощью методов registerNib:forCellReuseIdentifier: или registerClass:forCellReuseIdentifier:. По подобию ячеек таблицы, заголовки и нижние колонтитулы переиспользуют идентификатор (Identifier) представляющий строку, для многократного создания их в таблице. Используйте метод initWithReuseIdentifier: при их инициализации.
(Курсера)
Все, что вам нужно сделать, чтобы избавиться от этих строк, - это щелкнуть сам TableView, CategoryTable, щелкнуть инспектор атрибутов и посмотреть разделитель, как он говорит default, и изменить его на none. Тогда давайте запустим его еще раз и посмотрим, как он выглядит.
Замечательно, это похоже на изображение здесь, но не очень много кода. Я думаю, что наш текст похож на Черноватый, может быть, темно-серый, но мне все равно. Я не собираюсь говорить ДИЗАЙНЕРУ, Если ты этого не сделаешь, кого это волнует? Хотя это выглядит неплохо. Итак, что мы сделали здесь, так это установили изображения динамически, как если бы мы получили их с сервера. Мы также установим названия этих ячеек там динамически, как если бы мы получили их с какого-то сервера, и мы повторно заполняем данные, а затем, когда гипотетически, когда рубашки, одежда и прочее, продукты будут меняться онлайн, они будут меняться здесь автоматически, что действительно здорово. Так что это выглядит неплохо. И еще одно: давайте избавимся от этих уродливых скроллингов. Итак, давайте щелкнем здесь, TableView здесь и перейдем к нашей прокрутке, и давайте снимем индикаторы прокрутки, они называются индикаторами прокрутки. Я не помню названия, потому что они такие уродливые, что я не хочу о них думать. Снимите оба, и это хорошо. Нам здесь хорошо. Так что мы будем называть это видео законченным, мы уже многое сделали. Мы создали пользовательскую ячейку TableView, мы анализируем в нее данные,мы реализовали функции делегирования. Здесь здорово. Мы внедрили функции делегирования. Поэтому помните, что вы реализуете протокол, а затем он вызывает функцию делегата. Итак, у протокола есть делегат или что-то, что представляет протокол, вы узнаете об этом подробнее. Затем делегат действует от его имени. В нашем случае делегатом был CategoriesVC. Поэтому мы реализовали cellForRowAt indexPath, который в основном DQs ячейки, а затем делает ее многоразовой, а затем вы обновляете данные в ней. Мы установили идентификатор на нем здесь и в раскадровке, и мы установили, сколько строк мы хотим показать в целом, мы установили делегат и источник данных здесь tableView, и мы подключились к выходам IB, и мы тоже сделали некоторую отладку, когда Xcode просто облил всю нашу замечательную работу, мы точно знали, как это исправить. Так что здесь много хорошего. Возможно, вам придется смотреть это снова и снова, но когда я профессионально программирую в iOS-разработке и в приложении DevOps, не проходит и дня, чтобы кто-то из нашей команды не работал с TableViews и не делал именно этот тип кода. Так что пока все, и давайте поднимемся на борт.
Протокол(Қосымша мәлімет)
Протокол - это список методов, которые определяют "контракт" или "интерфейс". Давайте добавим следующие строки в код для наглядности:
protocol Speaker {
func Speak()
}
Этот протокол объявляет единственный метод Speak.
Любой класс, который соответствует этому протоколу, должен имплементировать этот метод. Давайте попробуем это сделать, добавив два класса, которые соответствуют протоколу Speaker:
class Vicki: Speaker {
func Speak() {
println("Hello, I am Vicki!")
}
}
class Ray: Speaker {
func Speak() {
println("Yo, I am Ray!")
}
}
Для того, чтобы класс соответствовал протоколу, вы должны поставить двоеточие после его имени, далее, перечислить все протоколы (после имени класса - все то, от чего наследует класс, если таковое есть). Такие классы не наследуют от других классов, так что просто можете перечислить протоколы напрямую.
И кстати, если вы не включите функцию Speak, то вы получите ошибку компиляции.
Теперь, давайте попробуем то же самое, только добавим еще наследование от другого класса:
class Animal {
}
class Dog : Animal, Speaker {
func Speak() {
println("Woof!")
}
}
В этом примере, Dog наследует от Animal, так что когда вы объявляете Dog, вы должны поставить двоеточие, после чего идет имя класса от которого наследуют, а затем список протоколов. Вы можете наследовать только от одного класса в Swift, но вы можете соответствовать любому числу протоколов.
Вы можете обозначить метод в протоколе как опциональный. Чтобы попробовать, замените протокол Speaker следующим кодом:
@objc protocol Speaker {
func Speak()
optional func TellJoke()
}
Если у вас произошла ошибка, то добавьте следующую строку на самый верх вашей площадки:
import Foundation
Если вы хотите создать протокол с опциональными методами, то вам нужно создать его с префиксом @objc (даже, если ваш класс никак не связан с Objective-C). После этого вы можете ставить префикс optional у любого метода, который должен быть опциональным.
Обратите внимание, что сейчас нет ошибки компилятора, для вашего класса Person или класса Dog.
В этом примере, Ray и Vicki расскажут шутку. Так что имлементируем этот метод только для двух классов:
class Vicki: Speaker {
func Speak() {
println("Hello, I am Vicki!")
}
func TellJoke() {
println("Q: What did Sushi A say to Sushi B?")
}
}
class Ray: Speaker {
func Speak() {
println("Yo, I am Ray!")
}
func TellJoke() {
println("Q: Whats the object-oriented way to become wealthy?")
}
func WriteTutorial() {
println("I'm on it!")
}
}
Обратите внимание, что когда вы имплементируете протокол, то при необходимости, ваш класс может иметь больше одного метода, а не только тот, что указан в протоколе. Здесь класс Ray имеет один дополнительный метод.
Кстати, а вы не догадались случайно до ответов на эти шуточные вопросы?
Q: What did Sushi A say to Sushi B? A: Wasabi!
Q: Whats the object-oriented way to become wealthy? A: Inheritence!
На данный момент, вы уже создали протокол и несколько классов, которые их используют, давайте теперь попробуем использовать их. Добавьте следующий код на площадку:
var speaker:Speaker
speaker = Ray()
speaker.Speak()
// speaker.WriteTutorial() // error!
(speaker as Ray).WriteTutorial()
speaker = Vicki()
speaker.Speak()
Смотрите, вместо того, чтобы объявлять speaker как Ray, вы объявляете его как Speaker. Это значит, вы можете только вызывать методы для speaker, который существует в протоколе Speaker, так что вызов WriteTutorial вызовет ошибку, даже не смотря на то, что speaker - это тип Ray. Вы можете вызвать WriteTutorial, если вы временно отправите speaker обратно к Ray, как вы можете наблюдать тут.
Также обратите внимание, что вы можете присвоить speaker Vicki, так как Vicki, так же подчиняется Speaker.
Теперь, добавьте эти строки для эксперимента с опциональным методом:
speaker.TellJoke?()
speaker = Dog()
speaker.TellJoke?()
Помните, что метод TellJoke - опциональный метод, и перед тем как его вызвать, нужно проверить его существование.
Эти строки используют технику последовательности опционала. Если вы поставите вопросительный знак ? после имени метода, то прежде чем вызвать метод, будет проводиться проверка на его существование. Если он не существует, то он будет вести себя так, как метод возвращающий nil.
Последовательность опционала - полезная техника, когда вы хотите проверить существование значения перед тем, как его использовать. Является альтернативой опциональной связке (if let). Именно эту технику мы и будем использовать до конца данного туториала и во всех остальных.
Делегат - переменная, которая подчиняется протоколу, который в свою очередь используется классом, для оповещения событий или выполнения различных подзадач. Лучше всего представить, что босс дает миньону задачу сообщать ему о каких-либо событиях, или просто просит выполнить его что-либо.
Чтобы наглядно понять, что я имею в виду, добавьте новый класс DateSimulator на вашу площадку. Он позволяет вашим двум классам, которые соответствуют Speaker, «пойти на свидание»:
class DateSimulator {
let a:Speaker
let b:Speaker
init(a:Speaker, b:Speaker) {
self.a = a
self.b = b
}
func simulate() {
println("Off to dinner...")
a.Speak()
b.Speak()
println("Walking back home...")
a.TellJoke?()
b.TellJoke?()
}
}
let sim = DateSimulator(a:Vicki(), b:Ray())
sim.simulate()
Представьте, что вы хотите иметь возможность сообщать другому классу о том, что «свидание» началось или закончилось. Это было бы полезно, если был бы какой-либо индикатор, который мог появляться или исчезать, к примеру, в зависимости от статуса.
Чтобы это осуществить, создадим протокол с событиями, о которых вы хотите получать уведомления, к примеру(добавьте это до DateSimulator):
protocol DateSimulatorDelegate {
func dateSimulatorDidStart(sim:DateSimulator, a:Speaker, b:Speaker)
func dateSimulatorDidEnd(sim:DateSimulator, a: Speaker, b:Speaker)
}
Затем создайте класс, который подчиняется протоколу (добавьте его сразу после DateSimulatorDelegate):
class LoggingDateSimulator:DateSimulatorDelegate {
func dateSimulatorDidStart(sim:DateSimulator, a:Speaker, b:Speaker) {
println("Date started!")
}
func dateSimulatorDidEnd(sim:DateSimulator, a: Speaker, b: Speaker) {
println("Date ended!")
}
}
Для простоты, вас просто информируют об этих событиях.
Затем, добавим новое свойство для DateSimulator, которое принимает класс, который подчиняется этому протоколу:
var delegate:DateSimulatorDelegate?
Вот это и есть пример делегата. Еще раз, делегат - это некоторый класс, который реализует протокол, позволяющий оповещать о каких-либо событиях или выполнять какие-то конкретные указания.
Вы наверное обратили внимание на то, что мы сделали опционал. Таким образом, DateSimulator должен работать нормально и при созданном делегате, и при отсутствии делегата.
Прямо перед строкой sim.simulate() установите переменную вашему LoggingDateSimulator:
sim.delegate = LoggingDateSimulator()
Наконец, модифицируйте вашу функцию simulate() так, чтобы она могла вызывать делегата и в начале, и в конце метода.
Попробуйте сделать это самостоятельно, так как это жутко полезная практика! Не забывайте, что прежде чем использовать делегат, вы должны проверить, установлен ли он. Я советую вам использовать последовательность опционала.
Для проверки вашего решения смотрим код ниже:
class DateSimulator {
let a:Speaker
let b:Speaker
var delegate:DateSimulatorDelegate?
init(a:Speaker, b:Speaker) {
self.a = a
self.b = b