Alexander Kraev
1.08K subscribers
87 photos
38 videos
1 file
76 links
Tech, Mobile (like SwiftUI, Compose), Code, Thoughts, feel free to reach me: @lexkraev

По вопросам рекламы: @lexkraev
Download Telegram
Media is too big
VIEW IN TELEGRAM
📹 Переходы между видео в стиле Рилс

📹 Find out how to repeat Reels

Thank you for subscribing and recommending channel… I really appreciate all of you 🫶

P.S. All these videos were captured by me in different subjects of Russia 🇷🇺

#tasty

@swiftui_dev
8👍5🔥5
Media is too big
VIEW IN TELEGRAM
🔥 Весьма удобный и крутой лайфхак, как можно быстро помочь себе в верстке

🧨 Really nice and awesome lifehack how to help yourself with layouts

Думаю, уже все знают про то, как найти размер вьюхи с помощью GeomertyReader, если .frame не задан. Если нет, то я напомню:

Hope everyone already knows how to find the view size using GeomertyReader if .frame is not specified. Don’t worry, I'll remind you:


import SwiftUI

struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}

struct MeasureSizeModifier: ViewModifier {
func body(content: Content) -> some View {
content.overlay(GeometryReader { geometry in
Color.clear.preference(key: SizePreferenceKey.self,
value: geometry.size)
})
}
}

extension View {
func measureSize(perform action: @escaping (CGSize) -> Void) -> some View {
self.modifier(MeasureSizeModifier.init())
.onPreferenceChange(SizePreferenceKey.self, perform: action)
}
}


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

Next let's create a struct and modifier for displaying the view size:


import SwiftUI

struct Measurements: View {

@State private var size: CGSize = .zero

let showSize: Bool
let color: Color

var body: some View {
label.measureSize { size = $0 }
}

var label: some View {
ZStack(alignment: .topTrailing) {
Rectangle()
.strokeBorder(
color,
lineWidth: 1
)

Text("H:\(size.height.formatted) W:\(size.width.formatted)")
.foregroundColor(.black)
.font(.system(size: 8))
.opacity(showSize ? 1 : 0)
}
}
}

extension View {
func measured(_ showSize: Bool = true, _ color: Color = Color.red) -> some View {
self
.overlay(Measurements(showSize: showSize, color: color))
}
}

extension CGFloat {

var formatted: String {
abs(self.remainder(dividingBy: 1)) <= 0.001
? .init(format: "%.0f", self)
: .init(format: "%.2f", self)
}
}

extension Double {
var formatted: String {
CGFloat(self).formatted
}
}


А теперь можете сравнить итоговые вьюхи с макетами и проверить на случайные .padding():

That’s all! Now you can compare views in Canvas with the designed in Figma and check on accidental .padding():


YourView()
.measure()


#howto #getsources #groovy

@swiftui_dev
👍18🔥52❤‍🔥2
Media is too big
VIEW IN TELEGRAM
📅 Календарь бронирования. Возьмем за основу календарь из Avito 👨‍💻

Компонент DatePicker в SwiftUI не позволяет пользователям выбирать диапазон дат. В свою очередь, MultiDatePicker поддерживает эту функцию, но этот контрол доступен только с iOS 16. Создадим свой для iOS 14. Основной дизайн сделаем по образцу календаря в приложении Avito. В нем есть несколько интересных вещей, таких как выбор диапазона дат, недоступные дни или обязательные к бронирования дни.

За кодом сюда

📆Booking calendar. Avito inspired 👨‍💻

SwiftUI’s DatePicker doesn’t allow users to pick a date range. On the other hand, MultiDatePicker allows this, but it is available only from iOS 16. Let’s create our own for iOS 14. The basic design is inspired by the booking calendar from the Avito app. It contains several interesting features, such as date range selection, unavailable days, or mandatory booking days.

Code is here

#tasty #groovy #trytodo

@swiftui_dev
👍7🔥6
Media is too big
VIEW IN TELEGRAM
📱 ↕️ Bottom sheet – это неотъемлемый элемент современных мобильных приложений, который получил широкое распространение благодаря своей универсальности и удобству. Они позволяют эффективно организовать взаимодействие пользователя с интерфейсом, предоставляя доступ к дополнительной информации или действиям, не покидая текущего экрана. Bottom sheet отлично подходят для контекстных меню, форм и быстрых настроек, оставаясь при этом интуитивно понятными благодаря своей привычной для пользователей механике. Их способность адаптироваться к разным сценариям делает их полезными для создания удобных и красивых приложений. Таким образом, это не только модный элемент, а важный инструмент, которая помогает улучшить навигацию, повысить удобство использования приложения для его пользователями.

