如何将 UITableView 放入 UIView

Posted

技术标签:

【中文标题】如何将 UITableView 放入 UIView【英文标题】:How to put UITableView inside a UIView 【发布时间】:2015-08-23 00:03:02 【问题描述】:

我正在尝试在 UIView 中实现表格视图。我目前正在努力解决的一个问题是链接委托和数据源。如果我取消注释 Xcode 下面的两行会给出“预期声明”错误。如果我从文档大纲中链接它们,Xcode 说没问题,但是模拟会崩溃。如果有人能解决这个问题,那就太好了。

谢谢!

import UIKit
class CurrentGameDetailView: UIView, UITableViewDelegate, UITableViewDataSource 

    @IBOutlet weak var tableView: UITableView!
    var theGame: GameObject? = nil

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return theGame!.roundNumber
    

//    tableView.delegate = self
//    tableView.dataSource = self

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("OldGameDetailCell", forIndexPath: indexPath) as! OldGameDetailCell

        cell.roundLabel.text = theGame!.gameRounds[indexPath.row].gamePlayed.description
        ...

        return cell
    

【问题讨论】:

我给你举个例子,等一下 快速提问,或者说真正的观察,将它放入 UIView 的目的是什么?可以将它放在 uiview 中,但您不需要将委派的内容粉碎到视图中。你需要在你的 UIViewConitroller 或 UITableViewContrioller 中调用 UIView 的 tableView 来控制这个视图,如果没有某种控制器,你就不能有一个独立的视图。所以,委托方法在 UIView 中有点没用,除非你真的需要它们,但这是一个很长的范围,而且是一些我认为你不打算的高级编码/ 我正在为游戏构建一个得分表应用程序。当用户点击 UIViewController 中的详细信息按钮时,我想显示一个动画 UIView。这个视图会有一个表格,显示前几轮比赛和球员的得分等。所以是的,这个类是用于自定义 UIView 的,它位于 UIViewController 内。顺便说一句,谢谢 触摸 uiviewcontroller 按钮弹出的视图应该是一个 uiviewcontroller 本身,它为 uitableview 提供数据,我会告诉你,很简单 任何需要控制内容的东西都应该是控制器,而不是视图,这是 MVC 的东西,控制器控制信息,视图只控制视图、布局、颜色等。最好做这是第一次正确的方式 【参考方案1】:

这是一个完整的项目,它有效。弹出第一个视图控制器,您会看到一些按钮,我只是使用了一个带有一些随机名称的 tableview,您单击名称,然后弹出另一个 tableview,其标题与您在第一个视图上单击的单元格名称相同控制器。这通过使用块来工作,在 UIView 中没有委托,但我将所有内容都放在自定义视图中,除了使用“loadView”方法创建的第一个 viewcontroller 视图。正如你所看到的,没有数据从 UIView 传递到 UITableViewCells,你不这样做,你从第一个视图控制器的单元格中提取所有数据,并假设第一个视图控制器的表格单元格有大量的数据形式一个 JSON 数组或类似的东西,然后您可以通过属性声明变量将整个“分数数组”传递给下一个视图控制器,我只是向您展示如何使用 UIViewController 的“标题”属性传递信息,您可以创建您自己的属性并在单击单元格按钮或常规按钮时传递整个数组。您不想将此信息放入您的子类 tableViewCell 中,但您可以从 secondViewController 的 cellForRowAtIndexPath 传递数据来为第二页上的记分牌设置自定义信息,UIVeiw 可以接受 UIViewController 的要求,而不是反过来,您使用自定义 UITableView 向 UIView 发送命令,并且自定义 UITableView 的数据由 UIViewController 设置。这是做事的正确方法。为了使这个超级健壮,可以在这个示例中添加更多内容,但想法是这样的:

示例 1: UIViewController1

button1 ==> UIViewController 从网络中提取信息或告诉 UIView 从网络中提取该信息存储在您制作的自定义 UIButton 中,其中包含数据数组

button2 ==> UIViewController 从网络中提取信息或告诉 UIView 从网络中提取此信息,这些信息存储在您制作的自定义 UIButton 中,其中包含数据数组

button3 ==> UIViewController 从网络中提取信息或告诉 UIView 从网络中提取此信息,这些信息存储在您制作的自定义 UIButton 中,其中包含数据数组

