【问题标题】:UITableView - Row - Multiple alignmentsUITableView - 行 - 多个对齐方式
【发布时间】:2015-06-21 17:13:33
【问题描述】:

我正在尝试使一行的内容(在 UITableView 中)具有多个对齐方式:左、中和右。我在 Stackoverflow 上搜索了一个解决方案,发现了这个:Different text alignment to a row in table view。唯一的问题是该解决方案是基于 ObjectiveC 的,我无法将其转换为基于 Swift 的解决方案。

具体来说,我有一个表示活动用户列表的 UITableView。将内容加载到表格视图中可以正常工作,但为了便于阅读,内容 - 分为三种类型 - 需要在视觉上分为三个不同的“部分”。我该怎么做呢?上面的解决方案在 Swift 中会是什么样子?提前致谢。

代码(适用于 Vive)

类声明

class MainViewController: UIViewController, UITableViewDataSource, UITableViewDelegate

viewDidLoad()

self.activeIDs.delegate = self
self.activeIDs.dataSource = self
self.activeIDs.rowHeight = 25
self.activeIDs.separatorStyle = UITableViewCellSeparatorStyle.None
self.activeIDs.allowsMultipleSelection = true

cellForRowAtIndexPath

func tableView(tableView: UITableView,
    cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
        let cell = activeIDs.dequeueReusableCellWithIdentifier("cellReuseId", forIndexPath: indexPath) as ActiveIDTableViewCell

        cell.leftLabel.text = "I go left"
        cell.middleLabel.text = "I go center"
        cell.rightLabel.text = "I go right"

        return cell
}

numberOfRowsInSection

func tableView(tableView: UITableView,
    numberOfRowsInSection section: Int) -> Int {
        return 4 //I randomly chose 4, just for example
}

ActiveIDTableViewCell.swift - 几乎复制了

import UIKit

class ActiveIDTableViewCell: UITableViewCell {

// alloc & init labels
let leftLabel = UILabel()
let middleLabel = UILabel()
let rightLabel = UILabel()

// MARK: Memory Management

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    // never forget to call super in overriden UIKit methods (almost never ;))
    super.init(style: .Default, reuseIdentifier: reuseIdentifier)

    // now let's assign these values we've set previously in storyboards
    leftLabel.textAlignment = NSTextAlignment.Left
    // you created a label, but you need to add this label as a subview of cells view.
    contentView.addSubview(leftLabel)

    middleLabel.textAlignment = NSTextAlignment.Center
    contentView.addSubview(middleLabel)

    rightLabel.textAlignment = NSTextAlignment.Right
    contentView.addSubview(rightLabel)
}

required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented") //Xcode points to this line when error is thrown
}

// MARK: Layout

override func layoutSubviews() {
    super.layoutSubviews()

    // here happens all the magic which skips autolayouts. You can ofc set autolayouts from code :)
    let frame = self.contentView.bounds
    // frame was the size of the cell, but we want to set our labels at least 10px from sides of cell - so it looks nicely
    let insetFrame = CGRectInset(frame, 10.0, 10.0)
    // width of each label is width of cell / 3 (you want 3 labels next to each other) minus margins
    let labelWidth = (CGRectGetWidth(insetFrame) - 20.0) / 3.0

    // lets calculate the rects and assign frames to calculated values
    let leftLabelRect = CGRectMake(CGRectGetMinX(insetFrame), CGRectGetMinY(insetFrame), labelWidth, CGRectGetHeight(insetFrame))
    leftLabel.frame = leftLabelRect

    let middleLabelRect = CGRectMake(CGRectGetMaxX(leftLabelRect), CGRectGetMinY(insetFrame), labelWidth, CGRectGetHeight(insetFrame))
    middleLabel.frame = middleLabelRect

    let rightLabelRect = CGRectMake(CGRectGetMaxX(middleLabelRect), CGRectGetMinY(insetFrame), labelWidth, CGRectGetHeight(insetFrame))
    rightLabel.frame = rightLabelRect
}}

我已经仔细检查了故事板中的单元格标识符和自定义类等内容是否正确设置。

