Горячее
Лучшее
Свежее
Подписки
Сообщества
Блоги
Эксперты
Войти
Забыли пароль?
или продолжите с
Создать аккаунт
Я хочу получать рассылки с лучшими постами за неделю
или
Восстановление пароля
Восстановление пароля
Получить код в Telegram
Войти с Яндекс ID Войти через VK ID
Создавая аккаунт, я соглашаюсь с правилами Пикабу и даю согласие на обработку персональных данных.
ПромокодыРаботаКурсыРекламаИгрыПополнение Steam
Пикабу Игры +1000 бесплатных онлайн игр
Классический пинбол, как в древнем игровом автомате или в компактной игрушке: есть пружины, шарики и препятствия. В нашем варианте можно не только зарабатывать очки: чтобы пройти уровень, придется выполнить дополнительную миссию.

Пинбол Пикабу

Аркады, На ловкость, Казуальные

Играть

Топ прошлой недели

  • AlexKud AlexKud 38 постов
  • SergeyKorsun SergeyKorsun 12 постов
  • SupportHuaport SupportHuaport 5 постов
Посмотреть весь топ

Лучшие посты недели

Рассылка Пикабу: отправляем самые рейтинговые материалы за 7 дней 🔥

Нажимая кнопку «Подписаться на рассылку», я соглашаюсь с Правилами Пикабу и даю согласие на обработку персональных данных.

Спасибо, что подписались!
Пожалуйста, проверьте почту 😊

Помощь Кодекс Пикабу Команда Пикабу Моб. приложение
Правила соцсети О рекомендациях О компании
Промокоды Биг Гик Промокоды Lamoda Промокоды МВидео Промокоды Яндекс Директ Промокоды Отелло Промокоды Aroma Butik Промокоды Яндекс Путешествия Постила Футбол сегодня

IT + Swift

С этим тегом используют

Программирование IT юмор Программист Юмор Работа Картинка с текстом Разработка Политика Санкции Банк Экономика Россия Тинькофф банк Все
25 постов сначала свежее
1
tproger.official
tproger.official
7 месяцев назад
Типичный программист

