在子视图中与 UITableView 交互

Posted

技术标签:

【中文标题】在子视图中与 UITableView 交互【英文标题】:Interacting with UITableView in a subview 【发布时间】:2019-06-25 12:33:40 【问题描述】:

我将 tableView 嵌入到自定义 UITextfield 的超级视图中。 tableview 是在视图上显示结果,这意味着我希望能够滚动并选择 tableView 中的单元格。表格视图显示,但我无法滚动或选择表格视图单元格。

这是使用 UITexfield 呈现 tableView 的方式。

UITexfield 扩展

override init(frame: CGRect) 
        super.init(frame: frame)
        shared()
        commonInit()
        if let superview = superview 
            setupAutocompleteTable(superview)
        
    

    public override func willMove(toSuperview newSuperview: UIView?) 
        super.willMove(toSuperview: newSuperview)
        commonInit()
        setupAutocompleteTable(newSuperview!)

    
    lazy var tableView: UITableView = 
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.dataSource = self
        tableView.delegate = self
        tableView.rowHeight = autoCompleteCellHeight
        tableView.isHidden = hidesWhenEmpty ?? true
        tableView.layer.borderColor = UIColor.lightGray.cgColor
        tableView.layer.borderWidth = 0.5
        tableView.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
        return tableView
    ()

    fileprivate func setupAutocompleteTable(_ view: UIView) 

        autoCompleteTableMargin = 10.0

        view.addSubview(tableView)
        tableView.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 20).isActive = true
        tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive = true
        tableView.heightAnchor.constraint(equalToConstant: 30).isActive = true

        autoCompleteTableView = tableView
        autoCompleteTableHeight = 200.0
    

视图控制器

lazy var searchField: GooglePlaceSearchField = 
        let field = GooglePlaceSearchField()
        field.translatesAutoresizingMaskIntoConstraints = true
        field.placeholder = "Enter area, city .."
        field.setIcon(#imageLiteral(resourceName: "zamasearch"))
        field.highLightTypeTextedEnabled = true
        return field
    ()

    view.addSubview(searchBgView)
    searchBgView.addSubview(searchField)

关于如何与 tableView 交互的任何帮助

searchField 的约束

searchBgView.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: nil, right: view.rightAnchor, paddingTop: 14, paddingLeft: 20, paddingBottom: 0, paddingRight: 20, width: 0, height: 50, enableInsets: false)
        searchField.anchor(top: searchBgView.topAnchor, left: searchBgView.leftAnchor, bottom: searchBgView.bottomAnchor, right: iconContainerView.leftAnchor, paddingTop: 00, paddingLeft: 20, paddingBottom: 0, paddingRight: 0, width: 0, height: 0, enableInsets: false)

【问题讨论】:

酷,我明白了,所以这就像一个小的建议表,会在您输入时从文本字段中弹出。 您在哪里为searchField 设置约束? searchField 被嵌入到searchBgView 中,该searchBgView 具有过滤按钮等其他组件,现在添加代码@Sh_Khan @matt 我已经更新了问题 @King -- 您要添加UITableView 作为UITextField子视图?我的第一个想法是:表格视图超出了它的超级视图(文本字段)的范围。如果您在文本字段中设置.clipsToBounds = true,那么您可能甚至不会看到表格视图。您需要实施命中测试以“启用”与表格视图的交互。 【参考方案1】:

编辑:

在重新阅读问题并关注 cmets 后,tableView 被添加为 textField 的同级......但同样的问题也适用。 tableView 在其父视图的边界之外

此示例仍可用作在“搜索背景视图”中处理命中测试的起点(或者,将表格用作字段的子视图,如此处所示)。


这是一个简单的示例,您应该可以将其用作自定义字段/数据源的基础。

如果您注释掉 hitTest(...) func,您将看到该表,但无法与之交互。

注意:这只是示例代码,可帮助您上路——不被视为生产就绪。

class MyCustomTextField: UITextField 

    lazy var tableView: UITableView = 
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.dataSource = self
        tableView.delegate = self
        tableView.layer.borderColor = UIColor.lightGray.cgColor
        tableView.layer.borderWidth = 0.5
        tableView.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
        return tableView
    ()

    let theData = [ "A", "B", "C", "D", "E", "F", "G", "H", "I" ]

    // if we comment-out this func, we will NOT be able to interact with the tableView
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 

        guard isUserInteractionEnabled else  return nil 

        guard !isHidden else  return nil 

        guard alpha >= 0.01 else  return nil 

        let convertedPoint = tableView.convert(point, from: self)
        if let v = tableView.hitTest(convertedPoint, with: event) 
            return v
        

        guard self.point(inside: point, with: event) else  return nil 

        return self
    


    override init(frame: CGRect) 
        super.init(frame: frame)
        setupAutocompleteTable(self)
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    public override func willMove(toSuperview newSuperview: UIView?) 
        super.willMove(toSuperview: newSuperview)
        setupAutocompleteTable(self)
    

    fileprivate func setupAutocompleteTable(_ view: UIView) 
        if tableView.superview == nil 
            view.addSubview(tableView)
            tableView.topAnchor.constraint(equalTo: view.bottomAnchor, constant: 20).isActive = true
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
            tableView.heightAnchor.constraint(equalToConstant: 200).isActive = true

            tableView.register(UITableViewCell.self, forCellReuseIdentifier: "TheCell")
        
    



