Случайны выбор дневника Раскрыть/свернуть полный список возможностей


Найдено 9579 сообщений
Cообщения с меткой

ios - Самое интересное в блогах

Следующие 30  »
rss_rss_hh_new

[Перевод] Программируйте там, где затык будет, а не там, где он был

Пятница, 30 Сентября 2016 г. 10:39 (ссылка)

В 2013 году от рождества Христова мысль, что телефоны с ARM-профессорами будут запускать полноценный JavaScript также быстро, как десктопы, оснащенные x86, вызывала смех. В те старые времена, три года назад, iPhone 5 отставал по мощности примерно в 10 раз. Казалось, что ничего не может измениться в ближайшее время.



Но всё изменилось. Новый айфон 7 запускает JavaScript, согласно измерениям JetStream benchmark, БЫСТРЕЕ, чем самый быстрый на сегодняшний день Макбук (не про и не эйр). Лучший 5K iMac с 4Ггц процессором i7 теперь всего в два раза быстрее 7го айфона в этом тесте. Процессоры ARM улучшаются с совершенно безумной скоростью. Мур расслабился с десктопами, но бежит как сумасшедший в мобильном мире.



img



Скорость прогресса пристыдила многие предсказания будущего, но этот конкретный пример все же поражает. Три года назад — это не так давно! С того времени мы пришли от "на порядок хуже" до "быстрее, чем большинство лаптопов".



Но что еще важнее метрик и бенчмарков это то, как последствия скачка производительности повлияют не только на возможности телефона, но и на общую стратегию.



Вот цитата из 2013:



Уточню: на телефоне возможно сделать совместную работу в реальном времени. Но это просто невозможно с JavaScript. Разница в производительности между нативными и веб-приложениями сравнима с разницей между FireFox and IE8: она слишком большая для серьезной работы.

Разницы больше нет. Так что, видимо, теперь айфон 7 официально подходит для Серьезной Работы ;-)



И вот что самое смешное. В 2013 мы сделали приложение под айфон для нашего инструмента совместной работы Basecamp. Мы использовали JavaScript и веб в комбинации. Мы любим веселиться на работе, но мне кажется, что результат был все же Достаточно Серьезным.



Мы использовали мобильный веб в самом сердце наших нативных приложений, и в то время это был рисковый шаг. Шрам от Фейсбука, который отказался от HTML5 в пользу чистого натива в 2012 году, был все еще слишком свеж в памяти тех, кто работал на пересечении веба и нативного кода. И, честно говоря, пришлось идти на компромиссы. Все было не так быстро, как в нативном варианте, но было достаточно быстрым.



И это было во времена "разницы на порядок"! Сегодня производительность этой стратегии не просто достаточно хорошая, она настолько высокая, что может быть офигенной. Или, иными словами, не является проблемой.



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



Я не говорю, что гибридный подход не приводит к компромиссам. Все еще есть некоторые моменты, которые ощущаются менее нативно, и им не хватает этой маленькой детали чтобы быть идеальными. И, конечно же, есть приложения, вроде крытых 3D-игр, где нужно выдавить все возможные капли производительности. Но в сегодняшних условиях количество приложений, которые можно создать таким гибридным веб/нативным подходом, и которые будут просто офигеть какие классные, несомненно очень большое. Это число намного, намного больше, чем в 2013 году.



Преимущества в продуктивности при разработке мультиплатформенных сервисов с помощью гибридного подхода — поразительны. Мы бы просто не смогли сделать Basecamp 3 за 18 месяцев и покрыть веб для десктопа, веб для мобильных устройств, нативный iOS, нативный Android и email без гибрида и величественного монолита. Как минимум, не раздувая команду разработки. Это пять платформ и 200+ отдельных экранов.



Это напоминает мне ситуацию, которую я описал в статье Ruby has been fast enough for 13 years. Увеличение производительности означает не только то, что наши штуки становятся быстрее. Это также означает, что мы можем делать новые штуки, новыми способами. Способами, которые были до невозможности медленными раньше. Способами, которые заставляют плакать людей, умещавших полные компьютерные демо в 4 килобайта. Но эти способы, тем не менее, увеличивают общую продуктивность масс.