Но, как это обычно бывает, в SwiftUI bottom sheet появились только в iOS 16, а многие полезные модификаторы для их настройки и вовсе позже - с iOS 16.4. Это создает определенные сложности на проектах, где необходимо поддерживать более ранние версии iOS. В таких случаях, предлагаю вам использовать мой package. Это самостоятельное решение представляет собой реализацию, вдохновленную подходом бэкпорта.

Основная идея состоит в следующем:
• Если на устройстве пользователя установлена iOS версии 16.4 или новее, будет использована нативная реализация bottom sheet.
• Если версия iOS ниже 16.4, то автоматически активируется кастомная реализация.

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

Краткий мануал здесь.

📋 🔽 Bottom sheets have become a standard in modern mobile apps, thanks to their killer versatility and seamless UX. They let you keep the user on the same screen while offering up extra info or actions on demand. Bottom sheets are well-suited for context menus, forms, and quick settings, remaining intuitive due to the familiar interaction pattern they employ. Their adaptability to different scenarios makes them highly useful for creating aesthetically pleasing and user-friendly applications. Thus, bottom sheets aren’t just a trendy widget – they’re a core tool that boosts navigation flow and levels up overall app usability.

However, as is often the case, SwiftUI introduced native bottom sheets only in iOS 16, and many useful modifiers for their configuration were added even later, starting in iOS 16.4. That’s a headache if your project has to support older iOS versions. Enter my swift package: it’s a custom solution inspired by the whole backport approach.

The core idea is as follows:
• If the user's device is running iOS 16.4 or later, the native bottom sheet implementation will be used.
• If the iOS version is earlier than 16.4, the custom implementation will automatically be activated.

You can disable this behavior if desired, opting to use only the custom implementation.

Quick guide is here.

#swiftpm #tasty #groovy #getsources

@swiftui_dev
👍7🔥53
Media is too big
VIEW IN TELEGRAM
🏷️ Ещё некоторое время назад разработка новых фичей на Android требовала бОльших затрат времени, нежели на iOS. Все изменилось с приходом Jetpack Compose, где множество компонентов доступно «из коробки» c высокой степенью кастомизации.

Одним из таких компонентов Compose стали FlowRow и FlowColumn — лэйауты, которые позволяют автоматически переносить элементы на следующую строку или колонку при нехватке пространства. Это особенно полезно при отображении тегов, фильтров, коллекций с динамическим числом элементов и других интерфейсных паттернов.

А что в SwiftUI? С выходом iOS 16 Apple представила механизм кастомных Layout'ов, который существенно расширил возможности по созданию сложных лэйаутов. Однако проекты, ориентированные на iOS 13, по-прежнему нуждаются в решениях, обеспечивающих аналогичный функционал.

Моя библиотека Flow закрывает этот пробел, предоставляя разработчикам мощный и гибкий инструмент для построения адаптивных интерфейсов в SwiftUI на iOS 13 и выше. Компоненты поддерживает передачу произвольного содержимого в замыкании через ViewBuilder. Это означает, что внутрь лэйаута можно передать абсолютно любые view, включая Text, Button, Label, кастомные компоненты, а также сложные композиции вью, комбинирующие различные элементы.

Если вы ищете решение, аналогичное FlowRow в Compose, библиотека Flow станет отличным выбором для вашего проекта.

Краткий мануал здесь

🏷️ Not so long ago, developing new features on Android used to take more time compared to iOS. Everything changed with the arrival of Jetpack Compose, which provides many UI components out of the box along with a high degree of customization.

Such component in Compose are FlowRow and FlowColumn — layouts that automatically wrap elements to the next row or column when there isn’t enough space. This is especially useful for displaying tags, filters, collections with a dynamic number of items, and other interface patterns.

But what about SwiftUI? With the release of iOS 16, Apple introduced the custom Layout API, which significantly expanded the capabilities for building advanced layouts. However, many projects targeting iOS 13 and above still require alternative solutions to achieve similar functionality.