【问题讨论】:

  • 把这段代码翻译成 Swift 到底有什么麻烦……?有什么特别的行吗?
  • 我可能会得到的objective-c,但主要是这样的:“在你的故事板中添加一个 UITableViewCell CustomTableViewCell 类型到你的 UITableView 并在... cellForRowAtIndexPath ...更改你的单元格之后连接你的标签” s identifier to your customtableviewcell" 所以,我在我的 tableview 中添加了一个单元格,并将其标识符设置为包含三个标签 (pastebin.com/7EgyNGp5) 的 swift 类的名称,包括将其自定义类设置为该类?然后我在 cellForRowAtIndexPath? 中简单地写这个(pastebin.com/CfS47p3q)?这会输出我使用 cell.leftLabel 的展开可选错误(nil)
  • 一直在努力,我现在在我的 cellForRowAtIndexPath 中使用这个:pastebin.com/5vn1EkGS。唯一的问题是 tableview 中什么都没有出现 - 如何将这三个标签放在单元格的 textLabel 中?
  • 您应该将代码的内容粘贴到问题中,而不是第 3 方页面中 - 对其他开发人员来说不那么麻烦。您应该使用代码或故事板。如果您决定使用情节提要,请在此处设置textAligment。更重要的是,即使您想在代码中执行此操作(这是一个坏主意),那么您应该在单元类的init 中设置textAligment,而不是在cellForRowAtIndexPath 中一遍又一遍地设置它。单元格是可重用的,这些属性应该只设置一次,而不是每次您将单元格出列时。所有这些都是旁注,不是问题的解决方案。
  • 明白了!感谢您的小费。我一定会编辑我现有的问题,以便将来为人们提供代码。 :) 在您下面的帖子中,我评论了我正在寻找哪种类型的解决方案(故事板或代码),并决定使用代码 - 所以更多的帮助肯定会受到赞赏!

标签: ios swift uitableview alignment


【解决方案1】:

好的,小教程怎么做,也请根据编程最佳实践在你的问题下查看我的cmets:

第一个文件,包含您的 ViewController:

class MyTableViewController: UITableViewController {

    // here ofc you'll have your array/dictionary of input data
    let titles = ["iOS", "Android", "Windows Phone", "Firefox OS", "Blackberry OS", "Tizen", "Windows Mobile", "Symbian", "Palm OS"]

    // this is to make sure how many rows your table should have
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return titles.count
    }

    // here you dequeue tableViewCells, so they're reusable (so your table is fast & responding
    // allocating new resources for new cell is very consuming
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell: MyTableViewCell = tableView.dequeueReusableCellWithIdentifier("cellId", forIndexPath: indexPath) as! MyTableViewCell
        // fill your data
        cell.rightLabel.text = "I'm right cell"
        cell.middleLabel.text = titles[indexPath.row]
        cell.leftLabel.text = "I'm left cell"

        return cell;
    }
}

第二个带有单元格类的文件:

class MyTableViewCell: UITableViewCell {

    @IBOutlet weak var leftLabel: UILabel!
    @IBOutlet weak var middleLabel: UILabel!
    @IBOutlet weak var rightLabel: UILabel!

}

现在如何告诉你如何处理 IB :/.就个人而言,我更喜欢代码解决方案,但这是您的决定。

  1. 复制代码 ;)
  2. TableViewController 从对象带到您的故事板,在Identity Inspector 中将类设置为MyTableViewController
  3. Identity Inspector 中将单元格扩展为所需的高度(我设置为50),将类设置为MyTableViewCell
  4. 为您的单元格添加 3 个标签
  5. 选择所有 3 个标签并添加约束:
    • 等宽
    • 等高
    • 到顶部的垂直间距:10
    • 到底部的垂直间距:10
  6. 选择左侧标签并设置:
    • 到中间标签的水平间距:10
    • 左侧水平间距:10
  7. 选择正确的标签并设置:
    • 到中间标签的水平间距:10
    • 向右水平间距:10
  8. 单击右下角的三角形并选择Update Frames。一切都应该正确定位。
  9. 选择每个标签,在右侧面板上选择Attributes Inspector,在其中标记正确的对齐方式(左侧为左侧标签,中间为中间标签,右侧为右侧标签)。
  10. 将适当的标签连接到MyTableViewCell IBOutlets 中的标签。

