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