UIViewcontroller2 UITableView 将您存储在上面数组中的每个按钮的数据传递给 secondViewController 然后填充表格,这就是它的工作原理。

示例 2: UIViewController1

button1 ==> UIViewController 只是给 UIButton 添加一个动作事件

button2 ==> UIViewController 只是给 UIButton 添加了一个动作事件

button3 ==> UIViewController 只是给 UIButton 添加一个动作事件

UIViewcontroller2 自定义方法中的 TableView 调用网络并根据 UIViewcontroller1 中按钮的标题填充数据数组,该数组通过使用从第二个视图控制器中的 viewdidload 调用的自定义方法被粉碎到第二个视图控制器中,然后,第二个视图控制器调用它自己的视图,该视图已被类型转换为带有 UITableView 的自定义 UIView 子类。这个 UIViewController2 然后调用“cellForRowAtIndexPath”并使用单元格上的 setter 方法填充表格,从而强制数据进入 UITableView

在这两种方法中,我们在自定义视图中设置了多少个委托?零,因为 UIViewControllers 应该负责设置数据并向下发送,除非 UIViewController 明确要求,否则 UIView 不应该向上发送数据:

工作简单的例子(程序化,没有故事板,所以你可以看到发生了什么):

AppDelegate.swift:

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate 

    var window: UIWindow?
    var rootViewController: UINavigationController?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool 
        window = UIWindow(frame: UIScreen.mainScreen().bounds)
        rootViewController = UINavigationController(rootViewController: ViewController())
        if let window = window 
            window.backgroundColor = UIColor.whiteColor()
            window.rootViewController = rootViewController
            window.makeKeyAndVisible()
        
        return true
    

ViewController.swift

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate 

    var tableView : UITableView?
    var items = [
        "Ginger",
        "larry",
        "Moe",
        "Fred",
        "Terry"]

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) 
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    
    required init(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    override func viewDidLoad() 
        super.viewDidLoad()
        tableView = UITableView(frame: CGRectMake(0, 0, 414, 736), style: UITableViewStyle.Plain)
        tableView!.delegate = self
        tableView!.dataSource = self
        tableView!.registerClass(CustomTableViewCell.self, forCellReuseIdentifier: "Cell")
        self.view .addSubview(tableView!)
    
    override func loadView() 
        var stuf = UIView()
        stuf.frame = CGRectMake(0, 0, 414, 736)
        stuf.backgroundColor = UIColor .redColor()
        self.view = stuf
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return items.count;
    
    func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return 1
    
    func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat 
        return 44
       
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! CustomTableViewCell
        cell.doWork = 
            () -> Void in
            self.doStuff(indexPath.row)
        
        cell.labelMessage.text = items[indexPath.row] as String
        return cell
    
    func doStuff(integer: NSInteger) 
        var newViewController = SecondViewController()
        newViewController.title = items[integer] as String
        var dismissButton = UIBarButtonItem(title: "done", style: UIBarButtonItemStyle.Plain, target: self, action: "dismiss")
        newViewController.navigationItem.rightBarButtonItem = dismissButton
        var navControl =  UINavigationController(rootViewController: newViewController);
        self.navigationController?.presentViewController(navControl, animated: true, completion:  () -> Void in )
    
    func dismiss() 
        self.navigationController?.dismissViewControllerAnimated(true, completion:  () -> Void in)
    

SecondViewController.swift

import UIKit

class SecondViewController: UIViewController , UITableViewDataSource, UITableViewDelegate 

    var items = [
        "sports score1",
        "sports score2",
        "sports score3",
        "sports score4",
        "sports score5"]

    var myView: CustomViewWithTableView! 
        return self.view as! CustomViewWithTableView
    
    override func loadView() 
        let height = self.navigationController!.navigationBar.frame.size.height as CGFloat

        view = CustomViewWithTableView(frame: CGRectMake(0, height, UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height-height as CGFloat))
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) 
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    
    required init(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    
    override func viewDidLoad() 
        super.viewDidLoad()
        self.myView.tableView.delegate = self
        self.myView.tableView!.dataSource = self
        self.myView.tableView!.registerClass(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return items.count;
    
    func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return 1
    
    func tableView(tableView:UITableView, heightForRowAtIndexPath indexPath:NSIndexPath)->CGFloat 
        return 44
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
        cell.textLabel!.text =  items[indexPath.row] as String
        return cell
    

CustomViewWithTableView.swift

import Foundation
import UIKit

class CustomViewWithTableView: UIView 

    var tableView: UITableView!

    override init(frame: CGRect) 
        super.init(frame: frame)
        tableView = UITableView(frame: CGRectMake(frame.origin.x, frame.origin.y, frame.width, frame.height), style: .Plain)
        self.addSubview(tableView)
    
    required init(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

CustomTableViewCell.swift

import Foundation
import UIKit

class CustomTableViewCell: UITableViewCell, UIGestureRecognizerDelegate 

    var labelMessage = UILabel()

    var doWork: (() -> Void)?

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        let touchie: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "this")
        touchie.delegate = self
        touchie.cancelsTouchesInView = false
        self.addGestureRecognizer(touchie)
        labelMessage.setTranslatesAutoresizingMaskIntoConstraints(false)
        contentView.addSubview(labelMessage)

        //custom layout constraints
        var viewsDict =  ["labelMessage" : labelMessage]

        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[labelMessage]|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-20-[labelMessage]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDict))

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

   //custom touch interceptor for presentation navigation
    func this () 
        if let callback = self.doWork 
            println("swipe detected, cell function run")
            callback ()
        
    