这个解决方案应该一切正常。

【讨论】:

  • 感谢您的深入回答,非常感谢。不过,您说代码解决方案更好,我更愿意使用它而不是情节提要 - 特别是因为我在约束方面的经验不是很好!无论如何,关于代码解决方案,我已经查看了您的代码并确保我的代码可以正常工作。现在,下一步是在MyTableViewCell类的'init()'方法中为每个标签设置textAlignment,然后将三个标签的内容设置为cell.textLabel的内容?如果是这样,这样的“init()”方法会是什么样子?
  • @Tice 我稍后会将它作为单独的答案发布,只需对其进行编码,因为我更喜欢 Obj-C 而不是 Swift,并且如果不检查语法就无法用 Swift 编写。另外,如果我的回答对您有所帮助,请接受它;)另外,我并没有声称代码更好 - 我只是认为它“更稳定”并且在更高级的项目中更漂亮。
【解决方案2】:

好的另一个答案(因为它完全不同)代码解决方案而不是使用故事板:

  1. 打开项目设置,删除主界面。您希望您的项目从代码运行,而不是使用 Storyboard。
  2. 现在您必须设置首先运行哪个控制器并设置一些 UIWindow 属性。

在下面更改 AppDelegate 代码的实现。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // create window and set proper size of it
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds) 
        self.window?.backgroundColor = UIColor.whiteColor()
        self.window?.makeKeyAndVisible()
        // assign your controller to be the 'entering' point
        self.window?.rootViewController = UINavigationController(rootViewController: MyViewController(style:UITableViewStyle.Plain)) 

        // start the app
        return true
    }
}
  1. 您还需要在控制器中设置比使用代码更多的详细信息。

复制以下代码:

import UIKit

class MyViewController: UITableViewController {

    // use let to keep ID, as at least in 2 places we need this string, it's easier to maintain it in one place and omit any typos
    let cellReuseId = "cellReuseId"
    let titles = ["iOS", "Android", "Windows Phone", "Firefox OS", "Blackberry OS", "Tizen", "Windows Mobile", "Symbian", "Palm OS"]

    // MARK: Memory Management

    override init(style: UITableViewStyle) {
        super.init(style: style)
    }

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

    override init!(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    // MARK: View Lifecycle

    override func viewDidLoad() {
        // tell the table which class should be used to dequeue cells with given reuse id (previously you set it in storyboards)
        tableView.registerClass(MyTableViewCell.self, forCellReuseIdentifier: cellReuseId)
    }

    // MARK: UITableViewDelegate && UITableViewDataSource

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return titles.count
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(cellReuseId, forIndexPath: indexPath) as! MyTableViewCell
        cell.rightLabel.text = "I'm right cell"
        cell.middleLabel.text = titles[indexPath.row]
        cell.leftLabel.text = "I'm left cell"

        return cell
    }
}
  1. 您还必须创建单元类并在其中设置所有变量。

复制以下代码:

import UIKit

class MyTableViewCell: UITableViewCell {

    // alloc & init labels
    let leftLabel = UILabel()
    let middleLabel = UILabel()
    let rightLabel = UILabel()

    // MARK: Memory Management

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        // never forget to call super in overriden UIKit methods (almost never ;))
        super.init(style: .Default, reuseIdentifier: reuseIdentifier)

        // now let's assign these values we've set previously in storyboards
        leftLabel.textAlignment = NSTextAlignment.Left
        // you created a label, but you need to add this label as a subview of cells view.
        contentView.addSubview(leftLabel)

        middleLabel.textAlignment = NSTextAlignment.Center
        contentView.addSubview(middleLabel)

