From a02fa53902231f7f5bcd62756ea3c60c7e7065b4 Mon Sep 17 00:00:00 2001 From: cheykrym Date: Thu, 14 Aug 2025 19:41:07 +0300 Subject: [PATCH] load from api --- Shared/Components/RemoteImageView.swift | 58 +++++++++++++++++++++++++ Shared/Views/Tab/NewHomeTab.swift | 21 ++++++--- yobble.xcodeproj/project.pbxproj | 4 ++ 3 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 Shared/Components/RemoteImageView.swift diff --git a/Shared/Components/RemoteImageView.swift b/Shared/Components/RemoteImageView.swift new file mode 100644 index 0000000..52892e0 --- /dev/null +++ b/Shared/Components/RemoteImageView.swift @@ -0,0 +1,58 @@ +import SwiftUI +import Combine + +// 1. Класс для загрузки изображения +class ImageLoader: ObservableObject { + @Published var image: UIImage? + private var cancellable: AnyCancellable? + private let url: URL + + init(url: URL) { + self.url = url + } + + deinit { + cancellable?.cancel() + } + + func load() { + cancellable = URLSession.shared.dataTaskPublisher(for: url) + .map { UIImage(data: $0.data) } + .replaceError(with: nil) + .receive(on: DispatchQueue.main) + .assign(to: \.image, on: self) + } + + func cancel() { + cancellable?.cancel() + } +} + +// 2. View для отображения удаленного изображения +struct RemoteImageView: View { + @StateObject private var loader: ImageLoader + private let placeholder: Image + + init(url: URL, placeholder: Image = Image("placeholderPhoto")) { + _loader = StateObject(wrappedValue: ImageLoader(url: url)) + self.placeholder = placeholder + } + + var body: some View { + content + .onAppear(perform: loader.load) + .onDisappear(perform: loader.cancel) + } + + private var content: some View { + Group { + if let image = loader.image { + Image(uiImage: image) + .resizable() + } else { + placeholder + .resizable() + } + } + } +} diff --git a/Shared/Views/Tab/NewHomeTab.swift b/Shared/Views/Tab/NewHomeTab.swift index 04f809d..2f07e2a 100644 --- a/Shared/Views/Tab/NewHomeTab.swift +++ b/Shared/Views/Tab/NewHomeTab.swift @@ -50,15 +50,24 @@ struct PostGridItem: View { let post: Post let width: CGFloat // Ширина элемента + // Формируем URL для загрузки изображения + private var imageURL: URL? { + // Используем picsum.photos для получения уникального изображения для каждого поста + URL(string: "https://picsum.photos/seed/\(post.id.uuidString)/400/400") + } + var body: some View { VStack(alignment: .leading, spacing: 0) { // Убираем отступ между картинкой и текстом // 1. Медиа контент - if let _ = post.media.first { - Image("placeholderPhoto") - .resizable() - .scaledToFill() // Заполняем кадр, сохраняя пропорции - .frame(width: width, height: width) // Устанавливаем жесткий размер - .clipped() // Обрезаем лишнее + if let url = imageURL { + // Создаем контейнер с четкими границами, чтобы избежать перекрытия + Color.clear + .frame(width: width, height: width) + .background( + RemoteImageView(url: url) + .scaledToFill() + ) + .clipped() } // Контейнер для текста, который создает эффект "расширения" diff --git a/yobble.xcodeproj/project.pbxproj b/yobble.xcodeproj/project.pbxproj index bcd5ad1..2e784e7 100644 --- a/yobble.xcodeproj/project.pbxproj +++ b/yobble.xcodeproj/project.pbxproj @@ -37,6 +37,7 @@ 1A9B016E2E4BFB9000887E0B /* NewHomeTabViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9B016D2E4BFB9000887E0B /* NewHomeTabViewModel.swift */; }; 1A9B017C2E4C087F00887E0B /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9B017B2E4C087F00887E0B /* SideMenuView.swift */; }; 1A9E4FB32E4D6A67002249D6 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9E4FB22E4D6A67002249D6 /* ThemeManager.swift */; }; + 1A9E4FD72E4E47EF002249D6 /* RemoteImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9E4FD62E4E47EF002249D6 /* RemoteImageView.swift */; }; 1AB4F8CD2E22E341002B6E40 /* AccountShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8CC2E22E341002B6E40 /* AccountShareSheet.swift */; }; 1AB4F8F32E22EC9F002B6E40 /* FollowersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8F22E22EC9F002B6E40 /* FollowersView.swift */; }; 1AB4F8F72E22ECAC002B6E40 /* FollowingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8F62E22ECAC002B6E40 /* FollowingView.swift */; }; @@ -92,6 +93,7 @@ 1A9B016D2E4BFB9000887E0B /* NewHomeTabViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewHomeTabViewModel.swift; sourceTree = ""; }; 1A9B017B2E4C087F00887E0B /* SideMenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = ""; }; 1A9E4FB22E4D6A67002249D6 /* ThemeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; }; + 1A9E4FD62E4E47EF002249D6 /* RemoteImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteImageView.swift; sourceTree = ""; }; 1AB4F8CC2E22E341002B6E40 /* AccountShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountShareSheet.swift; sourceTree = ""; }; 1AB4F8F22E22EC9F002B6E40 /* FollowersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersView.swift; sourceTree = ""; }; 1AB4F8F62E22ECAC002B6E40 /* FollowingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingView.swift; sourceTree = ""; }; @@ -257,6 +259,7 @@ 1AB7F5132E32EBF1003756F3 /* Components */ = { isa = PBXGroup; children = ( + 1A9E4FD62E4E47EF002249D6 /* RemoteImageView.swift */, 1AB7F5142E32EC1C003756F3 /* RefreshableScrollView.swift */, 1AB7F5152E32EC1C003756F3 /* TopBarView.swift */, ); @@ -429,6 +432,7 @@ 1AEE5EAB2E21A83200A3DCA3 /* HomeTab.swift in Sources */, 1ACE60F82E22F3DC00B37AC5 /* SettingsView.swift in Sources */, 1AD757CD2E27608C0069C1FD /* PostFeedView.swift in Sources */, + 1A9E4FD72E4E47EF002249D6 /* RemoteImageView.swift in Sources */, 1A9B017C2E4C087F00887E0B /* SideMenuView.swift in Sources */, 1A7940C62DF7A98E002569DA /* ContactsTab.swift in Sources */, 1ACE61092E22F57100B37AC5 /* AppPreferencesView.swift in Sources */,