Это также напоминает мне историю, описанную Джоном Кармаком, легендарным программистом, создателем Doom и Quake. Когда он работал над игрой, ему нужно было писать код не для текущей на тот момент производительности, но на три года вперед, потому что игра выходила через три года. Если бы он программировал для сегодняшнего дня, то игра при выходе была бы уже устаревшей. Поэтому Doom и Quake всегда выглядели круто.



Подумайте об этом когда делаете приложение сегодня. Вы программируете для условий мира 2013 года? Или 2016? Или 2018? Программируйте там, где затык производительности будет, а не там, где он был.


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/311398/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Управление зависимостями в iOS-приложения на Swift со спокойствием

Четверг, 29 Сентября 2016 г. 15:26 (ссылка)

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



Начав писать на Swift, мне пришлось столкнуться с многими проблемами, и одна из них — отсутствие конкуренции у IoC контейнеров на этом языке. По сути их всего два: Typhoon и Swinject. Swinject имеет мало возможностей, а Typhoon написан для Obj-С, что является проблемой, и работать с ним для меня оказалось большим стрессом.



И тут Остапа понесло я решил написать свой IoC контейнер для Swift, что из этого получилось читать под катом:



Итак, знакомьтесь — DITranquillity, IoC контейнер для iOS на Swift, интегрированный со Storyboard.



Интересная история про название — после сотни разных идей, остановился на «спокойствие». При придумывании названия я отталкивался от того, что основной причиной написание IoC контейнера был Typhoon. Вначале были мысли, назвать библиотеку стихийным бедствием посильнее тайфуна, но понял, что надо думать по другому: тайфун — это стресс, а моя библиотека должна обеспечить обратное, то есть спокойствие.



Планировалось все проверять статически (к сожалению, полностью все не удалось), а не падать в середине выполнения приложения по непонятным причинам что при использовании тайфуна в больших приложения не так уж и редко происходит, и xcode иногда не собирает проект из-за тайфуна, а падает при сборке.



Любители Typhoon, возможно, слегка расстроятся, но, мой взгляд, на именование некоторых сущностей отличается от взгляда тайфуна. Он такой же, как у Autofac, но с учетом особенностей языка.



Особенности



Начну с описания особенностей библиотеки:




  • Библиотека работает с чистыми Swift классами. Не надо наследоваться от NSObject и объявлять протоколы как Obj-C, c помощью данной библиотеки можно писать на чистом Swift;

  • Нативность — описание зависимостей происходит на родном языке, что позволяет делать легкий рефакторинг и...

  • Большая часть проверок на этапе компиляции — после Typhoon это может показаться раем, так как многие опечатки обнаруживаются на этапе компиляции, а не во время исполнения. К сожалению, похвастаться тем, что во время исполнения не могут возникнуть ошибки библиотека не может, но часть проблем, будьте уверены, отсекутся;

  • Поддержка всех паттернов Dependency Injection: Initializer Injection, Property Injection и Method Injection. Не знаю, почему это круто, но все пишут об этом;

  • Поддержка циклических зависимостей — библиотека поддерживает много различных вариантов циклических зависимостей, при этом без вмешательства программиста;

  • Интеграция со Storyboard — позволяет внедрять зависимости прямо во ViewController-ы.



А также:




  • Поддержка времени жизни объектов;

  • Указание альтернативных типов;

  • Разрешение зависимостей по типу и имени;

  • Множественная регистрация;

  • Разрешение зависимостей с параметрами инициализации;

  • Короткая запись для разрешения зависимости;

  • Специальные механизмы для «модульности»;

  • Поддержка CocoaPods;

  • Документация на русском (на самом деле, правильнее сказать черновая документация, там ошибок много).



И все это в 1500 строк кода, при этом порядка 400 строк из них, это автоматически генерируемый код, для типизированного разрешения зависимостей с разным количеством параметров инициализации.



