fix post style
This commit is contained in:
parent
64f59c8e91
commit
874e920f6f
@ -37,6 +37,8 @@ enum MediaType: String, Codable {
|
|||||||
struct MediaItem: Identifiable, Codable {
|
struct MediaItem: Identifiable, Codable {
|
||||||
let id: UUID
|
let id: UUID
|
||||||
let type: MediaType
|
let type: MediaType
|
||||||
|
let width: Int?
|
||||||
|
let height: Int?
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AccessLevel: String, Codable {
|
enum AccessLevel: String, Codable {
|
||||||
|
|||||||
@ -13,102 +13,52 @@ class PostService {
|
|||||||
guard posts.isEmpty else { return }
|
guard posts.isEmpty else { return }
|
||||||
|
|
||||||
let sampleTitles = [
|
let sampleTitles = [
|
||||||
"Обзор TikTok UI",
|
"Обзор TikTok UI", "Мой первый ролик", "Котик в кадре", "SwiftUI мастер-класс",
|
||||||
"Мой первый ролик",
|
"Анимации и переходы", "Съёмка с дрона", "Урок по дизайну", "Как сделать свайпы",
|
||||||
"Котик в кадре",
|
"Лучший UI 2025", "Мой первый ролик", "Котик в кадре", "SwiftUI мастер-класс",
|
||||||
"SwiftUI мастер-класс",
|
"Анимации и переходы", "Съёмка с дрона", "Урок по дизайну", "Как сделать свайпы",
|
||||||
"Анимации и переходы",
|
"Лучший UI 2025", "Мой первый ролик", "Котик в кадре", "SwiftUI мастер-класс",
|
||||||
"Съёмка с дрона",
|
"Анимации и переходы", "Съёмка с дрона", "Урок по дизайну", "Как сделать свайпы",
|
||||||
"Урок по дизайну",
|
"Лучший UI 2025", "Мой первый ролик", "Котик в кадре", "SwiftUI мастер-класс",
|
||||||
"Как сделать свайпы",
|
"Анимации и переходы", "Съёмка с дрона", "Урок по дизайну", "Как сделать свайпы",
|
||||||
"Лучший UI 2025",
|
"Лучший UI 2025", "Мой первый ролик", "Котик в кадре", "SwiftUI мастер-класс",
|
||||||
"Мой первый ролик",
|
"Анимации и переходы", "Съёмка с дрона", "Урок по дизайну", "Как сделать свайпы",
|
||||||
"Котик в кадре",
|
"Лучший UI 2025", "Завершаем проект"
|
||||||
"SwiftUI мастер-класс",
|
|
||||||
"Анимации и переходы",
|
|
||||||
"Съёмка с дрона",
|
|
||||||
"Урок по дизайну",
|
|
||||||
"Как сделать свайпы",
|
|
||||||
"Лучший UI 2025",
|
|
||||||
"Мой первый ролик",
|
|
||||||
"Котик в кадре",
|
|
||||||
"SwiftUI мастер-класс",
|
|
||||||
"Анимации и переходы",
|
|
||||||
"Съёмка с дрона",
|
|
||||||
"Урок по дизайну",
|
|
||||||
"Как сделать свайпы",
|
|
||||||
"Лучший UI 2025",
|
|
||||||
"Мой первый ролик",
|
|
||||||
"Котик в кадре",
|
|
||||||
"SwiftUI мастер-класс",
|
|
||||||
"Анимации и переходы",
|
|
||||||
"Съёмка с дрона",
|
|
||||||
"Урок по дизайну",
|
|
||||||
"Как сделать свайпы",
|
|
||||||
"Лучший UI 2025",
|
|
||||||
"Мой первый ролик",
|
|
||||||
"Котик в кадре",
|
|
||||||
"SwiftUI мастер-класс",
|
|
||||||
"Анимации и переходы",
|
|
||||||
"Съёмка с дрона",
|
|
||||||
"Урок по дизайну",
|
|
||||||
"Как сделать свайпы",
|
|
||||||
"Лучший UI 2025",
|
|
||||||
"Завершаем проект"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
let sampleDescriptions = [
|
let sampleDescriptions = [
|
||||||
"Первый тестовый пост с видео",
|
"Первый тестовый пост с видео", "Фейковый контент для ленты", "Видео с котиком 🐱",
|
||||||
"Фейковый контент для ленты",
|
"Интерфейс в стиле TikTok", "Просто тестирую отображение", "Код и UI — любовь",
|
||||||
"Видео с котиком 🐱",
|
"Видео в реальном времени", "Интересный UX пример", "Анимации, переходы, свайпы",
|
||||||
"Интерфейс в стиле TikTok",
|
"Фейковый контент для ленты", "Видео с котиком 🐱", "Интерфейс в стиле TikTok",
|
||||||
"Просто тестирую отображение",
|
"Просто тестирую отображение", "Код и UI — любовь", "Видео в реальном времени",
|
||||||
"Код и UI — любовь",
|
"Интересный UX пример", "Анимации, переходы, свайпы", "Фейковый контент для ленты",
|
||||||
"Видео в реальном времени",
|
"Видео с котиком 🐱", "Интерфейс в стиле TikTok", "Просто тестирую отображение",
|
||||||
"Интересный UX пример",
|
"Код и UI — любовь", "Видео в реальном времени", "Интересный UX пример",
|
||||||
"Анимации, переходы, свайпы",
|
"Анимации, переходы, свайпы", "Фейковый контент для ленты", "Видео с котиком 🐱",
|
||||||
"Фейковый контент для ленты",
|
"Интерфейс в стиле TikTok", "Просто тестирую отображение", "Код и UI — любовь",
|
||||||
"Видео с котиком 🐱",
|
"Видео в реальном времени", "Интересный UX пример", "Анимации, переходы, свайпы",
|
||||||
"Интерфейс в стиле TikTok",
|
|
||||||
"Просто тестирую отображение",
|
|
||||||
"Код и UI — любовь",
|
|
||||||
"Видео в реальном времени",
|
|
||||||
"Интересный UX пример",
|
|
||||||
"Анимации, переходы, свайпы",
|
|
||||||
"Фейковый контент для ленты",
|
|
||||||
"Видео с котиком 🐱",
|
|
||||||
"Интерфейс в стиле TikTok",
|
|
||||||
"Просто тестирую отображение",
|
|
||||||
"Код и UI — любовь",
|
|
||||||
"Видео в реальном времени",
|
|
||||||
"Интересный UX пример",
|
|
||||||
"Анимации, переходы, свайпы",
|
|
||||||
"Фейковый контент для ленты",
|
|
||||||
"Видео с котиком 🐱",
|
|
||||||
"Интерфейс в стиле TikTok",
|
|
||||||
"Просто тестирую отображение",
|
|
||||||
"Код и UI — любовь",
|
|
||||||
"Видео в реальном времени",
|
|
||||||
"Интересный UX пример",
|
|
||||||
"Анимации, переходы, свайпы",
|
|
||||||
"Вот это да, пост работает!"
|
"Вот это да, пост работает!"
|
||||||
]
|
]
|
||||||
|
|
||||||
for i in 0..<32 {
|
for i in 0..<32 {
|
||||||
let mediaID = UUID()
|
let mediaID = UUID()
|
||||||
// let thumbID = Bool.random() ? UUID() : nil
|
|
||||||
let thumbID = UUID()
|
let thumbID = UUID()
|
||||||
let postID = UUID()
|
let postID = UUID()
|
||||||
|
|
||||||
let authorId = UUID()
|
let authorId = UUID()
|
||||||
let authorUserName = "username_\(Int.random(in: 1...5))"
|
let authorUserName = "username_\(Int.random(in: 1...5))"
|
||||||
// let authorId = "user_\(Int.random(in: 1...5))"
|
|
||||||
|
// Генерируем случайные размеры для изображения
|
||||||
|
let width = 1080 // Стандартная ширина
|
||||||
|
let randomRatio = Double.random(in: 1.0...1.77) // от 1:1 до ~9:16
|
||||||
|
let height = Int(Double(width) * randomRatio)
|
||||||
|
|
||||||
let post = Post(
|
let post = Post(
|
||||||
id: postID,
|
id: postID,
|
||||||
title: sampleTitles[i],
|
title: sampleTitles[i],
|
||||||
description: sampleDescriptions[i],
|
description: sampleDescriptions[i],
|
||||||
media: [
|
media: [
|
||||||
MediaItem(id: mediaID, type: .video)
|
MediaItem(id: mediaID, type: .video, width: width, height: height)
|
||||||
],
|
],
|
||||||
mediaOrder: [mediaID],
|
mediaOrder: [mediaID],
|
||||||
thumbnailID: thumbID,
|
thumbnailID: thumbID,
|
||||||
|
|||||||
@ -1,32 +1,86 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
import UIKit // Для доступа к UIScreen
|
||||||
|
|
||||||
class NewHomeTabViewModel: ObservableObject {
|
class NewHomeTabViewModel: ObservableObject {
|
||||||
@Published var posts: [Post] = []
|
@Published var column1Posts: [Post] = []
|
||||||
@Published var isLoading = true
|
@Published var column2Posts: [Post] = []
|
||||||
@Published var isRefreshing = false
|
@Published var isLoading: Bool = false
|
||||||
|
@Published var isRefreshing: Bool = false
|
||||||
|
|
||||||
private var hasLoadedData = false
|
private var allPosts: [Post] = []
|
||||||
|
private let postService = PostService.shared
|
||||||
|
|
||||||
|
// Рассчитываем ширину колонки один раз
|
||||||
|
private let columnWidth = (UIScreen.main.bounds.width - 14) / 2
|
||||||
|
|
||||||
func fetchDataIfNeeded() {
|
func fetchDataIfNeeded() {
|
||||||
guard !hasLoadedData else { return }
|
guard allPosts.isEmpty else { return }
|
||||||
refreshData()
|
fetchData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshData() {
|
func refreshData() {
|
||||||
DispatchQueue.main.async {
|
isRefreshing = true
|
||||||
self.isRefreshing = true
|
fetchData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func fetchData() {
|
||||||
|
if !isRefreshing {
|
||||||
|
isLoading = true
|
||||||
}
|
}
|
||||||
|
|
||||||
PostService.shared.fetchAllPosts { [weak self] fetchedPosts in
|
postService.fetchAllPosts { [weak self] posts in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.posts = fetchedPosts
|
self.allPosts = posts
|
||||||
|
self.distributePosts()
|
||||||
self.isLoading = false
|
self.isLoading = false
|
||||||
self.hasLoadedData = true
|
|
||||||
self.isRefreshing = false
|
self.isRefreshing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func distributePosts() {
|
||||||
|
var tempColumn1Posts: [Post] = []
|
||||||
|
var tempColumn2Posts: [Post] = []
|
||||||
|
var column1Height: CGFloat = 0
|
||||||
|
var column2Height: CGFloat = 0
|
||||||
|
|
||||||
|
for post in allPosts {
|
||||||
|
let postHeight = calculatePostHeight(for: post)
|
||||||
|
|
||||||
|
// Добавляем пост в более короткую колонку
|
||||||
|
if column1Height <= column2Height {
|
||||||
|
tempColumn1Posts.append(post)
|
||||||
|
column1Height += postHeight
|
||||||
|
} else {
|
||||||
|
tempColumn2Posts.append(post)
|
||||||
|
column2Height += postHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.column1Posts = tempColumn1Posts
|
||||||
|
self.column2Posts = tempColumn2Posts
|
||||||
|
}
|
||||||
|
|
||||||
|
private func calculatePostHeight(for post: Post) -> CGFloat {
|
||||||
|
guard let media = post.media.first,
|
||||||
|
let width = media.width,
|
||||||
|
let height = media.height,
|
||||||
|
width > 0 else {
|
||||||
|
// Возвращаем стандартную высоту для постов без медиа или с неверными данными
|
||||||
|
return columnWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рассчитываем высоту изображения на основе его пропорций
|
||||||
|
let aspectRatio = CGFloat(height) / CGFloat(width)
|
||||||
|
let imageHeight = columnWidth * aspectRatio
|
||||||
|
|
||||||
|
// Здесь можно добавить примерную высоту для текста, если нужно
|
||||||
|
// Например, + 50-70 поинтов для заголовка, автора и кнопок
|
||||||
|
// Для простоты пока будем ориентироваться только на высоту картинки
|
||||||
|
return imageHeight
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,16 +3,7 @@ import SwiftUI
|
|||||||
struct NewHomeTab: View {
|
struct NewHomeTab: View {
|
||||||
@ObservedObject var viewModel: NewHomeTabViewModel
|
@ObservedObject var viewModel: NewHomeTabViewModel
|
||||||
|
|
||||||
private var column1Posts: [Post] {
|
// Ширина колонки теперь вычисляется в ViewModel, но нам она нужна и здесь для PostGridItem
|
||||||
viewModel.posts.enumerated().filter { $0.offset % 2 == 0 }.map { $0.element }
|
|
||||||
}
|
|
||||||
|
|
||||||
private var column2Posts: [Post] {
|
|
||||||
viewModel.posts.enumerated().filter { $0.offset % 2 != 0 }.map { $0.element }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Рассчитываем ширину колонки на основе ширины экрана.
|
|
||||||
// Отступы: 4 слева + 4 справа + 6 между колонками = 14.
|
|
||||||
private let columnWidth = (UIScreen.main.bounds.width - 14) / 2
|
private let columnWidth = (UIScreen.main.bounds.width - 14) / 2
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -25,12 +16,12 @@ struct NewHomeTab: View {
|
|||||||
}) {
|
}) {
|
||||||
HStack(alignment: .top, spacing: 6) {
|
HStack(alignment: .top, spacing: 6) {
|
||||||
LazyVStack(spacing: 6) {
|
LazyVStack(spacing: 6) {
|
||||||
ForEach(column1Posts) { post in
|
ForEach(viewModel.column1Posts) { post in
|
||||||
PostGridItem(post: post, width: columnWidth)
|
PostGridItem(post: post, width: columnWidth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LazyVStack(spacing: 6) {
|
LazyVStack(spacing: 6) {
|
||||||
ForEach(column2Posts) { post in
|
ForEach(viewModel.column2Posts) { post in
|
||||||
PostGridItem(post: post, width: columnWidth)
|
PostGridItem(post: post, width: columnWidth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +33,7 @@ struct NewHomeTab: View {
|
|||||||
.onAppear {
|
.onAppear {
|
||||||
viewModel.fetchDataIfNeeded()
|
viewModel.fetchDataIfNeeded()
|
||||||
}
|
}
|
||||||
.background(Color(.secondarySystemBackground)) // Фон для всей вкладки
|
// .background(Color(.secondarySystemBackground)) // Фон для всей вкладки
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,16 +44,34 @@ struct PostGridItem: View {
|
|||||||
// Формируем URL для загрузки изображения
|
// Формируем URL для загрузки изображения
|
||||||
private var imageURL: URL? {
|
private var imageURL: URL? {
|
||||||
// Используем picsum.photos для получения уникального изображения для каждого поста
|
// Используем picsum.photos для получения уникального изображения для каждого поста
|
||||||
URL(string: "https://picsum.photos/seed/\(post.id.uuidString)/400/400")
|
// Используем реальные размеры для большей вариативности
|
||||||
|
if let media = post.media.first, let w = media.width, let h = media.height {
|
||||||
|
return URL(string: "https://picsum.photos/seed/\(post.id.uuidString)/\(w)/\(h)")
|
||||||
|
}
|
||||||
|
return URL(string: "https://picsum.photos/seed/\(post.id.uuidString)/400/400")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вычисляем высоту изображения на основе данных из модели
|
||||||
|
private var imageHeight: CGFloat {
|
||||||
|
guard let media = post.media.first,
|
||||||
|
let mediaWidth = media.width,
|
||||||
|
let mediaHeight = media.height,
|
||||||
|
mediaWidth > 0 else {
|
||||||
|
return width // Возвращаем 1:1, если данных нет
|
||||||
|
}
|
||||||
|
|
||||||
|
let aspectRatio = CGFloat(mediaHeight) / CGFloat(mediaWidth)
|
||||||
|
return width * aspectRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) { // Убираем отступ между картинкой и текстом
|
VStack(alignment: .leading, spacing: 0) { // Убираем отступ между картинкой и текстом
|
||||||
|
|
||||||
// 1. Медиа контент
|
// 1. Медиа контент
|
||||||
if let url = imageURL {
|
if let url = imageURL {
|
||||||
// Создаем контейнер с четкими границами, чтобы избежать перекрытия
|
// Создаем контейнер с четкими границами, чтобы избежать перекрытия
|
||||||
Color.clear
|
Color.clear
|
||||||
.frame(width: width, height: width)
|
.frame(width: width, height: imageHeight) // Используем вычисленную высоту
|
||||||
.background(
|
.background(
|
||||||
RemoteImageView(url: url)
|
RemoteImageView(url: url)
|
||||||
.scaledToFill()
|
.scaledToFill()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user