Большинство iOS-разработчиков используют Xcode Debugger только для банальных
breakpoint и просмотра переменных. Но в нём есть куда больше фишек, которые реально экономят время.🔹 Conditional breakpoints
Можно остановить выполнение не всегда, а только при выполнении условия.
Например, поставить брейкпоинт на метод и задать условие
userId == 42. Тогда дебаггер не будет дёргать вас на каждом вызове.🔹 Exception Breakpoint
Очень полезно для поиска падений — Xcode сразу остановит выполнение в момент выброса исключения, до того как приложение упадёт. Настраивается через вкладку Breakpoints Navigator → + Exception Breakpoint.
🔹 Symbolic breakpoints
Позволяют ловить вызовы по имени метода или функции, даже если кода у вас нет (например, методы UIKit). Можно отследить момент, когда система вызывает
viewDidAppear: или layoutSubviews.🔹 Debug view hierarchy
Через кнопку "Debug View Hierarchy" можно визуально исследовать всю иерархию вьюх, включая невидимые и перекрытые элементы. Отличный способ поймать «невидимую кнопку», которая блокирует тап.
🔹 LLDB команды прямо в консоли
po object — печатает объект с вызовом description.expr — позволяет выполнять произвольный Swift/ObjC код прямо во время остановки.watchpoint set variable foo — можно следить за изменением конкретной переменной.🔹 Runtime Issues
Xcode сам подсказывает баги вроде «UI update вне main thread» или «дважды добавили constraint». Не игнорируйте эти подсказки — они часто спасают кучу времени.
Что из этого вы используете?
#буст #MiddlePath #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Обрезка — это удаление частей контента за пределами заданной формы. Представьте, что вы используете формочки для печенья: всё, что находится внутри формочки, остаётся, а всё, что снаружи, удаляется.
В Compose это делается с помощью
Modifier.clip функции:Image(
painter = painterResource(R.drawable.avatar),
contentDescription = null,
modifier = Modifier
.size(72.dp)
.clip(CircleShape)
)
Здесь изображение обрезается по кругу, независимо от фактических границ растрового изображения.
Если встроенных фигур (
CircleShape, RoundedCornerShape и т. д.) недостаточно, вы можете создать собственную Shape и нарисовать свой собственный контур. Например:class SquishedOvalShape : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
return Outline.Generic(
Path().apply {
addOval(Rect(0f, size.height / 4f, size.width, size.height))
}
)
}
}Примените его, как и любую другую форму:
Modifier.clip(SquishedOvalShape())
#PixelPerfect #MiddlePath #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Объекты можно объявлять внутри класса, при этом нет каких-либо ограничений по их количеству. Но только один объект можно пометить ключевым словом companion object в рамках одного класса.
Синглтон-свойство
companion object достигается за счет того, что он создается внутри класса в качестве статического поля. Он будет инициализирован при первом обращении к нему или при создании первого экземпляра класса, в котором он объявлен.Важно отметить, что
companion object будет инициализирован первым, а затем уже будет создан экземпляр класса:class MyClass {
init {
// Выполняется всегда после инициализации companion object
}
companion object {
init {
// Выполняется всегда перед блоком init содержащего класса
}
}
}
val myClass = MyClass()Такому объекту можно не указывать свое имя, и обращаться к методам и свойствам объекта через имя содержащего его класса без явного указания имени объекта.
class SomeClass {
companion object {
fun create()
}
}
val someClass = SomeClass.create()Компилируется в
public static final class на Java. Работает подобно ключевому слову static в Java.#буст #MiddlePath #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
👏3
Заметили, что ваше приложение долго запускается? Этот промпт поможет найти и устранить узкие места в процессе старта.
Analyze and optimize mobile app startup time that includes:
— Measure cold/warm/hot start durations
— Identify main thread blockers during launch
— Optimize application class initialization
— Reduce dynamic feature module loading time
— Implement lazy loading for heavy components
— Optimize dependency injection setup
— Improve splash screen and initial UI rendering
— Добавьте
Implement asynchronous resource loading для параллельной загрузки ресурсов— Добавьте
Add startup tracing and performance monitoring для постоянного отслеживания— Добавьте
Optimize pre-dexing and multidex configuration для больших приложений#буст #MiddlePath
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
В SwiftUI alert — это способ показать критически важную информацию или запросить решение у пользователя.
Используй их только для действительно важных случаев: ошибок, подтверждений (особенно разрушительных действий), запросов разрешений.
Самый простой способ — через
isPresented (булево значение):@State var isPresented = false
VStack {
// UI
}
.alert("Alert Title", isPresented: $isPresented) {
Button("OK") { /* action */ }
Button("Cancel") { }
}
Алёрт появляется, когда
isPresented = true, и автоматически закрывается после действия пользователя.Можно добавить поясняющий текст:
.alert("Alert Title", isPresented: $isPresented) {
Button("OK") { }
} message: {
Text("Подробнее о проблеме")
}Это помогает пользователю понять контекст и принять осознанное решение.
📦 Работа с данными
Для подтверждения действий над конкретными объектами есть параметр
presenting:.alert("Delete Document", isPresented: $isPresented, presenting: document) { document in
Button("Delete", role: .destructive) { delete(document) }
Button("Cancel", role: .cancel) { }
} message: { document in
Text("Удалить '\(document.name)'? Это действие нельзя отменить.")
}Так SwiftUI сохраняет корректные данные на время отображения диалога.
Если твои ошибки реализуют
LocalizedError, SwiftUI умеет красиво показывать их в алёртах:.alert(isPresented: $isPresented, error: error) {
Button("Retry") { }
Button("Cancel") { }
} message: { error in
Text(error.recoverySuggestion ?? "Попробуй позже.")
}Используй краткие заголовки и ясные действия.
Помечай кнопки ролями:
.cancel, .destructive.Не перегружай интерфейс — если это не критично, лучше показать баннер или inline-сообщение.
alert, banner или sheet?#АрхитектурныйКод #MiddlePath #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Индикатор пульса — это простой, но эффективный элемент пользовательского интерфейса, который помогает визуализировать состояние подключения или активности. В отличие от индикатора загрузки, он передает идею о сигнале, исходящем из центральной точки, что особенно полезно для отображения состояния GPS или сетевого подключения.
Вот полная составная функция, которая отображает индикатор пульса:
@Composable
закрытый метод PulseIndicator(
@DrawableRes значок: Int,
модификатор: Modifier = Modifier
) {
val periodMs = 3600L
val offsetsMs = longArrayOf(0L, 1200L, 2400L)
val startNs = запомнить { System.nanoTime() }
var frameTimeNs by запомнить { mutableLongStateOf(startNs) }
LaunchedEffect(Единица измерения) {
while (true) {
withFrameNanos { now -> frameTimeNs = now }
}
}
(offsetMs: Long)фазафункция: число с плавающей запятой {
val elapsedMs = (frameTimeNs - startNs) / 1_000_000L + offsetMs
return ((elapsedMs % periodMs).toFloat() / periodMs.toFloat())
}
Box(modifier.size(80.dp), contentAlignment = Alignment.Center) {
(p:
Float )Кольцофункция@Composable = Box(
Модификатор
.matchParentSize()
.graphicsLayer {
scaleX = 1f + 0,8f * p
scaleY = 1f + 0,8f * p
alpha = 1f - p
}
.border(1,5.dp, Color.White.copy(alpha = 0,9f), CircleShape)
)
Кольцо(фаза(смещения[0]))
Кольцо(фаза(смещения[1]))
Кольцо(фаза(смещения[2]))
Box(
Modifier
.size(80.dp)
.background(Color.White, CircleShape),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(icon),
contentDescription = null,
modifier = Modifier.size(32.dp)
)
}
}
}
Компонуемый элемент использует
withFrameNanos внутри LaunchedEffect. Это позволяет получить доступ к текущей временной метке кадра и обеспечивает плавность анимации, пока компонуемый элемент находится на экране. Когда компонуемый элемент покидает композицию, сопрограмма автоматически отменяется.Функция
phase(offsetMs) преобразует прошедшее время в значение между 0f и 1f. Каждое кольцо смещено (0, 1200, 2400 мс), поэтому они расширяются в разные моменты. Это создаёт иллюзию непрерывных волн.Каждое кольцо изображается в виде
Box с круглой рамкой. Его размер и непрозрачность изменяются с помощью graphicsLayer:scaleX и scaleY постепенно увеличиваются от 1f до 1.8f.alpha плавно переходит от 1f к 0f.Вместе они образуют расширяющийся, затухающий круг.
В центре находится сплошной белый круг с указанным значком (например, с меткой местоположения). Он служит статичной точкой привязки, в то время как анимированные кольца расходятся в стороны.
#PixelPerfect #MiddlePath #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4😁4👾1
OptionSet — это протокол, который представляет параметры в виде битов (битовой маски). Он также предоставляет вашему типу операции, подобные операциям с множествами, такие как объединение и пересечение. OptionSet позволяет объединять несколько значений.Это отличное решение, если вам нужно предоставить какие-то параметры, настройки, разрешения, стили и т. д.
Давайте напишем код:
struct TasksListOptions: OptionSet {
let rawValue: Int
static let showFilter = TasksListOptions(rawValue: 1 << 0)
static let showSearch = TasksListOptions(rawValue: 1 << 1)
static let showSort = TasksListOptions(rawValue: 1 << 2)
static let showLayoutSelector = TasksListOptions(rawValue: 1 << 3)
}Каждый вариант
rawValue представляет собой один бит в одном и том же целом числе: 1 << 0 — это 0001, 1 << 1 — это 0010, 1 << 2 — это 0100 и так далее. При объединении вариантов Swift объединяет их биты с помощью оператора побитового ИЛИ (|), поэтому showFilter + showSort становится 0101. Таким образом, несколько вариантов объединяются в одно компактное число, в котором каждый бит чётко обозначает отдельный вариант.Как уже упоминалось, замечательной особенностью
OptionSet является возможность комбинировать параметры. Один из распространённых сценариев — объявление предустановок по умолчанию с комбинированными значениями. Например, можно создать all параметр для представления всех параметров. В нашем примере мы можем создать предустановки для разных типов списков:struct TasksListOptions: OptionSet {
...
static let today: TasksListOptions = [.showFilter, .showSearch]
static let allTasks: TasksListOptions = [.showFilter, .showSearch, .showSort, .showLayoutSelector]
}Теперь ваша модель представления может использовать один из этих шаблонов для включения или отключения некоторых функций.
@Observable
final class TasksListViewModel {
private let options: TasksListOptions
}
#буст #MiddlePath #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2