И что со всем этим делать?



Кратко



Начну с небольшого примера синтаксиса: да простит меня Autofac, за то, что написал их пример, адаптированный под свою библиотеку.



// Classes

class TaskRepository: TaskRepositoryProtocol {
...
}

class LogManager: LoggerProtocol {
...
}

class TaskController {
var logger: LoggerProtocol? = nil
private let repository: TaskRepositoryProtocol

init(repository:TaskRepository) {
self.repository = repository
}
...
}

// Register

let builder = DIContainerBuilder()
builder.register(TaskRepository.self)
.asType(TaskRepositoryProtocol.self)
.initializer { TaskRepository() }

builder.register(LogManager.self)
.asType(LoggerProtocol.self)
.initializer { LogManager(Date()) }

builder.register(TaskController.self)
.initializer { (scope) in TaskController(repository: *!scope) }
.dependency { (scope, taskController) in taskController.logger = try? scope.resolve() }

let container = try! builder.build()

// Resolve

let taskController: TaskController = container.resolve()


А теперь по-порядку



Базовая интеграция в проект



В отличии от Typhoon, библиотека не поддерживает «автоматическую» инициализацию из plist, или подобные «фичи». В принципе, несмотря на то, что тайфун поддерживает такие возможности, я не уверен в их целесообразности.



Чтобы интегрироваться с проектом, который планируется более-менее крупным, нам надо:




  1. Интегрировать саму библиотеку в проект. Это можно сделать с помощью Cocoapods:



    pod 'DITranquillity'



  2. Объявить базовую сборку с помощью библиотеки (опционально):



    import DITranquillity

    class AppAssembly: DIAssembly { // Объявляем нашу сборку
    var publicModules: [DIModule] = [ ]

    var intermalModules: [DIModule] = [ AppModule() ]

    var dependencies: [DIAssembly] = [
    // YourAssembly2(), YourAssembly3() - зависимости на другие сборки
    ]
    }



  3. Объявить базовый модуль (опционально):



    import DITranquillity

    class AppModule: DIModule { // Объявляем наш модуль
    func load(builder: DIContainerBuilder) { // Согласно протоколу реализуем метод
    // Регистрируем типы
    }
    }



  4. Зарегистрировать типы в модуле (см. первый пример выше).




  5. Зарегистрировать базовую сборку в билдере и собрать контейнер:



    import DITranquillity

    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    public func applicationDidFinishLaunching(_ application: UIApplication) {
    ...
    let builder = DIContainerBuilder()
    builder.register(assembly: AppAssembly())
    try! builder.build() // Собираем контейнер
    // Если во время сборки произошла ошибка, то программа упадет, с описанием всех ошибок, которые нужно поправить
    }
    }



Storyboard



Следующим этапом, после написания пары классов, создается Storyboard, если его до этого еще не было. Интегрируем его в наши зависимости. Для этого нам нужно будет немного отредактировать базовый модуль:



class AppModule: DIModule {
func load(builder: DIContainerBuilder) {
builder.register(UIStoryboard.self)
.asName("Main") // Даем типу имя по которому мы сможем в будущем его получить
.instanceSingle() // Говорим что он должен быть единственный в системе
.initializer { scope in DIStoryboard(name: "Main", bundle: nil, container: scope) }

// Регистрируем остальные типы
}
}


И изменим AppDelegate:



public func applicationDidFinishLaunching(_ application: UIApplication) {
....
let container = try! builder.build() // Собираем наш контейнер

window = UIWindow(frame: UIScreen.main.bounds)

let storyboard: UIStoryboard = try! container.resolve(Name: "Main") // Получаем наш Main storyboard
window!.rootViewController = storyboard.instantiateInitialViewController()
window!.makeKeyAndVisible()

}


ViewController'ы на Storyboard



И так мы запустили наш код порадовались, что ничего не упало и убедились, что у нас создался наш ViewController. Самое время создать какой-нибудь класс, и внедрить его во ViewController.