Легкое недопонимание(⁠⁠

Легкое недопонимание( IT юмор, Программирование, IT, Программист, Swift, Мемы, Тейлор Свифт
Показать полностью 1
IT юмор Программирование IT Программист Swift Мемы Тейлор Свифт
17
3
andrey.zarembo
andrey.zarembo
1 год назад
Серия Дневник разработчика

Навигация в SwiftUI / 4 min⁠⁠

Навигация в SwiftUI / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

В этой статье вы прочитаете:

  • Как открывается попап / модальное окно

  • Как открыть другой экран на примере галереи

  • Как вернуться назад

  • Как открыть экран, связанный с объектом - на примере просмотра фото

Примеры изображений и кода из этой статьи доступны на GitHub

Для примеров я использую NavigationStack, доступный в iOS 16+.

Как открывается попап / модальное окно

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

Для отображения будет использоваться единый флаг AccessLevel. Этот флаг меняется в разных частях приложения через Binding. В случае его обновления сработает обновление состояния основного View приложения и будет отображен блокирующий экранчик проверки возраста.

Навигация в SwiftUI / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

Как открыть другой экран на примере галереи

Галерея - это экран, который не зависит от конкретного объекта, это просто экран в стеке. Поэтому для его отображения будет использоваться NavigationPath. Если грубо, то это последовательность элементов, которые определяют состояние NavigationStack. Один элемент - один экран.

Навигация в SwiftUI / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Я добавлю enum который будет определять необходимый экран. Для примера в нем будет один элемент - gallery, но такой подход позволяет добавить любое количество необходимых экранов, а associated value в enum позволят добавить данные или настройки для экрана. Данные о необходимом для открытии экране придут в модификатор navigationDestination с указанным enum.

Навигация в SwiftUI / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

Теперь для отображения экрана галереи необходимо в path через метод append добавить значение gallery.

Навигация в SwiftUI / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

Как вернуться назад

Чтобы вернуться назад необходимо вызывать метод dismiss у глобального объекта presentationMode, доступного по аналогичному ключу. Я повесил обработчик на левую кнопку верхней панели на экранах галереи и просмотра фото.

Навигация в SwiftUI / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

Как открыть экран, связанный с объектом - на примере просмотра фото

Экран просмотра фото - это экран, связанный с объектом - фотографией. Этому экрану обязательно надо передать изображение, которое он будет отображать. Самое подходящее место для передачи информации о фотографии - это сетка галереи GalleryGrid, которая обрабатывала выбор фото. Но протаскивать туда ссылку на path будет очень неудобно.

Но этого можно не делать, если использовать NavigationLink. Этот объект существует в двух основных вариантах:

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

  2. В параметрах Оформление и объект для навигации - это то, что я буду использовать для перехода из галереи в экран фотографии

Я добавил в GalleryGrid, созданной в статье про галерею, режим выбора и когда он выключен, галерея работает как переход на просмотр фото. NavigationLink в оverlay с Сolor.clear в качестве оформления будет работать как невидимая кнопка для перехода. NavigationLink отправит объект image в ближайший по иерархии NavigationStack.

Навигация в SwiftUI / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

В основном коде добавляется обработчик этих объектов через модификатор navigationDestination, который будет обрабатывать объекты MediaItem

Код примера

Я добавил .navigationBarHidden(true), потому что в приложении используются собственные панели и не требуется системная панель с кнопкой назад

Вот как это работает

Заключение

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

Все работающие примеры есть в проекте на Github, их можно скачать и запустить.

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

Показать полностью 6 1
[моё] IT Дневник Научпоп Swift Программирование Длиннопост Видео Без звука
0
4
andrey.zarembo
andrey.zarembo
1 год назад
Серия Дневник разработчика

Swift Combine - Часть 2 / 4 min⁠⁠

Это вторая статья о работе со Swift Combine, в ней я расскажу про математические операции, поиск совпадений, операции с элементами по индексам, выбор и объединение потоков. Ссылка на первую статью

Swift Combine - Часть 2 / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

В этой статье вы прочитаете:

  • Операции

  • Math
    Matching
    Sequence
    Select
    Collecting & Republishing

Примеры кода из этой статьи доступны на GitHub

Операции и преобразования

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

У некоторых операций есть try... вариант, который позволяет вернуть ошибку, но я их дополнительно не описывал.

Math

  • .count - подсчитывает количество объекто в возвращает число

  • .max - возращает самый большой объект. Он должен соответствовать Comparable. В примере просто сравниваются числа

  • .max(by ) - возвращает самый большой объект, но сравнение выполняется в блоке. В примере числа красных объектов умножаются на 10

  • .min - возращает самый маленький объект. Он должен соответствовать Comparable. В примере просто сравниваются числа

  • .min(by ) - возвращает самый маленький объект, но сравнение выполняется в блоке. В примере числа красных объектов умножаются на 10

Matching

  • .contains - возвращает bool, есть ли объект в потоке. Он должен соответствовать Equitable. В примерах 1 и 2 ищется синий треугольник

  • .contains(where: ) - возвращает bool, есть ли объект в потоке, но сравнение выполняется в блоке. В примере 3 ищется любой треугольник

  • .allSatisfy - возвращает bool, если все объекты соответствует условию в блоке. В примере ищутся звездочки

Sequence

Swift Combine - Часть 2 / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост
  • .drop(while:) - пропускает все элементы, пока они соответствую условию в блоке. Как только пришел другой объект, ничего больше отбрасываться не будет. В примере 1 и 2 условие - треугольник. Пока они в начале - отбрасываются, а после звезды уже остаются. Позволяет отфильтровать стартовые данные, заголовки и др

  • .drop(untilOutputFrom:) - пропускает все элементы потока, пока другой поток не выдаст сигнал. Например игнорировать таймер, пока не пришел флаг начала анимации

  • .dropFirst - отбрасывает первый элемент в потоке

  • .dropFirst(n) - отбрасывает первые N элементов в потоке

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

  • .append( pub ) - добавляет к завершившемуся потоку данные из другого потока. В примере красная звездочка игнорируется, т.к. первый поток на тот момент не завершился, а остальные элементы добавляются в конец.

  • .prepend(...) - добавляет в начало потока набор данных. В примере добавляются треугольник, звезда и звездочка

  • .prepend( pub ) - добавляет в начало данные из завершившегося потока

  • .prefix(n) - возвращает первые n элементов потока

  • .prefix(untilOutputFrom:) - возвращает элементы потока, пока второй поток не выдаст сигнал

  • .prefix(while: n) - возвращает элементы потока, пока они соответствуют условиям. После нарушения условия выдача останавливается

Swift Combine - Часть 2 / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Select

Swift Combine - Часть 2 / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост
  • .first - возвращает первый элемент потока

  • .first(where: ) - возвращает первый элемент потока, соответствующий условию в блоке

  • .last - возвращает последний элемент завершившегося потока

  • .last(where: ) - возвращает последний элемент завершившегося потока, соответствующий условию в блоке

  • .output(at:) - возвращает элемент с определенным индексом из потока

  • .output(in:) - возвращает элементы с индексами из диапазона

Collecting & Republishing

Swift Combine - Часть 2 / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост
  • .combineLatest( pub ) { a,b } - комбинирует элемент последнего сигнала потока с последним элементом другого потока. В примере сначала приходят красная звезда в потоке 1 и оранжевая единица в потоке 2, получается их пара. Затем в потоке 1 приходит синяя звезда и она комбинируется с оранжевой единицей в потоке 2. Фиолетовая двойка в потоке 2 приходит в тот же временной слот, но все-таки после синей звезды. И там по очереди Поток 1, Поток 2 приходят и сочетаются с последним сигналом другого потока

  • .combineLatest( pub ) { tuple } - у метода есть другой вариант, который возвращает не два параметра, а один tuple

  • .combineLatest ABC и ABCD - выполняет такие же операции, но сочетая 3 и 4 потока

  • .merge - склеивает несколько поток в один по мере поступления данных

  • .zip - собирает потоки в пары / тройки / четверки в порядке их следования в потоках. Сначала все первые элементы, потом все вторые и т.д. Поэтому на одинаковых данных zip дает 3 пары элементов, а combineLatest - 5

Заключение

В этой статье кратко рассмотрены операции групп Math, Matching, Sequence, Select, Collecting и Republishing с визуализацией. Все визуализации получены в приложении-песочнице.

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

Все работающие примеры preview есть в проекте на Github, их можно скачать и запустить.

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

Показать полностью 4
[моё] IT Дневник Научпоп Swift Программирование Длиннопост
1
1
andrey.zarembo
andrey.zarembo
1 год назад
Серия Дневник разработчика

Отображение фото / 5 min⁠⁠

Из этой статьи вы узнаете как создать компонент отображения фотографий для просмотра, редактирования и обрезки.

Отображение фото / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

В этой статье вы прочитаете:

  • Как отображать фотографии в разных режимах

  • Какие есть сложности в работе с просмотром фото в SwiftUI

  • Как реализовать просмотр фото на UIKit и через Prepresentable интегрировать его в SwiftUI

Примеры изображений и кода из этой статьи доступны на GitHub

Отображение фотографий в разных режимах

В приложении фотография отображается в трех режимах:

Просмотр фото - для отображения готового изображения. Оно уже обрезано, наклеены наклейки и применен фильтр.

Отображение фото / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Редактор фото - отображается исходное изображение, которое обрезано и к нему применен фильтр. Наклейки отображаются поверх в виде самостоятельных View, которые можно перемещать. Про наклейки будет отдельный пост.

Отображение фото / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Обрезка фото и поворот - отображается исходное изображение, которое можно вращать и поверх него будет отображаться рамка для обрезки фотографии. Про это будет отдельный пост.

Отображение фото / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Во всех трех режимах изображение можно масштабировать двумя пальцами или слайдером и двигать. Режим аналогичен системному приложению "Фото".

Про добавление стикеров и работу рамки для обрезки я расскажу в будущих статьях, в этой будет только просмотр.

Какие есть сложности в работе с просмотром фото в SwiftUI

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

Основные проблемы:

  1. ScrollView в SwiftUI не умеет из коробки работать с Zoom. Его можно эмулировать через ScaleEffect и MagnificationGesture(). Однако смещение будет сбиваться, т.к. жест масштабирования не возвращает свои стартовые точки

  2. Аналогичная проблема будет с вращением. От жеста можно получить угол поворота, но не центр

  3. Тяжело развести жесты обрезки и скролла. Если привязать жест к "ручке" изменения размера, то при резком перемещении он будет "отваливаться" и перемещение будет сбрасываться. И я не смог привязать жест к контейнеру и фильтровать его работу в зависимости от того, где был начат жест.

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

Результат опытов со SwiftUI можно посмотреть в Github

Просмотр фото на UIKit и обертка в Representative

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

Код готового компонента можно найти на Github

Как будет устроен компонент:

Отображение фото / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост
  • Scroll View - будет выполнять Zoom и Scroll

  • Container View - будет делать обрезку фото в режиме редактирования и обеспечивать зону для вращения фото в режиме обрезки и поворота. Внутри ScrollView, является ViewForZooming

  • Image View - будет отображать изображение, обрезанное или оригинальное. Центром привязано к Container View. В режиме редактирования смещается так, чтобы видимая в Container View часть совпадала с CropZoneView. В режиме редактирования и обрезки поворачивается на заданный угол.

  • CropZoneView - виртуальная View для отладки. В режиме обрезки рамка обрезки совпадает с CropZoneView, но отрисовывается в глобальных координатах, чтобы ручки изменения размера были одного размера независимо от масштаба.

Какие функции будет реализовывать компонент:

Скролл с отступами и центрированием

Если изображение больше, чем ScrollView, то к изображению будут добавлены фиксированные отступы, чтобы с ним было удобнее работать и просматривать. Если размер изображения меньше, чем ScrollView, к изображению будут добавлены плавающие отступы, чтобы его центрировать.

Для этого необходимо управлять параметром ContentOffset

Отображение фото / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Расположение фото фото и поворот

Для режимов редактора фото и обрезки необходимо повернуть фотографию, для этого она будет центром привязана к контейнеру внутри ScrollView, И с помощью transform будет повернута на необходимый угол. Изображение в режиме просмотра будет работать также, только у него угол всегда равен нулю

Отображение фото / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Обрезка фото

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

Сохранение данных об обрезке и повороте фотографии

Обрезанная и повернутая фотография получается из оригинальной с помощью серии операций:

  1. Сдвиг центра - сохраняется как Crop Center

  2. Новый размер - сохраняется как Crop Size

  3. Поворот - В приложении для детей есть ползунок с диапазоном поворота -45...45 градусов и кнопка вращения на 90 градусов. Поэтому хранятся два значения, малый угол и квадрант нуля

Все размеры хранятся в виде относительных значений. За 1 берется диагональ изображения. Сдвиг центра отсчитывается от центра изображения.

Отображение фото / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Использование Representable

Чтобы SwiftUI работал с UIKit не вдаваясь в детали, создается UIViewRepresentable или UIViewControllerReplresentable.

У меня ViewController, поэтому дальше будет про него, но для UIVIew работает точно так же. Его код можно найти на GitHub

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

View создается в методе makeUIViewController. Необходимо вернуть настроенный ViewController

У этого объекта создаются свойства SwiftUI: @State, @Bindable и др. Изменение эти свойств приводит к вызову метода updateUIViewController, который позволяет обновить свойства у UIKit ViewController.

Для обновления данных в обратную сторону создается объект, который называется Coordinator. В нем можно разместить все Binding переменные и передать сам объект во ViewController, чтобы обновлять значения.

При изменений значений внутри ViewController, необходимо новые значения сохранить в wrappedValue Binding координатора. Тогда SwiftUI пробросит их всем подпищикам изменений

Заключение

В этой статье я рассказал о том, как решил не мучаться себя и вместо чистого SwiftUI с костылями использовал UIKit через Representable.

Все работающие примеры есть в проекте на Github, их можно скачать и запустить.

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

Показать полностью 7
[моё] IT Дневник Научпоп Swift Программирование Длиннопост
0
1
andrey.zarembo
andrey.zarembo
1 год назад
Серия Дневник разработчика

Swift Combine - Часть 1 / 5 min⁠⁠

Это первая статья о работе со Swift Combine. Из неё вы узнаете о реактивном программировании и операциях преобразования, фильтрации и свертки сигналов.

Swift Combine - Часть 1 / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

В этой статье вы прочитаете:

  • Что такое Combine и реактивное программирование

  • Операции: Mapping, Filtering, Reducing

Примеры кода из этой статьи доступны на GitHub

Что такое Combine и реактивное программирование

Swift Combine - это интегрированная в язык Swift реализация реактивного программирования.

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

Пусть для простоты будут следующие операции:

  1. Получение ввода от пользователя, фильтрация 3х символов

  2. Формирование сетевого запроса для сервера и отправка запроса

  3. Расшифровка ответа

  4. Вывод результатов, сохраняя данные в переменную

В объектно-ориентированном императивном подходе на базе MVC у ViewController будет примерно так(Написал из головы):

Swift Combine - Часть 1 / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

В случае с реактивным программированием, это будет выглядеть примерно так:

Swift Combine - Часть 1 / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Получается поток обработчиков, в которых нет "висячих" результатов работы. И прямой поток данных, и ветка ошибок должны иметь свой обработчик, иначе приложение не скомпилируется.

В целом плюсы реактивного подхода:

  1. Однозначность обработки положительных и отрицательных исходов

  2. Фильтрация событий по времени. В примере из всех вводов пользователя за 0.3 секунды будет взят последний

  3. Возможность собирать несколько потоков и разделять на несколько потоков. Например, можно отправить запрос к кэшу на диске и сети и взять тот, что вернется быстрее без ошибки

  4. Удобная связка со SwiftUI и Async благодаря тому, что Combine - часть языка, а не внешняя библиотека. Большая часть системных операций и UI также готова к работе с Combine

Источники данных и потребители

Эту тему я рассмотрю подробнее в будущей статье, но кратко опишу.

В Combine источники данных - это Publisher и дочерние типы. У основных системных вызовов, например у сетевых инструментов, SwiftUI и др. есть ответы и события в формате Publisher.

Результат работы можно назначить в свойство через метод assign или обработать как callback в методе sink. Они возвращают объект Cancellable, который надо сохрнить в переменной напрямую или через метод store, иначе память освободится и обработчик не будет работать.

Песочница для реактивных операций

Swift Combine - Часть 1 / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

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

Песочница доступа в Github со всеми примерами, включая будущие статьи

Операции и преобразования

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

У некоторых операций есть try... вариант, который позволяет вернуть ошибку, но я их дополнительно не описывал.

Mapping

Swift Combine - Часть 1 / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примеров

  • .map - преобразование данных, количество на входе равно количеству на выходе. Можно вернуть объект того же типа или поменять тип. Аналогично методу map массива. В примере звезды заменяются треугольниками.

  • .mapError - преобразование ошибок, ошибка на входе - ошибка на выходе. Позволяет свести все ошибки к одному типу для обработки в конце. Обработка в блоке. В примере ошибка меняется на зеленую ошибку.

  • .replaceNil - замена nil на фиксированный объект. В примере nil заменяется оранжевой звездой

  • .scan - похоже на map, но результат передается как второй параметр в блок обработки. На первый шаг подается первый аргумент вызова scan. В примере подается объект-образец, в цвет которого будут покрашены все элементы

  • .setFailureType - позволяет поменять тип ошибки для синхронизации типов. Never поменять на свой тип. В примере в коде сначала ошибка приводится к Never с помощью Catch, а затем обратно к DemoError с помощью setFailureType.

  • .map Keypath - аналогично map, но можно передать 1 keypath, чтобы получить на выходе значение. Если передать 2 или 3 keypath, будет tuple. В примере 1 keypath .color используется для создания результатов, а в примере 2 .color и .icon используются для создания копий в результатах

Filtering

Swift Combine - Часть 1 / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примеров

  • .filter - аналогично методу над массивом, возвращает только подходящие элементы. В примере не пропускает треугольники

  • .compactMap - аналгично map, но nil пропускается. В примере для сравнения map и compactMap на тех же данных

  • .removeDuplicates - удаляет одинаковые, идущие подряд элементы. Для этого они должны соответствовать протоколу Equitable. В примере синий и синяя звезда - разные.

  • .removeDuplicates(by: ) - удаляет одинаковые, идущие подряд элементы, определяя "одинаковость" в блоке. В примере одинаковость определяется оп цвету, В примере синий и синяя звезда - одно и то же.

  • .replaceEmpty - если поток закончился и ничего не вернул, то можно задать какое-то значение. В примере 1 возвращается оранжевый, а во втором были данные - возвращаются они

  • .replaceError - возвращает определенные данные, если была ошибка. В отличие от catch нет возможности обработать ошибку, она заменяется одним значением на входе

Reducing

Swift Combine - Часть 1 / 5 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примеров

  • .collect - собирает объекты в массив. В примере три цветных объекта становится одним массивом

  • .collect(n) - собирает объекты в массив по несколько штук. В примере 6 объектов собираются в две кучки по три. В примере два в кучки 4 и 2

  • .collect(.byTime ) - собирает объекты по несколько штук, пришедших в указанный интервал

  • .collect(.byTimeOrCount ) - также собирает объекты по несколько штук за указанный интервал, но не больше указанного количества

  • .ignoreOutput - гинорирует то, что было в выдаче. Вернет только сигнал окончания потока или ошибку

  • .reduce - работет как scan, но на выходе будет только 1 объект, последний. В примере суммируются числа на объектах

Заключение

В этой статье кратко рассказано что такое Swift Combine и основные операции преобразования, фильтрации и свертки сигналов с визуализацией. Все визуализации получены в песочнице.

В будущих статьях я расскажу про операции для работы с несколькими потоками вместе, про входы/выходы и примеры использования.

Все работающие примеры preview есть в проекте на Github, их можно скачать и запустить.

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

Показать полностью 6
[моё] IT Дневник Научпоп Swift Программирование Длиннопост
0
4
andrey.zarembo
andrey.zarembo
1 год назад
Серия Дневник разработчика

Галерея фотографий / 5min⁠⁠

Из этой статьи вы узнаете как создать галерею для просмотра фотографий с динамическим размером ячеек и выбором фотографий

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

В этой статье вы прочитаете:

  • Как создать фото-галерею

  • Как реализовать выбор фотографий

  • Как сделать переключение размера фото

Примеры изображений и кода из этой статьи доступны на GitHub

Как создать фото-галерею

Чтобы создать фото-галерею необходимо 3 компонента:

  1. Источник фотографий

  2. Шаблон фотографии

  3. Лента для вывода фотографий

Источник фотографий

Под источником фотографий я подразумеваю объект, который будет сообщать ленте о том, сколько есть фотографий и принимать событие о том, что ленту докрутили до конца и нужно загрузить ещё фотографий.

💡 Добавление/удаление фотографий будет описано в одной из будущих статей

Сами фотографии могут загружаться через Core Data, Swift Data, из сети по API. Мой проект использует Realm. Для примера приложение будет загружать фотографии из временной папку, куда будут скопированы ресурсы.

Источник фотографий - это объект типа ObservableObject - специальный тип объекта, который при изменении свойств, помеченных как published будет вызывать обновление состояния View.

Потребуется два таких:

  1. Массив фотографий

  2. Состояние активной загрузки

У загрузки будет три состояния:

  • Есть контент для загрузки

  • Активная загрузка

  • Все загружено

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

Сама фотография должна хранить в себе ссылку на изображение, а также флаг качества фотографии. Также будет необходим уникальный ID для вывода списка фото в галерее и для выбора фотографий

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

Загрузка фотографий

Также потребуется метод loadMore, который будет вызываться для дозагрузки фото. Этот метод будет вызываться при скролле галереи.

Загрузка фотографий будет выполняться пачками. Загружать фото будет выполнять объект, который будет реализует протокол ContentSource. В метод загрузки будет передаваться последняя фотография в ленте и размер пачки.

💡 Это позволит переключаться между разными хранилищами, я использую Realm и отладочные хранилища с разными фотографиями.

Сам метод loadMore будет выставлять режим загрузки, получать новые фотографии и анализировать. Если пусто, то будет выставляться режим done, иначе - hasMore

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

💡Здесь нет обработки ошибок, чтобы не загромождать пример

Автоматическая загрузка

Автоматическая загрузка будет осуществляться аналогично примеру с исчезающими боковыми кнопками. Потребуется два GeometryReader:

  • Один будет определять высоту контейнера галереи

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

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Разница этих двух значений больше нуля будет означать, что нижняя граница фотографий находится выше нижнего края экрана. Если при этом галерея находится в режиме hasMore, можно загрузить ещё фотографий.

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

Шаблон фотографии

Шаблон фотографии - это ZStack, в котором будет находиться изображение, а также в углах будут находиться индикаторы для выбора фото и качества фотографии

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Фотографии большие, поэтому загружать их целиком при каждом отображении ленты будет слишком накладно - будет нужна загрузка, масштабирование и обрезка. Чтобы этого избежать необходимо кэширование изображений.

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

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

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

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

Лента для вывода фотографий

Для отображения галереи потребуется ScrollView, внутри которого будут LazyVGrid и индикатор загрузки. Внутри галереи фотографии будут выводиться через ForEach для массива.

Необходимо добавить GeometryReader для определения размера фотографии.

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

Как реализовать выбор фотографий

Для выбора фото необходимо создать массив с ID фотографий. Галерея будет проверять наличие ID фотографии в этом массиве и выставлять флаг Selected. Нажатие на фотографию буде добавлять и удалять ID в массив.

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

💡 Кнопка выбора фотографии добавляется как overlay. Чтобы она была прозрачной в её label добавляется Color.clear, но для того, чтобы её можно было нажать, необходимо ей задать "тело" прямоугольной формы с помощью contentShape и clipShape

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

Как сделать переключение размера фото

В галерее будет три режима вывода фотографии, мелкие, средние и крупные фотографии. Если взять средний iPhone, например 14, то на экране будет 5, 3 и 1 соответственно. Конечно выводить в ленте по 1 фото на экран iPad Pro будет слишком, поэтому на планшетах будет примерно такой же размер фотографий.

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

LazyVGrid позволяет задать автоматическое определение количества колонок для размеров ячеек в границах размера. Необходимо создать enum с тремя параметрами размера фото, а также массив размеров фотографий.

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

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

Галерея фотографий / 5min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера выше

Выглядеть и работать переключение будет так

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

Заключение

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

Все работающие примеры есть в проекте на Github, их можно скачать и запустить.

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

Показать полностью 12 1
[моё] IT Дневник Научпоп Swift Программирование Длиннопост Видео Без звука
2
7
andrey.zarembo
andrey.zarembo
1 год назад
Серия Дневник разработчика

SwiftUI View Builder / 4 min⁠⁠

В этой статье я расскажу про то, как работает создание интерфейса в SwiftUI при помощи ViewBuilder

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

В этой статье вы прочитаете:

  • Почему у ViewBuilder такой формат

  • Как создать свой контейнер для View

  • 3 способа преобразовать результат ViewBuilder в массив

Примеры изображений и кода из этой статьи доступны на GitHub

Почему у ViewBuilder такой формат

Если посмотреть на протокол View, то видно, что body - это переменная, которая возвращает объект, реализующий протокол View, который должен в итоге получить конкретный тип. И следующий код будет прекрасно работать. Следующий код будет работать

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Но Apple стремится упростить синтаксис для SwiftUI, поэтому у body есть два префикса - ViewBuilder и MainActor.

ViewBuilder - это обертка вокруг блока, который создает View. Он дает возможность вернуть несколько View, описывая их подряд, по строчкам, не используя дополнительные артефакты в виде массивов.

На выходе создается объект типа AnyView<TupleView<(...)>>. Т.е. следующие два блока кода эквивалентны

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Как создать свой контейнер для View

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

Для этого необходимо объявить Generic структуру, в которой параметром шаблона будет Content типа View. Необходимо объявить свойство content с типом ViewBuilder, которое будет блоком-фабрикой.

А применить стиль можно с помощью обертки этого content в необходимый существующий контейнер и в Group - специальную конструкцию, которая применяет все свои модификаторы к тому, что находится внутри.

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Также Group можно использовать, чтобы применить какой-то стиль к View, созданному по условию

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

ViewExtractor и как преобразовать содержимое ViewBuilder в массив

Увы, способ выше не позволяет каким-то образом изменить порядок дочерних элементов, добавить между ними какие-то объекты или использовать какое-то форматирование, зависящее от количества элементов.

Первый путь

Библиотека ViewExtractor. Она дает достаточно простой синтаксис

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

В ней используется тайное знание о том, как устроен SwiftUI, _VariadicView.Tree хранит к себе элементы из ViewBuilder. Но это использование приватного API и Apple может заблокировать такое приложение или оно может сломаться после обновления операционной ситсемы.

Второй путь

Манипуляции c памятью в расширении над ExtensionView. Суть в том, чтобы взять из TupleView дочерние объекты и сказать, что эта область памяти - массив.

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Но Mirror работает не очень быстро, т.к. отключает часть оптимизаций на объекте, чтобы добраться до его элементов. А withUnsafeBytes может обрушить приложение, если в будущей версии iOS изменится структура хранения в памяти.

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Из-за того, что ViewBuilder все оборачивает в AnyView, content[index] не сможет использоваться для сравнения типа, но index и количество объектов позволяют добавить разделители, если необходимо.

Третий путь

Можно создать контейнер, который будет принимать на вход массив элементов определенного протокола.

Плюсы

  • Можно сортировать и фильтровать объекты

  • Сохраняется информация о типах и можно изменять отдельные параметры

  • Доступен индекс объекта и их количество

Минусы

  • Синтаксис требует квадратных скобок и запятых

  • Каждый класс должен соответствовать протоколу через extension

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

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Придется отказаться от синтаксиса ViewBuilder, но такой способ не отвалится внезапно от обновления iOS и даст возможность сортировать и фильтровать объекты, а также обращаться к их типам.

SwiftUI View Builder / 4 min IT, Дневник, Научпоп, Swift, Программирование, Длиннопост

Код примера выше

Заключение

В этой статье разобрана суть ViewBuilder, почему он такой, как создать свой контейнер и как превратить вывод ViewBuilder в массив тремя способами.

Все работающие примеры preview есть в проекте на Github, их можно скачать и запустить.

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

Показать полностью 9
[моё] IT Дневник Научпоп Swift Программирование Длиннопост
0
3
andrey.zarembo
andrey.zarembo
1 год назад
Серия Дневник разработчика

Родительский контроль / 4 мин⁠⁠

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

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Из этой статьи вы узнаете:

  • Как и зачем проверять возраст

  • Как создать состояние для всего приложения и управлять им

  • Как создать всплывающее модальное окно проверки возраста

  • Как создать простые случайные примеры проверки возраста

Исходный код всех примеров для этой главы доступен на Git.

Этот подход можно использовать для Premium подписки приложения, а вместе проверки возраста показывать PayWall.

Как и зачем проверять возраст

В мобильном приложении есть несколько разделов и функций, которыми должны пользоваться только взрослые:

  • Отправка, удаление и сохранение фото в галерею

  • Оформление подписки

В соответствии с пунктом 1.3 Kids Category документа о правилах аудита приложений App Store Review Guidelines для доступа в эти разделы необходимо создать Parental Gate

Самые популярные способы проверки возраста: простые арифметические операции, операции с фигурами, но бывают и забавные, например "Что делает видеомагнитофон?" с вариантами ответов.

💡 В моём приложении я буду использовать задачи на арифметику. Сколько будет 4+9, 4x4 или 25/5 с тремя вариантами ответов.

Как создать состояние для всего приложения и управлять им

Родительский режим включается по всему приложению, это состояние будет храниться на уровне приложения. И транслироваться по цепочке как Binding, чтобы можно было добавить переключающие кнопки и обновлять состояние приложения.

Есть два подхода к управлению приложением:

  1. Добавить отдельное состояние детский режим - родительский режим и отдельно управлять состоянием "Запрос разрешения"
    Плюсы - четкость разделения назначения каждого флага
    Минусы - нужно разбираться с 4 состояниями приложения в обработчиках кнопок

  2. Создать три состояния детский режим - запрос разрешения - родительский режим
    Плюсы
    - четкая последовательность смены состояний
    Минусы - нельзя проверить возраст без перехода в родительский режим

💡 Какой вариант выбрать? Я выберу вариант с тремя состояниями. Мне важно, чтобы родительский режим всегда выделялся цветом, а четкость назначения флагов в таком маленьком проекте не важна

Состояние будет храниться в enum, который будет работать как конечный автомат.
Метод next будет переключать состояния по кругу:
Детский режим -> Проверка возраста -> Родительский режим.

Метод cancel возвращает состояние из Проверка возраста в Детский режим. Другие состояния не меняются.

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Про переключение фонов кнопок и панелей я рассказывал в предыдущих статьях. Код выглядит так:

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Как создать всплывающее модальное окно проверки возраста

Отображение панели будет завязано на состояние запроса возраста

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

Панель проверки возраста появляется модально - т.е. перекрывает целиком весь экран приложения и блокирует работу с ним, пока не будет выбран один из ответов.

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Для отображения панели требуется 6 параметров: A, Оператор, Б и три ответа. Я разделил пример на три части, чтобы можно было знак операции сделать крупнее.

Отображаться панель будет если состояние приложения меняется на "Запрос разрешения". Эта же кнопка будет возвращать детский режим.

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

А сворачиваться панель будет по тапу на фон или нажатию на кнопку ответа. Правильный ответ продвинет состояние вперед методом next, а неправильный или тап на фон - cancel.

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

Как создать простые случайные примеры проверки возраста

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

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

Алгоритм создания операции такой:

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код реализации

Создавать новый пример необходимо при отображении окна. При этом важно помнить, что SwiftUI может обновлять состояние View и пересоздавать всю структуру в случае внешних событий. Поэтому создавать пример необходимо в модификаторе onAppear панели.

Родительский контроль / 4 мин IT, Дневник, Научпоп, Swift, Программирование, Длиннопост, Видео, Без звука

Код примера

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

Заключение

В этой статье я рассказал о том, как добавить в приложении специальный режим для всего приложения. Он применим не только для родительского режима, но может использоваться и для Premium раздела приложения.

Все работающие примеры есть в проекте на Github, их можно скачать и запустить.

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

Показать полностью 8 1
[моё] IT Дневник Научпоп Swift Программирование Длиннопост Видео Без звука
0
Посты не найдены
О нас
О Пикабу Контакты Реклама Сообщить об ошибке Сообщить о нарушении законодательства Отзывы и предложения Новости Пикабу Мобильное приложение RSS
Информация
Помощь Кодекс Пикабу Команда Пикабу Конфиденциальность Правила соцсети О рекомендациях О компании
Наши проекты
Блоги Работа Промокоды Игры Курсы
Партнёры
Промокоды Биг Гик Промокоды Lamoda Промокоды Мвидео Промокоды Яндекс Директ Промокоды Отелло Промокоды Aroma Butik Промокоды Яндекс Путешествия Постила Футбол сегодня
На информационном ресурсе Pikabu.ru применяются рекомендательные технологии