        rightLabel.textAlignment = NSTextAlignment.Right
        contentView.addSubview(rightLabel)
    }

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

    // MARK: Layout

    override func layoutSubviews() {
        super.layoutSubviews()

        // here happens all the magic which skips autolayouts. You can ofc set autolayouts from code :)
        let frame = self.contentView.bounds
        // frame was the size of the cell, but we want to set our labels at least 10px from sides of cell - so it looks nicely
        let insetFrame = CGRectInset(frame, 10.0, 10.0)
        // width of each label is width of cell / 3 (you want 3 labels next to each other) minus margins
        let labelWidth = (CGRectGetWidth(insetFrame) - 20.0) / 3.0

        // lets calculate the rects and assign frames to calculated values
        let leftLabelRect = CGRectMake(CGRectGetMinX(insetFrame), CGRectGetMinY(insetFrame), labelWidth, CGRectGetHeight(insetFrame))
        leftLabel.frame = leftLabelRect

        let middleLabelRect = CGRectMake(CGRectGetMaxX(leftLabelRect), CGRectGetMinY(insetFrame), labelWidth, CGRectGetHeight(insetFrame))
        middleLabel.frame = middleLabelRect

        let rightLabelRect = CGRectMake(CGRectGetMaxX(middleLabelRect), CGRectGetMinY(insetFrame), labelWidth, CGRectGetHeight(insetFrame))
        rightLabel.frame = rightLabelRect
    }
}
  1. 现在您可以运行项目了,一切都应该运行顺利。

最后,我个人更喜欢代码的一个小解释:

  1. 您不能在情节提要中使用继承。对我来说这是不可接受的。您甚至不能重用视图(除非您为它们制作额外的 xib)。因此,您可以添加相同的子视图并重新单击少数 TableView 中使用的单元格的相同自动布局 -> 愚蠢。如果我错了,请纠正我,我想知道我错了。
  2. 使用 GIT 和合并时更容易维护。
  3. 至少从 iOS 3 开始,LayoutSubviews 就可以正常工作。同时,在故事板中,有几种不同的布局方式,但都不是最好的方式。
  4. 大多数开发人员在使用 Storyboard 时并不使用真正的 MVC:即使他们在代码中使用视图执行某些操作,他们也从不创建 UIView 类。
  5. 您无法在 Storyboard 中完成所有操作。无法使用 CoreGraphics 等进行绘图。
  6. 使用 Storyboard 创建复杂的布局很麻烦。同时,您可以更快地在代码中完成所有操作。

【讨论】:

  • 我对故事板继承一无所知,但我仍然打算在这个项目中使用我的故事板,因为删除它会导致我不得不重做很多事情。尽管如此,我已经通读了您的答案(多次)并对我的代码进行了必要的更改。不幸的是,我在 ActiveIDTableViewCell.swift 中被抛出错误:“init(coder:) has not been implemented:”(在主题问题中检查我的代码)。我已经(希望)在我的主题问题中发布了与此问题相关的所有代码,并希望您能进一步帮助我摆脱这个错误。谢谢你:)
  • @Tice 好吧,您仍然从情节提要而不是从 ViewController 加载数据。您需要从推送该视图控制器的位置删除操作(它是初始的吗?也许它来自按钮单击?)并且您应该通过代码调用推送,因此在这种情况下您将进入该视图控制器并跳过情节提要。你的代码没问题。另外,为了防止这个错误,只需添加这个方法(你可以基于我的 init 方法)。
  • 我不明白。我的 MainViewController 是初始的 - 我需要对此进行任何更改吗?您指的是什么初始化方法以及在哪里?
  • @Tice 你如何检查你的 MainViewController 是否是初始的 - 在情节提要中,不是吗?问题是您正在使用情节提要,因此您需要选择情节提要解决方案或从 AppDelegate 转换此代码(如果那是初始代码)。您无法从情节提要运行 viewController 解决方案并跳过情节提要。
  • 我明白了。我已经让故事板解决方案工作了——除了我根本不使用任何约束。您所指的那些值(到处都是 10)是否依赖于 rowHeight 为 50?因为这可以解释为什么每次我尝试使用 32 的 rowHeight 设置约束时它都不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-02
相关资源
最近更新 更多