Создадим Presenter:



class YourPresenter {
...
}


Также нам понадобится дать имя (тип) нашему ViewController, и добавить инъекцию через свойства или метод, но в нашем коде мы воспользуемся инъекцией через свойства:



class YourViewController: UIViewController {
var presenter: YourPresenter!
...
}


Также не забудьте в Storyboard указать, что ViewController является не просто UIViewController, а YourViewController.



И теперь надо зарегистрировать наши типы в нашем модуле:



 func load(builder: DIContainerBuilder) {
...

builder.register(YourPresenter.self)
.instancePerScope() // Говорим, что на один scope нужно создавать один Presenter
.initializer { YourPresenter() }

builder.register(YourViewController.self)
.instancePerRequest() // Специальное время жизни для ViewController'ов
.dependency { (scope, self) in self.presenter = try! scope.resolve() } // Объявляем зависимость
}


Запускаем программу, и видим что у нашего ViewController’а есть Presenter.



Но, погодите, что за странное время жизни instancePerRequest, и куда делся initializer? В отличии от всех остальных типов ViewController'ы, которые размещаются на Storyboard создаем не мы, а Storyboard, поэтому у нас нет initializer и они не поддерживают инъекцию через метод инициализации. Так как наличие initializer является одним из пунктов проверки при попытке создать контейнер, то нам надо объявить, что данный тип создается не нами, а кем-то другим — для этого существуем модификатор `instancePerRequest`.



Добавляем работу с данными



Дальше проект что-то должен делать и за частую на мобильных устройства приложения получают информацию из сети, обрабатывать её и отображают. Для простоты примера опустим шаг обработки данных и не будем вдаваться в детали получения данных из сети. Просто предположим, что у нас есть протокол Server, с методом `get` и соответственно есть реализация этого протокола. То есть у нас в программе появляется вот такой код:



protocol Server {
func get(method: String) -> Data?
}

class ServerImpl: Server {
init(domain: String) {
...
}
func get(method: String) -> Data? {
...
}
}


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



import DITranquillity

class ServerModule: DIModule {
func load(builder: DIContainerBuilder) {
builder.register(ServerImpl.self)
.asSelf()
.asType(Server.self)
.instanceSingle()
.initializer { ServerImpl(domain: "https://your_site.com/") }
}
}


Мы зарегистрировали тип ServerImpl, при этом в программе он будет известен под 2 типами: ServerImpl и Server. Это некоторая особенность поведения при регистрации — если указан альтернативный тип, то основной тип не используется, если не указать этого явно. Также мы указали, что сервер в нашей программе один.



Также слегка модифицируем нашу сборку, чтобы она знала о новом модуле:



class AppAssembly: DIAssembly {
var publicModules: [DIModule] = [ ServerModule() ]
}


Различие между publicModules и internalModules
Существует два уровня видимости модулей: Internal и Public. Public — означает, что данный модуль будет виден, и в других сборках, которые используют эту сборку, Internal — модуль будет виден только внутри нашей сборки. Правда, надо уточнить, что так как сборка является всего лишь объявлением, то данное правило о видимости модулей распространяется на контейнер, по принципу: все модули из сборок которые были напрямую добавлены в builder, будут включены в собранный им контейнер, а модуля из зависимых сборок включаться в контейнер, только если он объявлены публичными.


Теперь поправим немного Presenter — добавим ему информацию о том, что ему нужен сервер:



class YourPresenter {
private let server: Server

init(server: Server) {
self.server = server
}
}


Мы внедрили зависимость через метод инициализации, но могли сделать это, как и во ViewController’е — через свойства, или метод.



И дописываем регистрацию нашего Presenter — говорим, что мы будем внедрять Server в Presenter:



   builder.register(YourPresenter.self)
.instancePerScope() // Говорим, что на один scope нужно создавать один Presenter
.initializer { (scope) in YourPresenter(server: *!scope) }


