📱 Осваиваем скроллинг в SwiftUI
Начиная с iOS 17 в SwiftUI появилась функция
В этой статье на реальном примере автор показывает, как использовать
👉 Читать статью
#новость #ios #swift
Начиная с iOS 17 в SwiftUI появилась функция
scrollTargetBehavior, позволяющая разработчикам управлять поведением прокрутки с большей точностью. Будь то выравнивание представлений или реализация пользовательских эффектов пагинации, ScrollTargetBehavior обеспечивает надежную поддержку. Что еще более важно, разработчики могут создавать собственные реализации функции для удовлетворения конкретных потребностей. В этой статье на реальном примере автор показывает, как использовать
scrollTargetBehavior и в конечном итоге реализовать пользовательскую логику управления прокруткой.👉 Читать статью
#новость #ios #swift
🥰4❤1
🍏 Назовите 5 утверждений передачи управления (Control Transfer Statements)
Вот онислева-направо (сверху-вниз):
•
•
•
•
•
Операторы передачи управления изменяют порядок выполнения вашего кода.
Например, вы можете использовать оператор передачи управления
#swift
Вот они
•
Break•
Continue•
Fallthrough•
Throw•
ReturnОператоры передачи управления изменяют порядок выполнения вашего кода.
Например, вы можете использовать оператор передачи управления
break для завершения выполнения цикла for, когда продолжение цикла считается ненужным:for choice in choices:if isCorrect(choice):print(«Correct choice found!»)break
#swift
❤2
😴 Task.sleep() и Task.yield()
В Swift Concurrency мы можем использовать Task.sleep() и Task.yield(), чтобы перевести конкретную задачу в режим ожидания или приостановки в течение определённого периода времени. Оба они выглядят и ведут себя одинаково, но есть несколько существенных различий, о которых следует знать Swift-разработчикам.
Зная эти различия, вы сможете лучше понять, когда и как использовать те или иные функции, а также оптимизировать свой код соответствующим образом. Прежде чем перейти к рассмотрению различий, автор сначала объясняет, что делают эти модификаторы.
👉 Читать статью
#новость #swift
В Swift Concurrency мы можем использовать Task.sleep() и Task.yield(), чтобы перевести конкретную задачу в режим ожидания или приостановки в течение определённого периода времени. Оба они выглядят и ведут себя одинаково, но есть несколько существенных различий, о которых следует знать Swift-разработчикам.
Зная эти различия, вы сможете лучше понять, когда и как использовать те или иные функции, а также оптимизировать свой код соответствующим образом. Прежде чем перейти к рассмотрению различий, автор сначала объясняет, что делают эти модификаторы.
👉 Читать статью
#новость #swift
🤩3❤1👍1
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Как и большинство языков программирования, Swift позволяет каждой функции возвращать только одно значение. Если этот элемент является примитивным типом, вы вернёте только одно значение.
Кроме того, объект может быть сложным типом, например классом, структурой, кортежем или массивом. В этой ситуации вы можете объединить несколько значений в сложный тип. После этого вы формально возвращаете один элемент с несколькими значениями, хранящимися внутри этой структуры данных.
func functionWithMultipleReturnValues(
val1: Int,
val2: Int
) -> (sum: Int, product: Int) {
let sum = val1 + val2
let prod = val1 * val2
return (sum, prod)
}
let result = functionWithMultipleReturnValues(val1: 10, val2: 20)
let s = result.sum
let p = result.product
#буст #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Оператор
guard используется для передачи управления программой за пределы области видимости. Оператор guard похож на оператор if, но он запускается только тогда, когда некоторые условия не выполняются.Например, оператор
guard используется для выхода из функции:func myFun() {
guard false else {
print("This block is run")
return
}
print("This is never run")
}
myFun()Вывод:
This block is run
#буст #JuniorKit #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
AppCode — мощная IDE от JetBrains для iOS-разработки, и если ты хочешь писать код быстрее и чище — эти хоткеи must have.
Shift + F6Переименовывает переменные, классы, методы — и IDE сама обновит все упоминания.
Ctrl + Alt + V (Cmd + Option + V на Mac)Выделяешь выражение → IDE создаёт переменную автоматически.
Ctrl + Alt + M (Cmd + Option + M)Отлично помогает разгрести длинные функции и вынести повторяющийся код.
Ctrl + Alt + N (Cmd + Option + N)Обратно «встраивает» метод или переменную прямо в место вызова.
Alt + Delete (Cmd + Delete)Проверит, где элемент используется, и безопасно удалит его без сюрпризов.
#буст #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Дженерики позволяют писать гибкий и многократно используемый код, который может работать с любым типом данных.
Представьте, что вы пишете трехмерную векторную структуру, но хотите иметь возможность создавать векторы, используя целые, плавающие и двойные числа. Вы определенно не хотите писать один и тот же код для каждого типа данных отдельно.
Именно здесь вы можете использовать дженерики.
Например, вы можете создать общий тип для параметров (для представления любого типа), используя букву, например
T, следующим образом:struct Vec3D<T> {
let x, y, z: T
init(x: T, y: T, z: T) {
self.x = x
self.y = y
self.z = z
}
}
let intVector = Vec3D(x: 1, y: 2, z: 5)
let floatVector = Vec3D(x: 1.0, y: 2.0, z: 5.0)#буст #JuniorKit #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
if age >= 18 {
driveCar()
} else {
doNotDrive()
}Этот код хорошо работает - но можете ли вы предложить небольшое улучшение рефакторинга, чтобы сделать его еще лучше?
age >= 18 ? driveCar() : doNotDrive()
#буст #JuniorKit #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱6❤2😢2🤔1
В проектах на SwiftUI тело
View может быстро разрастись: десятки вложенных VStack/HStack, логика отображения, стили — всё в одном месте. Вот три практики, которые помогут сделать код более понятным, гибким и удобным в сопровождении.
Если часть интерфейса имеет собственное назначение — вынеси её в отдельный
struct-View:struct ArticleRow: View {
let article: Article
var body: some View {
HStack { … }
}
}Затем:
ForEach(articles, id: \.url) { article in
ArticleRow(article: article)
}Это делает главный
View более “обзорным” и облегчает повторное использование. Когда видишь дублирования стилей, вынеси их:
struct CardStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color(.secondarySystemBackground))
.cornerRadius(8)
}
}
extension View {
func cardStyle() -> some View {
modifier(CardStyle())
}
}Теперь:
ArticleRow(article: article)
.cardStyle()
Так ты централизуешь “язык дизайна” и можешь менять стиль в одном месте.
Если повторяются шаблоны (например, “заголовок секции”), можно сделать расширение:
extension View {
func sectionHeader(_ title: String) -> some View {
VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.title3)
.bold()
self
}
}
}Пример:
VStack {
ForEach(articles, id: \.url) { article in
ArticleRow(article: article)
}
}
.sectionHeader("Articles")Такие расширения уменьшают вложенность и делают код декларативнее.
Если вы не уверены, стоит ли что-то извлекать, спросите:
Есть ли у этого элемента пользовательского интерфейса чёткое назначение и возможность повторного использования?
View или ViewModifier.Цель состоит не в том, чтобы сократить количество строк кода, а в том, чтобы повысить ясность и возможность повторного использования для улучшения архитектуры SwiftUI.
#АрхитектурныйКод #SeniorView #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🤝1
Полиморфизм представлять возможность взаимозаменяемости типов, которые находятся в одной иерархии классов. Например, возьмем следующую иерархию классов:
class Person{
var name: String
var age: Int
init(name: String, age: Int){
self.name = name
self.age = age
}
func display(){
print("Имя: \(name) Возраст: \(age)")
}
}
class Employee : Person{
var company: String
init(name: String, age: Int, company: String) {
self.company = company
super.init(name:name, age: age)
}
override func display(){
print("Имя: \(name) Возраст: \(age) Сотрудник компании: \(company)")
}
}
class Manager : Employee{
override func display(){
print("Имя: \(name) Возраст: \(age) Менеджер компании: \(company)")
}
}В данном случае класс Manager (менеджер компании) наследуется от класса Employee (сотрудник компании), а класс Employee - от класса Person (человек). Тем самым класс Manager ненапрямую тоже наследуется от Person.
Поскольку и сотрудник компании и менеджер компании в то же время являются людьми, то есть объектами класса Person, то мы можем написать следующим образом:
let tom: Person = Person(name:"Tom", age: 23)
let bob: Person = Employee(name: "Bob", age: 28, company: "Apple")
let alice: Person = Manager(name: "Alice", age: 31, company: "Microsoft")
Все три константы представляют тип Person, однако первая хранит ссылку на объект Person, вторая - на объект Employee, а третья - на объект Manager. Таким образом, переменная или константа одного типа может принимать многообразные формы в зависимости от конкретного объекта, на который она указывает.
Но что будет, если мы вызовем метод
display() для всех трех объектов:let tom: Person = Person(name:"Tom", age: 23)
let bob: Person = Employee(name: "Bob", age: 28, company: "Apple")
let alice: Person = Manager(name: "Alice", age: 31, company: "Microsoft")
tom.display() // Имя: Tom Возраст: 23
bob.display() // Имя: Bob Возраст: 28 Сотрудник компании: Apple
alice.display() // Имя: Alice Возраст: 31 Менеджер компании: Microsoft
Несмотря на то, что все три константы представляют тип Person, при вызове метода display будет вызываться реализация метода именно того класса, ссылку на объект которого хранит константа. Данный примем называется динамической диспетчеризацией - во время выполнения программы на основании типа объекта система решает, какую именно реализацию метода вызывать.
В одной стороны, это нам дает ряд преимуществ - мы можем работать с объектом производного типа как с объектом базового типа и использовать его везде, где требуется объект базового типа. Но с другой стороны, поскольку решение о выборе реализации принимается во время выполнения, то это несколько замедляет общий ход работы программы.
#буст #JuniorKit #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🥱1
В Clean Swift приложение состоит из сцен, т.е. каждый экран приложения — это одна сцена. Основное взаимодействие в сцене идет через последовательный цикл между компонентами ViewController -> Interactor -> Presenter. Это называется VIP цикл.
Мостом между компонентами выступает файл Models, который хранит в себе передаваемые данные. Так же есть Router, отвечающий за переход и передачу данных между сценами, и Worker, который берет часть логики Interactor’a на себя.
🔹 View
Storyboard’ы, XIB’ы или UI элементы, написанные через код.
🔹 ViewController
Отвечает только за конфигурацию и взаимодействие с View. В контроллере не должно находиться никакой бизнес логики, взаимодействия с сетью, вычислений и так далее.
Его задача обрабатывать события с View, отображать или отправлять данные (без обработки и проверок) в Interactor.
🔹 Interactor
Содержит в себе бизнес логику сцены.
Он работает с сетью, базой данных и модулями устройства.
Interactor получает запрос из ViewController’a (с данными или пустой), обрабатывает его и, если это требуется, передает новые данные в Presenter.
🔹 Presenter
Занимается подготовкой данных для отображения.
Как пример, добавить маску на номер телефона или сделать первую букву в названии заглавной.
Обрабатывает данные, получение из Interactor’a, после чего отправляет их обратно во ViewController.
🔹 Models
Набор структур для передачи данных между компонентами VIP цикла. Каждый круг цикла имеет в себе 3 вида структур:
🔹 Worker
Разгружает Interactor, забирая на себя часть бизнес логики приложения, если Interactor стремительно разрастается.
Так же можно создавать общие для всех сцен Worker’ы, если их функционал используется в нескольких сценах.
Как пример, в Worker можно выносить логику работы с сетью или базой данных.
🔹 Router
В Router выносится вся логика, отвечающая за переходы и передачу данных между сценами.
____________________
Для прояснения картины работы VIP цикла приведу стандартный пример — авторизация.
1. Пользователь ввел свой логин и пароль, нажал на кнопку авторизации
2. У ViewController срабатывает IBAction, после чего создается структура с введенными, в TextField’ы, данными пользователя (Models -> Request)
3. Созданная структура передается в метод fetchUser в Interactor’e
4. Interactor отправляет запрос в сеть и получает ответ об успешности авторизации
5. На основе полученных данных, создает структуру с результатом (Models -> Response) и передается в метод presentUser в Presenter’e
6. Presenter форматирует данные по необходимости и возвращает их (Models -> ViewModel) в метод displayUser в ViewController’e
7. ViewController отображает полученные данные пользователю. В случае с авторизацией, может выводиться ошибка или срабатывать переход на другую сцену с помощью Router
Таким образом, мы получаем единую и последовательную структуру, с распределением обязанностей на компоненты.
#АрхитектурныйКод #SeniorView #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Please open Telegram to view this post
VIEW IN TELEGRAM