Как изменить размер изображения (Image) в SwiftUI: масштабирование и кадрирование
Всем привет! Я только начал изучать SwiftUI и столкнулся с проблемой: никак не могу нормально изменить размер изображения. В UIKit я привык использовать imageView.contentMode и imageView.frame, а в SwiftUI всё иначе.
У меня есть изображение, которое я загружаю из Assets:
struct ContentView: View {
var body: some View {
Image("myPhoto")
}
}
Проблема в том, что картинка отображается в оригинальном размере (4000x3000 пикселей) и вылезает за границы экрана. Я пробовал использовать .frame(width: 300, height: 200), но изображение просто обрезается, а не масштабируется. Пробовал разные модификаторы, но ничего не работает как надо.
Подскажите, пожалуйста, как правильно изменить размер изображения в SwiftUI? Мне нужно:
- Просто уменьшить картинку до определённого размера (например, 300x200) с сохранением пропорций.
- Заполнить контейнер изображением, обрезав лишнее по краям (как AspectFill).
- Уместить изображение целиком внутри контейнера (как AspectFit).
Буду благодарен за примеры кода и объяснение, как работают модификаторы .resizable(), .aspectRatio(), .scaledToFit() и .scaledToFill(). В документации Apple написано сложно, а на практике ничего не получается.
Заранее спасибо!
<<Основные модификаторы для изменения размера Image в SwiftUI
В SwiftUI управление размером изображения строится на трёх ключевых модификаторах: .resizable(), .aspectRatio() / .scaledToFit() / .scaledToFill() и .frame(). Разберём каждый на примерах.
1. .resizable() — обязательный первый шаг
Без этого модификатора Image всегда рисуется в оригинальном размере. .resizable() позволяет изображению растягиваться или сжиматься под заданный контейнер:
Image("myPhoto")
.resizable() // теперь изображение можно масштабировать
.frame(width: 300, height: 200)
⚠️ Если просто добавить .frame(), изображение может исказиться, так как по умолчанию SwiftUI растягивает его без сохранения пропорций.
2. Сохранение пропорций: .aspectRatio(), .scaledToFit(), .scaledToFill()
Эти модификаторы решают проблему искажения:
.scaledToFit()— вписывает изображение целиком в заданный прямоугольник, сохраняя пропорции. Свободное пространство заполняется фоном..scaledToFill()— заполняет весь прямоугольник, обрезая лишнее по краям (как AspectFill в UIKit)..aspectRatio(contentMode: .fit)— то же, что.scaledToFit()..aspectRatio(contentMode: .fill)— то же, что.scaledToFill().
Примеры:
// Вариант 1: Уместить целиком (AspectFit)
Image("myPhoto")
.resizable()
.scaledToFit()
.frame(width: 300, height: 200)
.background(Color.gray) // чтобы видеть свободное пространство
// Вариант 2: Заполнить контейнер с обрезкой (AspectFill)
Image("myPhoto")
.resizable()
.scaledToFill()
.frame(width: 300, height: 200)
.clipped() // обрезает выступающие части
Обратите внимание на .clipped() — без него изображение при .scaledToFill() может вылезать за границы .frame().
3. Точное управление соотношением сторон
Иногда нужно задать конкретное соотношение, например 16:9, независимо от размера контейнера:
Image("myPhoto")
.resizable()
.aspectRatio(16/9, contentMode: .fit)
.frame(width: 300)
В этом случае высота будет рассчитана автоматически как 300 * 9 / 16 = 168.75.
4. Работа с GeometryReader для адаптивного размера
Если размер должен зависеть от экрана или родительского контейнера, используйте GeometryReader:
GeometryReader { geo in
Image("myPhoto")
.resizable()
.scaledToFit()
.frame(width: geo.size.width * 0.8) // 80% от ширины контейнера
}
.frame(height: 200)
5. Продвинутый пример: округлённое изображение с обрезкой
Image("avatar")
.resizable()
.scaledToFill()
.frame(width: 100, height: 100)
.clipShape(Circle()) // обрезает по кругу
.shadow(radius: 5)
Итоговые рекомендации
- Всегда начинайте с
.resizable(). - Для простых случаев используйте
.scaledToFit()или.scaledToFill(). - Не забывайте про
.clipped()при.scaledToFill(). - Если нужно точное соотношение сторон —
.aspectRatio(_:contentMode:). - Для динамических размеров —
GeometryReader.
Теперь ваше изображение будет выглядеть аккуратно на любом экране!
<<Альтернативный подход: модификатор .frame() с параметром alignment
Иногда нужно не просто масштабировать, а ещё и выровнять изображение внутри контейнера. Например, показать верхнюю часть фотографии или центрировать её. Для этого комбинируйте .frame() с alignment:
Image("myPhoto")
.resizable()
.scaledToFill()
.frame(width: 300, height: 200, alignment: .top) // прижать к верху
.clipped()
Доступные значения: .center, .top, .bottom, .leading, .trailing и их комбинации (.topLeading и т.д.).
Работа с SF Symbols
Для системных иконок (SF Symbols) размер задаётся по-другому — через .font() или .imageScale():
Image(systemName: "star.fill")
.font(.system(size: 40)) // размер иконки
.foregroundColor(.yellow)
// Или через imageScale:
Image(systemName: "heart")
.imageScale(.large) // .small, .medium, .large
Использование AsyncImage для загрузки из сети
Если изображение загружается из интернета, размер тоже настраивается стандартно:
AsyncImage(url: URL(string: "https://example.com/photo.jpg")) { phase in
if let image = phase.image {
image
.resizable()
.scaledToFit()
.frame(width: 300)
} else if phase.error != nil {
Color.red
} else {
ProgressView()
}
}
Главное — не забыть применить .resizable() к полученному image внутри замыкания.
Этот подход особенно удобен, когда нужно гибко управлять положением изображения внутри контейнера или работать с системными иконками.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
WebSocket на JavaScript: создаем real-time приложения на Node.js