Тут мы для получения зависимости использовали «быстрый» синтаксис `*!` который является эквивалентом записи: `try! scope.resolve()`



Запускаем нашу программу и видим, что у нашего Presenter'а есть Server. Теперь его можно использовать.



Внедряем логер



Наша программа работает, но у каких-то пользователей она неожиданно стала работать не корректно. Мы не можем воспроизвести проблему у себя и решаем — все пора, нам нужен логер. Но так как у нас уже проснулась вера в паранормальное, то логер должен писать данные в файл, в консоль, на сервер и еще в море мест, и все это должно легко включаться/отключаться и использоваться.



И так, мы создаем базовый протокол `Logger`, с функцией `log(message: String)` и реализуем несколько реализаций: ConsoleLogger, FileLogger, ServerLogger… Создаем базовый логер который дергает все остальные, и называем его — MainLogger. Дальше мы в те классы, в которых собираемся логировать добавляем строчку на подобии: `var log: Logger? = nil`, и… И теперь нам надо зарегистрировать все те действия, которые мы произвели.



Вначале создаем новый модуль `LoggerModule`:



import DITranquillity

class LoggerModule: DIModule {
func load(builder: DIContainerBuilder) {
builder.register(ConsoleLogger.self)
.asType(Logger.self)
.instanceSingle()
.initializer { ConsoleLogger() }

builder.register(FileLogger.self)
.asType(Logger.self)
.instanceSingle()
.initializer { FileLogger(file: "file.log") }

builder.register(ServerLogger.self)
.asType(Logger.self)
.instanceSingle()
.initializer { ServerLogger(server: "http://server.com/") }

builder.register(MainLogger.self)
.asType(Logger.self)
.asDefault()
.instanceSingle()
.initializer { scope in MainLogger(loggers: **!scope) }
}
}


И не забываем, добавить внедрение нашего логера, во все классы где мы его объявили, к примеру, вот так:



builder.register(YourPresenter.self)
.instancePerScope() // Говорим, что на один scope нужно создавать один Presenter
.initializer { scope in try YourPresenter(server: *!scope) }
.dependency { (scope, obj) in obj.log = *?scope }


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



Вначале мы зарегистрировали 3 наших логера, которые будут доступны по имени Logger — то есть мы осуществили множественную регистрацию. При этом если мы уберем MainLogger, то в программе не будет единственного логера, так как если мы захотим получить один логер, то библиотека не сможет понять какой логер хочет от неё программист. Далее для MainLogger мы делаем две вещи:




  1. Говорим, что это стандартный логер. То есть если нам нужен единственный логер то это будет MainLogger, а не какой-то другой.




  2. В MainLogger передаем список всех наших логеров, за исключением самого себя (это одна из возможностей библиотеки, при множественном разрешении зависимостей исключаются рекурсивные вызовы. Но если мы сделаем тоже самое в блоке dependency, то у нас выдадутся все логеры в том числе MainLogger).Для этого используется быстрый синтаксис `**!`, который является эквивалентом `try! scope.resolveMany()`



Итоги



С помощью библиотеки мы смогли выстроить зависимости между несколькими слоями: Router, ViewController, Presenter, Data. Были показаны такие вещи как: внедрение зависимостей через свойства, внедрение зависимостей через инициализатор, альтернативные типы, модули, немного коснулись времени жизни и сборок.



Многие возможности были упущены: циклические зависимости, получение зависимостей по имени, время жизни, сборки. Их можно посмотреть в документации



Этот пример доступен по этой ссылке.



Планы




  • Добавление подробного логирования, с возможность указывать внешние функции, в которые приходят логи

  • Поддержка других систем (MacOS, WatchOS)



Альтернативы




  • Typhoon — не поддерживает чистые swift типы, и его синтаксис, на мой взгляд является громоздким

  • Swinject — отсутствие альтернативных типов, и множественной регистрации. Менее развитые механизмы для «модульности», но это хорошая альтернатива



