4

使用List视图,有没有一种方法可以访问(并因此修改)底层UITableView对象,而无需将整个对象重新实现ListUIViewRepresentable?

我已经尝试List在我自己的中初始化 a UIViewRepresentable,但我似乎无法让 SwiftUI 在我需要时初始化视图,而且我只是得到一个UIView没有子视图的空基本。

这个问题是为了帮助在 SwiftUI 中找到Bottom-first scrolling 的答案


UITableView或者,在 SwiftUI中重新实现的库或其他项目也可以回答这个问题。

4

6 回答 6

4

答案是肯定的。有一个很棒的库可以让你检查底层的 UIKit 视图。这是它的链接

于 2020-04-06T07:20:16.383 回答
1

答案是不。从 iOS 13 开始,SwiftUI 的 List 目前还没有设计来取代 UITableView 的所有功能和可定制性。它旨在满足 UITableView 的最基本用途:一个标准外观、可滚动、可编辑的列表,您可以在其中在每个单元格中放置一个相对简单的视图。

换句话说,您放弃了可定制性,以便为您自动实现滑动、导航、移动、删除等的简单性。

我敢肯定,随着 SwiftUI 的发展,List(或等效视图)将变得更加可定制,我们将能够执行诸如从底部滚动、更改填充等操作。确保发生这种情况的最佳方法是向Apple 提交反馈建议。我确信 SwiftUI 工程师已经在努力设计将出现在 WWDC 2020 上的功能。他们为指导社区想要和需要的内容提供的输入越多越好。

于 2019-09-26T14:04:56.443 回答
1

Rotoscope 我在 GitHub 上找到了一个库(我不是这个库的作者)。

该库也由同一作者RefreshUI 在 GitHub 上实现。

它的工作原理是Rotoscope有一个方法,它在你的顶部tagging覆盖一个 0 大小(所以它是不可见的)。该视图将挖掘视图链并最终找到托管 SwiftUI 视图的视图。然后,它将返回宿主视图的第一个子视图,该子视图应该包含一个包装器,然后您可以通过获取包装器的子视图来访问表视图对象。 UIViewRepresentableListUIHostingViewUITableView

RefreshUI库使用这个库来实现对 SwiftUI 的刷新控制List(您可以进入 GitHub 链接并查看源代码以了解它是如何实现的)。

但是,我认为这更像是一种 hack,而不是实际的方法,因此由您决定是否要使用它。无法保证它会在重大更新之间继续工作,因为 Apple 可能会更改内部视图布局并且此库会中断。

于 2020-02-29T05:27:49.703 回答
1

你能行的。但它需要一个黑客。

  1. 添加任何自定义 UIView
  2. 使用 UIResponder 回溯,直到找到 table View。
  3. 修改 UITableView 自己喜欢的方式。

添加拉取刷新的代码示例:

//1: create a custom view 
final class UIKitView : UIViewRepresentable {
    let callback: (UITableView) -> Void

    init(leafViewCB: @escaping ((UITableView) -> Void)) {
        callback = leafViewCB
    }

    func makeUIView(context: Context) -> UIView  {
        let view = UIView.init(frame: CGRect(x: CGFloat.leastNormalMagnitude,
                                             y: CGFloat.leastNormalMagnitude,
                                             width: CGFloat.leastNormalMagnitude,
                                             height: CGFloat.leastNormalMagnitude))
        view.backgroundColor = .clear
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        if let superView = uiView.superview {
            superView.backgroundColor = uiView.backgroundColor
        }

        if let tableView = uiView.next(UITableView.self) {
            callback(tableView)
        }
    }
}

extension UIResponder {
    func next<T: UIResponder>(_ type: T.Type) -> T? {
        return next as? T ?? next?.next(type)
    }
}

////Use:

struct Result: Identifiable {
    var id = UUID()
    var value: String
}


class RefreshableObject: ObservableObject {
    let id = UUID()
    @Published var items: [Result] = [Result(value: "Binding"),
                                      Result(value: "ObservableObject"),
                                      Result(value: "Published")]

    let refreshControl: UIRefreshControl

    init() {
        refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action:
            #selector(self.handleRefreshControl),
                                      for: .valueChanged)
    }

    @objc func handleRefreshControl(sender: UIRefreshControl) {
           DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {  [weak self] in
                sender.endRefreshing()
                self?.items = [Result(value:"new"), Result(value:"data"), Result(value:"after"), Result(value:"refresh")]
           }
    }
}



struct ContentView: View {

    @ObservedObject var refreshableObject = RefreshableObject()
    var body: some View {
            NavigationView {
                Form {
                    Section(footer: UIKitView.init { (tableView) in
                        if tableView.refreshControl == nil  {
                           tableView.refreshControl = self.refreshableObject.refreshControl
                        }
                    }){
                        ForEach(refreshableObject.items) { result in
                            Text(result.value)
                        }
                    }

                }
                    .navigationBarTitle("Nav bar")

            }
    }
}

