我正在尝试使一行的内容(在UITableView中)具有多种对齐方式:左,中和右。我在 Stackoverflow 中搜索了一个解决方案,发现这个:表格视图中一行的文本对齐方式不同。唯一的问题是该解决方案是基于 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
}
行数在部分
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
}}
我已经仔细检查了单元格标识符和自定义类等内容是否在情节提要中正确设置。
好的,小教程怎么做,也请根据编程最佳实践查看我在您的问题下的评论:
第一个文件,包含您的视图控制器:
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:/。就个人而言,我更喜欢代码解决方案,但这是您的决定。
- 复制代码;)
- 将对象中的
TableViewController
带到情节提要,Identity Inspector
设置类中MyTableViewController
- 扩展单元格,使高度达到所需的值(我已设置为 50),
Identity Inspector
将类设置为MyTableViewCell
- 向单元格添加 3 个标签
- 选择所有 3 个标签并添加约束:
- 等宽
- 同等高度
- 到顶部的垂直空间:10
- 到底部的垂直空间:10
- 选择左侧标签并设置:
- 水平间距到中间标签:10
- 左侧水平间距:10
- 选择正确的标签并设置:
- 水平间距到中间标签:10
- 右侧水平间距:10
- 单击右下角的三角形,然后选择
Update Frames
.一切都应该正确定位。
选择 - 每个标签,在右侧面板上选择
Attributes Inspector
,在其中标记正确的对齐方式(左为左标签,中间为中间标签,右为右标签)。 - 将相应的标签连接到
MyTableViewCell
IBOutlets
中的标签。
使用此解决方案,一切都应该可以正常工作。
好的,代码解决方案的另一个答案(因为它完全不同),而不是使用情节提要:
- 打开项目设置,删除主界面。您希望项目从代码运行,而不是使用情节提要。
- 现在,您必须设置哪个控制器应首先运行并设置一些 UIWindow 属性。
在下面更改应用委托代码的实现。
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
}
}
- 您还需要在控制器中设置更多详细信息,而不是通过使用代码。
复制以下代码:
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
}
}
- 您还必须创建单元格类并在其中设置所有变量。
复制以下代码:
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
}
}
- 现在您可以运行项目,一切都应该顺利进行。
最后,简单解释一下为什么我个人更喜欢代码:
- 不能在情节提要中使用继承。对我来说,这是不可接受的。您甚至不能重用视图(除非您为它们制作额外的 xib)。因此,您可以添加相同的子视图,并为少数 TableView 中使用的单元格重新单击相同的自动布局 ->愚蠢的。如果我错了,请纠正我,我想知道我错了。
- 使用 GIT 和合并时更容易维护。
- 至少从iOS 3开始,LayoutSubviews就可以正常工作。与此同时,在故事板中,有几种不同的方法来布局事物,但都不是最好的方法。
- 大多数开发人员在使用情节提要时不使用真正的 MVC:即使他们在代码中使用视图执行某些操作,他们也从不创建 UIView 类。
- 您无法在情节提要中执行所有操作。使用核心图形等进行绘图是不可能的。
- 使用情节提要创建复杂的布局很麻烦。同时,您可以更快地在代码中完成所有操作。