P.S.
На данный момент проект находится, на пререлизном состоянии, и мне бы хотелось, прежде чем давать ему версию 1.0.0 узнать мнения других людей, так как после “официального” выхода, менять что-то кардинально станет сложнее.


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/311334/

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
Mykinoplus

Полкардана кинопортал

Вторник, 27 Сентября 2016 г. 21:23 (ссылка)


Смотрите на нашем сайте фильмысериалымультфильмыонлайн ТВ, в хорошем качестве, бесплатно и без регистрации. Все фильмы доступны на мобильных телефонах, смартфонах, планшетах, Android, iOS, при качестве HD 720p. Удобный стол заказов, позволяет пользователям сделать быструю заявку на фильм, или сериал, который на данный момент отсутствует в фильмотеке кинопортала, а общение в "живом" чате, даст возможность обсудить последние новости мира кино, или просто пообщаться с другими пользователями. Отзывчивая и дружелюбная команда нашего сайта, всегда готова идти на встречу пожеланиям пользователей, поэтому мы всегда открыты для новых идей и сотрудничества. Если по какой-то из причин, пребывание на кинопортале станет Вам в тягость, или не оправдает ожидаемых результатов, Администрация ресурса приложит максимум усилий, дабы разрешить конфликтную ситуацию и сделать Ваше пребывание максимально комфортным и результативным!

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

[Из песочницы] Зачем нужны UITableViewController и UICollectionViewController

Понедельник, 26 Сентября 2016 г. 18:07 (ссылка)

Всем привет, меня зовут Артём, я iOS-разработчик. Сегодня хочу рассказать о подходах к использованию UITableViewController и UICollectionViewController.



Едва ли можно найти мобильное приложение, в котором не используется списочное представление данных. Существенную часть времени мы (iOS-разработчики) проводим с TableView или CollectionView. Именно поэтому критически важным является выбор подходов к использованию этих базовых элементов из соображений скорости разработки и стоимости дальнейшей поддержки создаваемых решений. Хочу поделиться выводами, к которым пришли с коллегами в Touch Instinct.



Статья рассчитана на разработчиков, которые работают с TableView (CollectionView), но почему-то не работают с TableViewController (CollectionViewController). Далее будет упоминаться только TableView(Controller), но все написанное касается и CollectionView(Controller) тоже.



Вариант 1. MassiveViewController



Самый простой и привычный для многих вариант — когда разработчик создает в storyboard’е ViewController, располагает на нём TableView и указывает ViewController в качестве объекта, который будет реализовывать протоколы UITableViewDelegate и UITableViewDataSource.





Все хорошо, но не всегда. Порой, возникают проблемы. И возникают они, когда в данном контроллере появляется дополнительная логика, мало связанная с TableView. Ладно еще, если в контроллере появляются обработчики UIBarButtonItem’ов. Но, как правило, появляется в этом контроллере всё подряд: обработчики кнопок под (над/слева/справа) TabieView, различные всплывающие элементы, работа с сетью, конфигурирование ячеек и так далее. Налицо грубое нарушение принципов Single Responsibility. ViewController грустит… Еще больше грустят другие разработчики, когда видят такую картину.



Вариант 2. Sолидный



Рядового разработчика после первой встречи со статьями про SOLID начинают преследовать сомнительные идеи различной степени тяжести. Одна из таких идей заключается в повсеместном применении принципа Single Responsibility. В том числе, в ущерб здравому смыслу.



Под впечатлением от прочитанного разработчик создает отдельные объекты для реализации каждого протокола, как показано на схеме ниже.





Но проблема такого подхода заключается в том, что UIKit зачастую затрудняет поддержку данного принципа. На деле получается, что тесно связанные между собой вещи оказываются по разную сторону баррикад. Элементарный пример: при расчете высоты ячейки (Delegate) необходимо знать её содержимое (DataSource). В конечном итоге всё может стать еще более запутанным, чем в предыдущем варианте.



Вариант 3. ПолуSолидный



