自动布局以编程方式划分视图宽度
AutoLayout divide width of view programatically
我一直在将一些代码从使用框架更改为使用自动布局,但遇到了一个小问题
作为背景,我一直在构建一个日历,我的问题出在我一直试图制作的日历的“日”视图中,以便它复制苹果的日历。
这个应用自 2018 年以来一直在我的硬盘中,所以有点过时了,但在 2018 年它做了以下事情:
通过创建 EventContainerView
并将其放置在 TableView
上(不是作为单元格)来显示 Events
。
同时存在的事件会缩短它们的宽度以防止容器视图重叠。
这最后一部分是我的问题所在。
我通过获取 tableView 的宽度,将其除以重叠事件的数量,然后将其乘以一个 shiftBy
变量来将事件向右移动来做到这一点
我这辈子都找不到用 autoLayout 实现它的方法。
自动布局之前
func drawEvent(_ event:Event, overlaps:Int, shiftBy:Int) -> EventContainerView{
....
....
let eventWidth = (tableView.frame.width - 30) / CGFloat(overlaps)
let shift = eventWidth * CGFloat(shiftBy)
var frame: CGRect
if(shiftBy > 0){
frame = CGRect(x: CGFloat(30 + (5*shiftBy)) + shift, y: startPoint, width: eventWidth, height: endpoint)
}else{
frame = CGRect(x: CGFloat(30) + shift, y: startPoint, width: eventWidth, height: endpoint)
}
自动布局后
func drawEvent(_ event:Event, overlaps:Int, shiftBy:Int) -> EventContainerView{
....
....
let eventView = EventContainerView(forEvent: event, today: self)
eventView.translatesAutoresizingMaskIntoConstraints = false
var left: NSLayoutConstraint = eventView.leftAnchor.constraint(equalTo: tableView.leftAnchor, constant: +20)
if(shiftBy>0){
left = ????
}
tableView.addSubview(eventView)
let layout = [eventView.topAnchor.constraint(equalTo: tableView.topAnchor, constant: startPoint),
left,
eventView.heightAnchor.constraint(equalToConstant: endpoint),
eventView.widthAnchor.constraint(equalTo: tableView.widthAnchor, multiplier: 1/CGFloat(overlaps), constant: -30)]
NSLayoutConstraint.activate(layout)
return eventView
任何人都可以就我如何实现这段代码提出任何建议吗?
谢谢
编辑
这就是我想要实现的目标(我在 macOS 的日历上使用了它,但它仍然清楚地显示了我希望日历事件如何显示)
这就是我所拥有的(15 到 16 之间的深黄色是堆叠在“测试一切”事件下方的另一个事件。我需要其中一个转移(以及可能会或可能不会发生的任何其他事件)被添加到同一时间范围)
许多不同的方法来解决这个问题,但要使其接近您正在做的事情...
您可以创建跨越宽度的等宽视图,使用它们之间的固定间距和每边的特定“填充”,方法是:
- 将第一个视图的行距限制为父视图的行距+填充
- 将每个连续视图的前导限制为前一个视图的尾随 + 间距
- 将最后一个视图的尾部限制为父视图的尾部 - 填充
然后,限制每个视图的宽度等于前一个视图的宽度。
这是一个简单的例子:
class AndyViewController: UIViewController {
let numEvents = 1
override func viewDidLoad() {
super.viewDidLoad()
// a var to track the previously added view
var prevView: UIView!
var startPoint: CGFloat = 100
// create a couple eventViews
for i in 0..<numEvents {
let thisView = eventView()
view.addSubview(thisView)
// set top constraint
thisView.topAnchor.constraint(equalTo: view.topAnchor, constant: startPoint).isActive = true
// let's pretend each view is 20-pts "later"
startPoint += 20.0
// for now, make them all 50-pts tall
thisView.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// if it's the first view
if i == 0 {
// constrain its leading to view leading + 30
thisView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30.0).isActive = true
} else {
// make sure prevView has been set
guard let pv = prevView else {
fatalError("Did something wrong!!!")
}
// start this view at the right-end of the previous view + 5
thisView.leadingAnchor.constraint(equalTo: prevView.trailingAnchor, constant: 5.0).isActive = true
// make this view width equal to previous view width
thisView.widthAnchor.constraint(equalTo: pv.widthAnchor).isActive = true
}
// if it's the last view
if i == numEvents - 1 {
// constrain its trailing to view trailing -30
thisView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
}
// set prevView to thisView
prevView = thisView
}
}
func eventView() -> UIView {
let v = UIView()
v.backgroundColor = UIColor.yellow.withAlphaComponent(0.8)
v.translatesAutoresizingMaskIntoConstraints = false
return v
}
}
结果,使用 1、2、3 或 4 个“事件”:
您应该能够根据自己的需要进行调整。
我一直在将一些代码从使用框架更改为使用自动布局,但遇到了一个小问题
作为背景,我一直在构建一个日历,我的问题出在我一直试图制作的日历的“日”视图中,以便它复制苹果的日历。
这个应用自 2018 年以来一直在我的硬盘中,所以有点过时了,但在 2018 年它做了以下事情:
通过创建 EventContainerView
并将其放置在 TableView
上(不是作为单元格)来显示 Events
。
同时存在的事件会缩短它们的宽度以防止容器视图重叠。
这最后一部分是我的问题所在。
我通过获取 tableView 的宽度,将其除以重叠事件的数量,然后将其乘以一个 shiftBy
变量来将事件向右移动来做到这一点
我这辈子都找不到用 autoLayout 实现它的方法。
自动布局之前
func drawEvent(_ event:Event, overlaps:Int, shiftBy:Int) -> EventContainerView{
....
....
let eventWidth = (tableView.frame.width - 30) / CGFloat(overlaps)
let shift = eventWidth * CGFloat(shiftBy)
var frame: CGRect
if(shiftBy > 0){
frame = CGRect(x: CGFloat(30 + (5*shiftBy)) + shift, y: startPoint, width: eventWidth, height: endpoint)
}else{
frame = CGRect(x: CGFloat(30) + shift, y: startPoint, width: eventWidth, height: endpoint)
}
自动布局后
func drawEvent(_ event:Event, overlaps:Int, shiftBy:Int) -> EventContainerView{
....
....
let eventView = EventContainerView(forEvent: event, today: self)
eventView.translatesAutoresizingMaskIntoConstraints = false
var left: NSLayoutConstraint = eventView.leftAnchor.constraint(equalTo: tableView.leftAnchor, constant: +20)
if(shiftBy>0){
left = ????
}
tableView.addSubview(eventView)
let layout = [eventView.topAnchor.constraint(equalTo: tableView.topAnchor, constant: startPoint),
left,
eventView.heightAnchor.constraint(equalToConstant: endpoint),
eventView.widthAnchor.constraint(equalTo: tableView.widthAnchor, multiplier: 1/CGFloat(overlaps), constant: -30)]
NSLayoutConstraint.activate(layout)
return eventView
任何人都可以就我如何实现这段代码提出任何建议吗?
谢谢
编辑
这就是我想要实现的目标(我在 macOS 的日历上使用了它,但它仍然清楚地显示了我希望日历事件如何显示)
这就是我所拥有的(15 到 16 之间的深黄色是堆叠在“测试一切”事件下方的另一个事件。我需要其中一个转移(以及可能会或可能不会发生的任何其他事件)被添加到同一时间范围)
许多不同的方法来解决这个问题,但要使其接近您正在做的事情...
您可以创建跨越宽度的等宽视图,使用它们之间的固定间距和每边的特定“填充”,方法是:
- 将第一个视图的行距限制为父视图的行距+填充
- 将每个连续视图的前导限制为前一个视图的尾随 + 间距
- 将最后一个视图的尾部限制为父视图的尾部 - 填充
然后,限制每个视图的宽度等于前一个视图的宽度。
这是一个简单的例子:
class AndyViewController: UIViewController {
let numEvents = 1
override func viewDidLoad() {
super.viewDidLoad()
// a var to track the previously added view
var prevView: UIView!
var startPoint: CGFloat = 100
// create a couple eventViews
for i in 0..<numEvents {
let thisView = eventView()
view.addSubview(thisView)
// set top constraint
thisView.topAnchor.constraint(equalTo: view.topAnchor, constant: startPoint).isActive = true
// let's pretend each view is 20-pts "later"
startPoint += 20.0
// for now, make them all 50-pts tall
thisView.heightAnchor.constraint(equalToConstant: 50.0).isActive = true
// if it's the first view
if i == 0 {
// constrain its leading to view leading + 30
thisView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30.0).isActive = true
} else {
// make sure prevView has been set
guard let pv = prevView else {
fatalError("Did something wrong!!!")
}
// start this view at the right-end of the previous view + 5
thisView.leadingAnchor.constraint(equalTo: prevView.trailingAnchor, constant: 5.0).isActive = true
// make this view width equal to previous view width
thisView.widthAnchor.constraint(equalTo: pv.widthAnchor).isActive = true
}
// if it's the last view
if i == numEvents - 1 {
// constrain its trailing to view trailing -30
thisView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
}
// set prevView to thisView
prevView = thisView
}
}
func eventView() -> UIView {
let v = UIView()
v.backgroundColor = UIColor.yellow.withAlphaComponent(0.8)
v.translatesAutoresizingMaskIntoConstraints = false
return v
}
}
结果,使用 1、2、3 或 4 个“事件”:
您应该能够根据自己的需要进行调整。