如上所述,位于自定义 UIView 中的 UITableView 已死,直到 UIViewController 告诉视图它没有死。这就是 MVC 应该如何工作的,事实上,为了补充这一点,我会亲自添加一些东西以使这一点更加明显。

1. ViewController.swift 的另一个自定义 UIView

2。 SecondViewController.swift 的另一个自定义 UITableViewCell

3.第一组 Button 数据的对象模型,这是一个 NSObject 子类,这是第一个 viewController 屏幕上的“按钮”数据

4.另一个对象模型(也许)对“分数”数据中的第二组数据进行建模

5. UILabel 的 2 个自定义子类

6. UIButton 的 2 个自定义子类

UIView 子类在上面的 5 和 6 中声明了子类 UIElement 的 AS 属性,因此 5 和 6 只控制它们自己,并且 UIView 有点像它们的直接父级,作为 UIView 本身的属性。

在这个项目结束时,我会为每个 ViewController 拥有大约 5-10 个 swift 子类,甚至可能更多(使用 ObjC,它会是这个大小的两倍)。子视图的父类只控制子视图的内容和属性,从不控制父视图的内容,ios就是这样设计的,封装了从UIViewController向下延伸的“数据喋喋不休”的流程,而不是其他方式。

无论如何,祝你在 Swift 冒险中好运,我也在学习 Swift,因为我回答了这样的问题并将密集和高级的 ObjC 代码翻译成 Swift。如果您还有其他问题,请询问。谢谢,祝你好运!

【讨论】:

感谢您的广泛反馈。我意识到了这个问题,并且对 MVC 有了更好的理解。正如您所说,我已将更改应用于我的代码,现在可以正常运行,但是,当我点击按钮时,我的表是空的。 这是我所做的:CustomViewClass: @IBOutlet weak var tableView: UITableView! ViewController: self.detailsView.tableView.delegate = self self.detailsView.tableView.dataSource = self 在 vi​​ewDidLoad 和下面的部分 func tableView(tableView: UITableView, numberOfRowsInSection... func tableView(tableView: UITableView, cellForRowAtIndexPath ... 这样代码工作没有错误,但表格单元格由于某种原因不显示任何数据。 让我看看发生了什么事,给我几个小时,我会为你重新发布一些东西 你好 Larcerax 。我不在城里,仍然无法查看为什么我的表格视图加载空单元格的问题,但再次感谢您的支持!如果我能解决问题,我会添加评论。 是的,给我几个小时

以上是关于如何将 UITableView 放入 UIView的主要内容,如果未能解决你的问题,请参考以下文章

将 UITableViewController 插入其他 UIView

UIButton 嵌入 UIVIew 不负责任

如何将 UITableView 作为子视图添加到 xib 中的 UIView?

Swift:UIView 出现在 UITableView 单元格中

Swift:如何将 MapView 放入 UIView(?)

如何在 UIView 中嵌入 UITableView?