refresh home page
This commit is contained in:
		
							parent
							
								
									bb8c9a2b91
								
							
						
					
					
						commit
						8fd955d3cb
					
				
							
								
								
									
										71
									
								
								Shared/Components/RefreshableScrollView.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								Shared/Components/RefreshableScrollView.swift
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					import SwiftUI
 | 
				
			||||||
 | 
					import UIKit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct RefreshableScrollView<Content: View>: UIViewRepresentable {
 | 
				
			||||||
 | 
					    var content: Content
 | 
				
			||||||
 | 
					    var onRefresh: (UIRefreshControl) -> Void
 | 
				
			||||||
 | 
					    var isRefreshing: Binding<Bool>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init(isRefreshing: Binding<Bool>, onRefresh: @escaping (UIRefreshControl) -> Void, @ViewBuilder content: () -> Content) {
 | 
				
			||||||
 | 
					        self.content = content()
 | 
				
			||||||
 | 
					        self.onRefresh = onRefresh
 | 
				
			||||||
 | 
					        self.isRefreshing = isRefreshing
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func makeUIView(context: Context) -> UIScrollView {
 | 
				
			||||||
 | 
					        let scrollView = UIScrollView()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Создаем UIRefreshControl и добавляем его
 | 
				
			||||||
 | 
					        let refreshControl = UIRefreshControl()
 | 
				
			||||||
 | 
					        refreshControl.addTarget(context.coordinator, action: #selector(Coordinator.handleRefresh), for: .valueChanged)
 | 
				
			||||||
 | 
					        scrollView.refreshControl = refreshControl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Создаем хостинг для нашего SwiftUI контента
 | 
				
			||||||
 | 
					        let hostingController = UIHostingController(rootView: content)
 | 
				
			||||||
 | 
					        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
 | 
				
			||||||
 | 
					        scrollView.addSubview(hostingController.view)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Настраиваем Auto Layout
 | 
				
			||||||
 | 
					        NSLayoutConstraint.activate([
 | 
				
			||||||
 | 
					            hostingController.view.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
 | 
				
			||||||
 | 
					            hostingController.view.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
 | 
				
			||||||
 | 
					            hostingController.view.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
 | 
				
			||||||
 | 
					            hostingController.view.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
 | 
				
			||||||
 | 
					            hostingController.view.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor)
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        context.coordinator.hostingController = hostingController
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return scrollView
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func updateUIView(_ uiView: UIScrollView, context: Context) {
 | 
				
			||||||
 | 
					        // Обновляем состояние индикатора
 | 
				
			||||||
 | 
					        if isRefreshing.wrappedValue {
 | 
				
			||||||
 | 
					            uiView.refreshControl?.beginRefreshing()
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            uiView.refreshControl?.endRefreshing()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Обновляем SwiftUI View, если нужно
 | 
				
			||||||
 | 
					        context.coordinator.hostingController?.rootView = content
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func makeCoordinator() -> Coordinator {
 | 
				
			||||||
 | 
					        Coordinator(self)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Coordinator: NSObject {
 | 
				
			||||||
 | 
					        var parent: RefreshableScrollView
 | 
				
			||||||
 | 
					        var hostingController: UIHostingController<Content>?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        init(_ parent: RefreshableScrollView) {
 | 
				
			||||||
 | 
					            self.parent = parent
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @objc func handleRefresh(_ sender: UIRefreshControl) {
 | 
				
			||||||
 | 
					            parent.isRefreshing.wrappedValue = true
 | 
				
			||||||
 | 
					            parent.onRefresh(sender)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -3,6 +3,7 @@ import SwiftUI
 | 
				
			|||||||
struct HomeTab: View {
 | 
					struct HomeTab: View {
 | 
				
			||||||
    @State private var posts: [Post] = []
 | 
					    @State private var posts: [Post] = []
 | 
				
			||||||
    @State private var isLoading = true
 | 
					    @State private var isLoading = true
 | 
				
			||||||
 | 
					    @State private var isRefreshing = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var body: some View {
 | 
					    var body: some View {
 | 
				
			||||||
        NavigationView {
 | 
					        NavigationView {
 | 
				
			||||||
@ -10,7 +11,9 @@ struct HomeTab: View {
 | 
				
			|||||||
                if isLoading {
 | 
					                if isLoading {
 | 
				
			||||||
                    ProgressView("Загрузка ленты...")
 | 
					                    ProgressView("Загрузка ленты...")
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    ScrollView {
 | 
					                    RefreshableScrollView(isRefreshing: $isRefreshing, onRefresh: { _ in
 | 
				
			||||||
 | 
					                        fetchData()
 | 
				
			||||||
 | 
					                    }) {
 | 
				
			||||||
                        LazyVStack(spacing: 24) {
 | 
					                        LazyVStack(spacing: 24) {
 | 
				
			||||||
                            ForEach(posts) { post in
 | 
					                            ForEach(posts) { post in
 | 
				
			||||||
                                PostDetailView(post: post)
 | 
					                                PostDetailView(post: post)
 | 
				
			||||||
@ -22,12 +25,25 @@ struct HomeTab: View {
 | 
				
			|||||||
            .navigationTitle("Лента")
 | 
					            .navigationTitle("Лента")
 | 
				
			||||||
            .onAppear {
 | 
					            .onAppear {
 | 
				
			||||||
                if posts.isEmpty {
 | 
					                if posts.isEmpty {
 | 
				
			||||||
                    PostService.shared.fetchAllPosts { fetchedPosts in
 | 
					                    fetchData(isInitialLoad: true)
 | 
				
			||||||
                        self.posts = fetchedPosts
 | 
					 | 
				
			||||||
                        self.isLoading = false
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private func fetchData(isInitialLoad: Bool = false) {
 | 
				
			||||||
 | 
					        if isInitialLoad {
 | 
				
			||||||
 | 
					            isLoading = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        PostService.shared.fetchAllPosts { fetchedPosts in
 | 
				
			||||||
 | 
					            self.posts = fetchedPosts
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if isInitialLoad {
 | 
				
			||||||
 | 
					                print("content updated")
 | 
				
			||||||
 | 
					                self.isLoading = false
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            self.isRefreshing = false
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -38,6 +38,7 @@
 | 
				
			|||||||
		1ACE61192E22FF1400B37AC5 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61182E22FF1400B37AC5 /* Post.swift */; };
 | 
							1ACE61192E22FF1400B37AC5 /* Post.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61182E22FF1400B37AC5 /* Post.swift */; };
 | 
				
			||||||
		1ACE61212E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61202E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift */; };
 | 
							1ACE61212E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ACE61202E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift */; };
 | 
				
			||||||
		1AD757CD2E27608C0069C1FD /* PostFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD757CC2E27608C0069C1FD /* PostFeedView.swift */; };
 | 
							1AD757CD2E27608C0069C1FD /* PostFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD757CC2E27608C0069C1FD /* PostFeedView.swift */; };
 | 
				
			||||||
 | 
							1AD757D12E27640F0069C1FD /* RefreshableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AD757D02E27640F0069C1FD /* RefreshableScrollView.swift */; };
 | 
				
			||||||
		1AE587052E23264800254F06 /* PostService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE587042E23264800254F06 /* PostService.swift */; };
 | 
							1AE587052E23264800254F06 /* PostService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE587042E23264800254F06 /* PostService.swift */; };
 | 
				
			||||||
		1AE587252E23337000254F06 /* ProfileContentGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE587242E23337000254F06 /* ProfileContentGrid.swift */; };
 | 
							1AE587252E23337000254F06 /* ProfileContentGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AE587242E23337000254F06 /* ProfileContentGrid.swift */; };
 | 
				
			||||||
		1AEE5EAB2E21A83200A3DCA3 /* HomeTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AEE5EAA2E21A83200A3DCA3 /* HomeTab.swift */; };
 | 
							1AEE5EAB2E21A83200A3DCA3 /* HomeTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AEE5EAA2E21A83200A3DCA3 /* HomeTab.swift */; };
 | 
				
			||||||
@ -81,6 +82,7 @@
 | 
				
			|||||||
		1ACE61182E22FF1400B37AC5 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = "<group>"; };
 | 
							1ACE61182E22FF1400B37AC5 /* Post.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Post.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1ACE61202E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileContentTabbedGrid.swift; sourceTree = "<group>"; };
 | 
							1ACE61202E22FFD000B37AC5 /* ProfileContentTabbedGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileContentTabbedGrid.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1AD757CC2E27608C0069C1FD /* PostFeedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostFeedView.swift; sourceTree = "<group>"; };
 | 
							1AD757CC2E27608C0069C1FD /* PostFeedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostFeedView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
 | 
							1AD757D02E27640F0069C1FD /* RefreshableScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RefreshableScrollView.swift; path = Components/RefreshableScrollView.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1AE587042E23264800254F06 /* PostService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostService.swift; sourceTree = "<group>"; };
 | 
							1AE587042E23264800254F06 /* PostService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostService.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1AE587242E23337000254F06 /* ProfileContentGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileContentGrid.swift; sourceTree = "<group>"; };
 | 
							1AE587242E23337000254F06 /* ProfileContentGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileContentGrid.swift; sourceTree = "<group>"; };
 | 
				
			||||||
		1AEE5EAA2E21A83200A3DCA3 /* HomeTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTab.swift; sourceTree = "<group>"; };
 | 
							1AEE5EAA2E21A83200A3DCA3 /* HomeTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTab.swift; sourceTree = "<group>"; };
 | 
				
			||||||
@ -119,6 +121,7 @@
 | 
				
			|||||||
		1A7940792DF77BC2002569DA /* Shared */ = {
 | 
							1A7940792DF77BC2002569DA /* Shared */ = {
 | 
				
			||||||
			isa = PBXGroup;
 | 
								isa = PBXGroup;
 | 
				
			||||||
			children = (
 | 
								children = (
 | 
				
			||||||
 | 
									1AD757D02E27640F0069C1FD /* RefreshableScrollView.swift */,
 | 
				
			||||||
				1A7940FA2DF7B898002569DA /* Resources */,
 | 
									1A7940FA2DF7B898002569DA /* Resources */,
 | 
				
			||||||
				1A7940E52DF7B341002569DA /* Services */,
 | 
									1A7940E52DF7B341002569DA /* Services */,
 | 
				
			||||||
				1A7940A02DF77DCD002569DA /* Network */,
 | 
									1A7940A02DF77DCD002569DA /* Network */,
 | 
				
			||||||
@ -402,6 +405,7 @@
 | 
				
			|||||||
				1A7940AA2DF77E05002569DA /* LoginViewModel.swift in Sources */,
 | 
									1A7940AA2DF77E05002569DA /* LoginViewModel.swift in Sources */,
 | 
				
			||||||
				1AB4F8F32E22EC9F002B6E40 /* FollowersView.swift in Sources */,
 | 
									1AB4F8F32E22EC9F002B6E40 /* FollowersView.swift in Sources */,
 | 
				
			||||||
				1A79408D2DF77BC3002569DA /* yobbleApp.swift in Sources */,
 | 
									1A79408D2DF77BC3002569DA /* yobbleApp.swift in Sources */,
 | 
				
			||||||
 | 
									1AD757D12E27640F0069C1FD /* RefreshableScrollView.swift in Sources */,
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
			runOnlyForDeploymentPostprocessing = 0;
 | 
								runOnlyForDeploymentPostprocessing = 0;
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user