Разработчик понял, что использование Sолидного варианта связано с определенными трудностями, и решил слегка упростить его. Для этого создается не два класса, а один, который реализует протоколы Delegate и DataSource. Данный вариант реализовать легче, но и у него есть свои недостатки, которые заключается в том, что все равно необходимо создать дополнительный класс и обеспечить двустороннюю связь между ViewController’ом и объектом, реализующим протоколы.







На самом деле все просто



Когда-то давным-давно обсуждали эту проблему с коллегами. И тут один опытный разработчик сказал: «А что тут обсуждать-то? Все и так есть из коробки».



И ведь действительно есть. Почему-то многие разработчики не используют TableViewController, аргументируя это тем, что есть отдельный TableView, который можно положить на view, как захочется. На это хочется ответить двумя аргументами:




  1. Практически всегда TableView будет растягивается на всю view – не проще ли сразу использовать TableViewController?

  2. Если TableView будет расположен не на всю view, значит будут присутствовать другие элементы, а связанный с ними код появится в ViewController’е.



Нет ничего хорошего в том, что код TableView смешивается с кодом других элементов, за которые отвечает ViewController. А представьте насколько усугубляется картина, если речь идет об iPad приложении и на экране больше чем две таблицы, которые обрабатываются одним ViewController’ом.



Подход, который я предлагаю рассмотреть – компромисс между Sолидностью и простотой. Заключается он в отказе от отдельно лежащих TableView. Последние версии iOS и Xcode позволяют применять данный подход без боли и мучений, с удобством и удовольствием.





Многим придется не по вкусу идея плодить ViewController’ы, но на деле они будут появляться только там, где это действительно нужно. К примеру, стоит задача сделать таблицу на весь экран. Лучше сразу использовать для этой цели TableViewController. Если нужно добавить такую же таблицу куда-нибудь еще, то вы спокойно сможете переиспользовать данный TableViewController, т.к. в нём будет только относящаяся к таблице логика и ничего лишнего.



Если вдруг появилась необходимость изменить расположение TableView, то просто создается отдельный ViewController, в который интегрируется TableViewController (через ViewController Containment). Данное решение является настолько коробочным, что все можно сделать через storyboard:





А еще интегрированные ViewController’ы будут изменять свой размер в соответствии с контейнером, в который они интегрированы, что не может не радовать глаз и в очередной раз подтверждает «коробочность» данного решения.



Тоже самое можно сделать и в коде:



let embedController = UIViewController()
addChildViewController(embedController)
view.addSubview(embedController.view)
embedController.view.frame = view.bounds // Здесь можно использовать Auto Layout, но это совсем другая история
embedController.didMoveToParentViewController(self)


Специальная рубрика: вопрос на засыпку!



Почему не нужно вызывать embedController.willMoveToParentViewController(self)?



Правильный ответ
Данный метод вызывается внутри
addChildViewController(embedController)


Следует обратить внимание, что в случае удаления встроенного ViewController’а все происходит наоборот:



embedController.willMoveToParentViewController(nil)
embedController.view.removeFromSuperview()
embedController.removeFromParentViewController()


А метод embedController.didMoveToParentViewController(self) будет вызван автоматически.



Идем далее.



Честно признаться, я и сам не хочу плодить лишние ViewController’ы, поэтому есть несколько элементов, которые может и не относятся к таблице напрямую, но все же не заслуживают создания отдельного ViewController’а:




  • BarButtonItems. Их можно легко добавить в TableViewController и обработать там же. Для этого необходимо включить отображение NavigationBar’а в Simulated Metrics и добавить Navigation Item.






  • Шапка таблицы. Не все знают, что в TableView можно вставить header для всей таблицы, а не только для секций.





    Добавленная таким образом шапка будет скроллиться вместе с контентом таблицы.




Как теперь с этим жить работать?



Если из ParentViewController нужно что-то передать в ChildViewController, необходимо переопределить метод prepareForSegue.



private var someController: SomeViewController!

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let someController = segue.destinationViewController as? SomeViewController {
self.someController = someController
}
}


Ну и совсем уж очевидный совет напоследок