My package Flow fills this gap by providing developers with a powerful and flexible tool for building adaptive layouts in SwiftUI on iOS 13+. The component supports passing arbitrary content into the closure via ViewBuilder. This means you can embed any SwiftUI views inside the layout — including Text, Button, Label, custom components, or even complex view compositions combining multiple elements.

If you're looking for a solution similar to FlowRow in Compose, the Flow package is a great choice for your SwiftUI project.

Quick guide is here

#swiftpm #tasty #groovy #getsources

@swiftui_dev
1👍4🔥4
Я давно сюда не писал, и если вы всё это время оставались — спасибо вам огромное. Я правда очень это ценю 🫶

В последнее время я старался делиться здесь open-source решениями — тем, что делает жизнь разработчиков чуть проще. Как правило, это были решения реальных проблем, с которыми я сталкивался в работе, чтобы другим не приходилось тратить на них своё время. Иногда — просто небольшие челленджи для себя 🙂

Но регулярно писать только про такие вещи непросто, а делиться хочется чаще.

Поэтому дальше здесь будет немного больше и других постов, связанных с разработкой 👨‍💻

Спасибо, что вы здесь ❤️

I haven’t posted here for a while, and if you’ve stayed all this time — thank you so much. I really appreciate it 🫶

Lately, I’ve been sharing open-source solutions here — things that make developers’ lives a bit easier. Most of them came from real problems I ran into at work, so others wouldn’t have to spend time solving the same things. Sometimes it was just small personal challenges 🙂

But writing only about things like that on a regular basis isn’t easy, and I want to share more often.

So there will be a bit more posts here related to development as well 👨‍💻

Thanks for being here ❤️
815👍1👎1
Важность дизайн-системы для фронтенда (как для web, так и для мобаила) сложно переоценить.

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

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

Первая — у дизайн-системы часто нет конкретного владельца. Нет человека, который за неё отвечает целиком: принимает решения, держит целостность и понимает, куда она должна развиваться.

Вторая — нет единственного code owner’а. Когда компоненты пишут все понемногу, система быстро превращается в набор разрозненных решений с разной логикой и качеством. Имо дизайн-систему вообще не стоит делать коллективно. По опыту, гораздо лучше, когда её ведут несколько разработчиков (а то и вовсе один, но опытный), которые последовательно и осознанно выстраивают систему, а команда уже использует её и даёт обратную связь.

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

Приведу пример последнего на SwiftUI:

Вот нативный Toggle:



Toggle(isOn: $isEnabled) {
Text("Notifications")
}



а вот toggle из одного реального проекта:



DSToggle(
title: "Notifications",
value: isEnabled,
style: .switch,
enabled: true,
onValueChanged: { newValue in
isEnabled = newValue
}
)



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

Пример действительно классной дизайн-системы (поверьте, я их видел крайне много):

https://orbit.kiwi/components

Реализация на SwiftUI, Compose, React.

The importance of a design system for frontend development — both web and mobile — is hard to overestimate.
There are tons of articles about this 🙂 Everyone talks about its benefits: brand recognition, visual consistency, and development speed.

But in practice, truly well-designed design systems are very rare. And the problems are usually the same.

First, a design system often has no clear owner.
There’s no single person responsible for it end-to-end: making decisions, maintaining consistency, and understanding where the system should evolve.

Second, there is no single code owner.
When everyone contributes a little, the system quickly turns into a collection of fragmented solutions with different logic and quality levels. IMO, a design system shouldn’t really be built collectively. From my experience, it works much better when it’s driven by one or two experienced developers (or even just one), who build it thoughtfully and consistently, while the rest of the team uses it and provides feedback.

From a technical perspective, the biggest issue is that components are often not implemented in a native way.

A simple example in SwiftUI.

A native Toggle:



Toggle(isOn: $isEnabled) {
Text("Notifications")
}


And here’s a Toggle from a real project:


swift

DSToggle(
title: "Notifications",
value: isEnabled,
style: .switch,
enabled: true,
onValueChanged: { newValue in
isEnabled = newValue
}
)



Any design system should strive to preserve native naming, the number and order of parameters, and feel like a natural extension of the framework rather than a separate DSL.

As an example of a truly great design system (trust me, I’ve seen a lot of them):
https://orbit.kiwi/components

Implemented in SwiftUI, Compose, React.

#readthis@swiftui_dev #getsources@swiftui_dev
👍51👎1🤨1