截屏:在此处输入图像描述

于 2020-05-02T11:32:47.800 回答
0

要从刷新操作更新,正在使用绑定isUpdateOrdered 。

此代码基于我在网上找到的代码,找不到作者


import Foundation
import SwiftUI

class Model: ObservableObject{
    @Published var isUpdateOrdered = false{
        didSet{
            if isUpdateOrdered{
                update()
                isUpdateOrdered = false
                print("we got him!")
            }
        }
    }
    var random = 0
    @Published var arr = [Int]()
    
    func update(){
        isUpdateOrdered = false
        //your update code.... maybe some fetch request or POST?
    }
}

struct ContentView: View {
    @ObservedObject var model = Model()
    var body: some View {
        NavigationView {
            LegacyScrollViewWithRefresh(isUpdateOrdered: $model.isUpdateOrdered) {
                VStack{
                    if model.arr.isEmpty{ 
//this is important to fill the
//scrollView with invisible data,
//in other case scroll won't work
//because of the constraints.
//You may get rid of them if you like. 
                        Text("refresh!")
                        ForEach(1..<100){ _ in
                            Text("")
                        }
                    }else{
                        ForEach(model.arr, id:\.self){ i in
                            NavigationLink(destination: Text(String(i)), label: { Text("Click me") })
                        }
                    }
                    
                }
                
            }.environmentObject(model)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


struct LegacyScrollViewWithRefresh: UIViewRepresentable {
    enum Action {
        case idle
        case offset(x: CGFloat, y: CGFloat, animated: Bool)
    }
    typealias Context = UIViewRepresentableContext<Self>
    @Binding var action: Action
    @Binding var isUpdateOrdered: Bool
    private let uiScrollView: UIScrollView
    private var uiRefreshControl = UIRefreshControl()
    
    init<Content: View>(isUpdateOrdered: Binding<Bool>, content: Content) {
        let hosting = UIHostingController(rootView: content)
        hosting.view.translatesAutoresizingMaskIntoConstraints = false
        self._isUpdateOrdered = isUpdateOrdered
        uiScrollView = UIScrollView()
        
        uiScrollView.addSubview(hosting.view)
        
        let constraints = [
            hosting.view.leadingAnchor.constraint(equalTo: uiScrollView.leadingAnchor),
            hosting.view.trailingAnchor.constraint(equalTo: uiScrollView.trailingAnchor),
            hosting.view.topAnchor.constraint(equalTo: uiScrollView.contentLayoutGuide.topAnchor),
            hosting.view.bottomAnchor.constraint(equalTo: uiScrollView.contentLayoutGuide.bottomAnchor),
            hosting.view.widthAnchor.constraint(equalTo: uiScrollView.widthAnchor)
        ]
        uiScrollView.addConstraints(constraints)
        
        self._action = Binding.constant(Action.idle)
    }
    
    init<Content: View>(isUpdateOrdered: Binding<Bool>, @ViewBuilder content: () -> Content) {
        self.init(isUpdateOrdered: isUpdateOrdered, content: content())
    }
    
    init<Content: View>(isUpdateOrdered: Binding<Bool>, action: Binding<Action>, @ViewBuilder content: () -> Content) {
        self.init(isUpdateOrdered: isUpdateOrdered, content: content())
        self._action = action
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> UIScrollView {
        
        
        uiScrollView.addSubview(uiRefreshControl)
        uiRefreshControl.addTarget(context.coordinator, action: #selector(Coordinator.handleRefreshControl(arguments:)), for: .valueChanged)
        return uiScrollView
    }
    
    func updateUIView(_ uiView: UIScrollView, context: Context) {
        switch self.action {
            case .offset(let x, let y, let animated):
                uiView.setContentOffset(CGPoint(x: x, y: y), animated: animated)
                DispatchQueue.main.async {
                    self.action = .idle
                }
            default:
                break
        }
    }
    
    
    class Coordinator: NSObject {
        let legacyScrollView: LegacyScrollViewWithRefresh
        
        init(_ legacyScrollView: LegacyScrollViewWithRefresh) {
            self.legacyScrollView = legacyScrollView
        }
        @objc func handleRefreshControl(arguments: UIRefreshControl){
            print("refreshing")
            self.legacyScrollView.isUpdateOrdered = true
            DispatchQueue.main.asyncAfter(deadline: .now() + 2){
                arguments.endRefreshing()
//refresh animation will
//always be shown for 2 seconds,
//you may connect this behaviour
//to your update completion
            }
        }
    }
    
}
于 2021-03-15T14:20:30.097 回答
-1

目前没有办法访问或修改底层的 UITableView

于 2019-09-26T18:25:08.190 回答