Не нужно забивать ViewController всякой дрянью. Логика, которая не относится к элементам на экране, должна быть вынесена. Хорошей практикой, о которой, к счастью, знают почти все, является вынесение кода конфигурирования ячеек в классы этих ячеек.



func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if let cell = cell as? SomeCell {
cell.textLabel = someObject.someText // плохо
cell.numberLabel = someObject.someNumber // плохо
cell.configureForObject(someObject) // хорошо
} else if let cell = cell as? OtherCell {
cell.textLabel = otherObject.text // плохо
cell.numberLabel = otherObject.number // плохо
cell.configureForObject(otherObject) // хорошо
}
}


Резюме




  • Не используем отдельный TableView

  • Используем TableViewController

  • Нужно добавить что-то в TableViewController — создаем ParentViewController

  • Не конфигурируем ячейки прямо во ViewController’е, а передаём модель в ячейку и производим конфигурацию там

  • Применяем все тоже самое и к CollectionView


Original source: habrahabr.ru.

https://habrahabr.ru/post/311064/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
masiania1

Apple TV получит App Store и поддержку Bluetooth-контроллеров

Понедельник, 26 Сентября 2016 г. 20:16 (ссылка)
lapplebi.com/news/2804-appl...lerov.html

Apple TV станет не просто медиакомбайном, а настоящей игровой приставкой, которая будет поддерживать беспроводную технологию Bluetoth (с помощью него и будут подключаться контроллеры).
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
masiania1

Mortal Kombat — игра, на которой выросло поколение SEGA

Понедельник, 26 Сентября 2016 г. 17:52 (ссылка)
lapplebi.com/games/2803-mor...-sega.html

Мортал Комбат – это даже не игра и не франшиза, это целая вселенная со своими мирами, персонажами и законами, по которой сняты полнометражные фильмы.
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
rss_rss_hh_new

Дайджест интересных материалов для мобильного разработчика #172 (19-25 сентября)

Воскресенье, 25 Сентября 2016 г. 16:09 (ссылка)

App Store пустеет, приложения интегрируются с Siri, HoloLens получает свой Hello World, жестокая реальность губит мобильные FPS шутеры, РЖД заказывает приложение за 67 млн рублей – это лишь малая часть нового большого дайджеста.












Сентябрь. Пустеет яблоневый сад

Это не метафора. Число приложений в магазине Apple резко уменьшилось. А началась эта история 1 сентября.






Masking Bitmaps на Android

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



Теперь дайджест доступен и в виде рассылки. Подписаться вы можете тут.



iOS



Android



Windows



Разработка



Аналитика, маркетинг и монетизация



Устройства и IoT





<- Предыдущий дайджест. Если у вас есть другие интересные материалы или вы нашли ошибку — пришлите, пожалуйста, в почту.
Original source: habrahabr.ru.

https://habrahabr.ru/post/310966/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best

Комментарии (0)КомментироватьВ цитатник или сообщество
masiania1

Skype для iOS и Android

Понедельник, 26 Сентября 2016 г. 10:37 (ссылка)
lapplebi.com/prilozheniya/2...droid.html

Известная и очень популярная во всем мире программа Skype используется очень многими для диалога через Инет.
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество
masiania1

NFS Shift — о них не слышал только глухой

Суббота, 24 Сентября 2016 г. 12:59 (ссылка)
lapplebi.com/games/2796-nfs...luhoy.html

В каждом из жанров компьютерных игр есть игра, являющаяся своего рода В каждом из жанров компьютерных игр есть игра, являющаяся своего рода флагманом игр такого типа. В гоночном жанре,
Метки:   Комментарии (0)КомментироватьВ цитатник или сообщество

Следующие 30  »

<ios - Самое интересное в блогах

Страницы: [1] 2 3 ..
.. 10

LiveInternet.Ru Ссылки: на главную|почта|знакомства|одноклассники|фото|открытки|тесты|чат
О проекте: помощь|контакты|разместить рекламу|версия для pda