extension MyCustomTextField: UITableViewDataSource 
    func numberOfSections(in tableView: UITableView) -> Int 
        return 1
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return theData.count
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "TheCell", for: indexPath)
        cell.textLabel?.text = theData[indexPath.row]
        return cell
    


extension MyCustomTextField: UITableViewDelegate 
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        self.text = theData[indexPath.row]
    


class TextFieldSubViewController: UIViewController 

    let customTextField: MyCustomTextField = 
        let v = MyCustomTextField()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.borderStyle = .roundedRect
        v.backgroundColor = .yellow  // makes it easy to see
        return v
    ()

    override func viewDidLoad() 
        super.viewDidLoad()

        view.addSubview(customTextField)

        NSLayoutConstraint.activate([
            customTextField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40.0),
            customTextField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40.0),
            customTextField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40.0),
            ])

    


结果:


编辑 2:

这是另一个简单示例,使用“搜索背景视图”,UITextFieldUIButtonUITableView 作为子视图(彼此的兄弟)。当文本字段开始/结束编辑时,表格视图将显示/隐藏。

class CustomSearchView: UIView 

    lazy var theTextField: UITextField = 
        let v = UITextField()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.borderStyle = .roundedRect
        v.delegate = self
        return v
    ()

    lazy var tableView: UITableView = 
        let tableView = UITableView()
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.dataSource = self
        tableView.delegate = self
        tableView.layer.borderColor = UIColor.lightGray.cgColor
        tableView.layer.borderWidth = 0.5
        tableView.layer.zPosition = CGFloat(Float.greatestFiniteMagnitude)
        return tableView
    ()

    let doneButton: UIButton = 
        let v = UIButton()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.setTitle("Done", for: .normal)
        v.setTitleColor(.blue, for: .normal)
        return v
    ()

    let theData = [ "A", "B", "C", "D", "E", "F", "G", "H", "I" ]

    override init(frame: CGRect) 
        super.init(frame: frame)
        commonInit()
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        commonInit()
    

    func commonInit() -> Void 

        addSubview(theTextField)
        addSubview(doneButton)
        addSubview(tableView)

        NSLayoutConstraint.activate([

            doneButton.topAnchor.constraint(equalTo: topAnchor, constant: 4.0),
            doneButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4.0),
            doneButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8.0),

            theTextField.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8.0),
            theTextField.trailingAnchor.constraint(equalTo: doneButton.leadingAnchor, constant: -8.0),
            theTextField.centerYAnchor.constraint(equalTo: doneButton.centerYAnchor),

            tableView.topAnchor.constraint(equalTo: theTextField.bottomAnchor, constant: 8.0),
            tableView.leadingAnchor.constraint(equalTo: theTextField.leadingAnchor, constant: 0.0),
            tableView.trailingAnchor.constraint(equalTo: theTextField.trailingAnchor, constant: 0.0),

            tableView.heightAnchor.constraint(equalToConstant: 200.0),

            ])

        doneButton.setContentHuggingPriority(.required, for: .horizontal)

        doneButton.addTarget(self, action: #selector(doneTapped), for: .touchUpInside)

        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "TheCell")

        tableView.isHidden = true

    

    @objc func doneTapped() -> Void 
        self.endEditing(true)
    

    // if we comment-out this func, we will NOT be able to interact with the tableView
    // Note: this hitTest func based on source here: http://khanlou.com/2018/09/hacking-hit-tests/
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 

        guard isUserInteractionEnabled else  return nil 

        guard !isHidden else  return nil 

        guard alpha >= 0.01 else  return nil 

        for subview in subviews.reversed() 
            let convertedPoint = subview.convert(point, from: self)
            if let candidate = subview.hitTest(convertedPoint, with: event) 
                return candidate
            
        

        guard self.point(inside: point, with: event) else  return nil 

        return self
    



extension CustomSearchView: UITextFieldDelegate 
    func textFieldDidBeginEditing(_ textField: UITextField) 
        tableView.isHidden = false
    
    func textFieldDidEndEditing(_ textField: UITextField) 
        tableView.isHidden = true
    


extension CustomSearchView: UITableViewDataSource 
    func numberOfSections(in tableView: UITableView) -> Int 
        return 1
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return theData.count
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "TheCell", for: indexPath)
        cell.textLabel?.text = theData[indexPath.row]
        return cell
    


extension CustomSearchView: UITableViewDelegate 
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        self.theTextField.text = theData[indexPath.row]
    



class TextFieldSubViewController: UIViewController 

    let customSearchView: CustomSearchView = 
        let v = CustomSearchView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .lightGray
        return v
    ()

    override func viewDidLoad() 
        super.viewDidLoad()

        view.addSubview(customSearchView)

        NSLayoutConstraint.activate([
            customSearchView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0.0),
            customSearchView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0.0),
            customSearchView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0.0),
            ])

    


结果:


【讨论】:

我必须认为有一天这对某人来说会非常有用。抱歉,我只能投票一次。

以上是关于在子视图中与 UITableView 交互的主要内容,如果未能解决你的问题,请参考以下文章

UITableView 可重用的自定义单元格在子视图中显示错误的内容

表视图的 headerView 中的 UITableView

iPhone UIView:是不是可以仅在子视图上启用用户交互?

使用容器视图控制器,如何在子视图控制器之间添加交互式动画?

具有实时数据的 UITableView 也同时具有用户交互

滚动/调整 UITableView