UITableview Header 使用 VSL 的水平和垂直约束
UITableview Header Horizontal and Vertical constraints using VSL
我有一个带有标签和按钮的 header 视图。我正在添加这样的约束:
//create a view, button and label
view.addSubview(label)
view.addSubview(button)
label.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
let views = ["label": label, "button": button, "view": view]
let horizontallayoutContraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-19-[label]-60-[button]-22-|", options: .alignAllCenterY, metrics: nil, views: views)
view.addConstraints(horizontallayoutContraints)
let verticalLayoutContraint = NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)
view.addConstraint(verticalLayoutContraint)
return view
这非常有效,但现在我想添加一个跨越标签和按钮上方宽度的分隔视图。像这样:
let frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: 10)
let divView = UIView(frame: frame)
divView.backgroundColor = UIColor.lightGray
我似乎无法弄清楚实现这一目标的约束组合。基本上我希望 divView
跨越 tableview 的宽度,现有视图位于其下方。理想情况下,我可以这样嵌套它:
V:|[divView]-20-[H:|-19-[label]-60-[button]-22-|]-20-|
有没有专家可以帮我解决这个问题?我可以制作一个 NIB,但我更愿意以编程方式进行。
诚然,我对视觉格式语言所做的工作非常非常少,但据我所知,您不能以这种方式 nest。
具体取决于您尝试获得的最终结果,以及可能添加到此视图的其他内容(标签?图像?等),您可能找到它使用一个 UIStackView
或两个
更容易
但是,这里是一个使用 VFL 的示例...这将 运行 在 Playground 中保持原样,因此很容易进行调整以查看效果。另请注意,我采用两种方式 - 将标签和按钮与分隔线对齐,或将分隔线与标签和按钮对齐。评论和 if
块应该是不言自明的。这些颜色只是为了便于查看元素的框架结束的位置。
import UIKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 600, height: 600))
container.backgroundColor = UIColor.green
PlaygroundPage.current.liveView = container
// at this point, we have a 600 x 600 green square to use as a playground "canvas"
// create a 400x100 view at x: 40 , y: 40 as a "header view"
let headerView = UIView(frame: CGRect(x: 40, y: 40, width: 400, height: 100))
headerView.backgroundColor = UIColor.blue
// add the Header View to our "main container view"
container.addSubview(headerView)
var label: UILabel = {
let l = UILabel()
l.backgroundColor = UIColor.yellow
l.text = "The Label"
return l
}()
var button: UIButton = {
let l = UIButton()
l.backgroundColor = UIColor.red
l.setTitle("The Button", for: .normal)
return l
}()
var divView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.lightGray
return v
}()
headerView.addSubview(divView)
headerView.addSubview(label)
headerView.addSubview(button)
divView.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
var vcs: [NSLayoutConstraint]
var views = ["divView": divView, "label": label, "button": button, "headerView": headerView]
let bAlignToDivider = true
if bAlignToDivider {
// use the width of the divView to control the left/right edges of the label and button
// V: pin divView to the top, with a height of 10
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:|[divView(10)]", options: [], metrics: nil, views: views)
headerView.addConstraints(vcs)
// H: pin divView 20 from the left, and 20 from the right
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:|-20-[divView]-20-|", options: [], metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin label to bottom of divView (plus spacing of 8)
// using .alignAllLeft will pin the label's left to the divView's left
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-8-[label]", options: .alignAllLeft, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin button to bottom of divView (plus spacing of 8)
// using .alignAllRight will pin the button's right to the divView's right
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-8-[button]", options: .alignAllRight, metrics: nil, views: views)
headerView.addConstraints(vcs)
// H: add ">=0" spacing between label and button, so they use intrinsic widths
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:[label]-(>=0)-[button]", options: .alignAllCenterY, metrics: nil, views: views)
headerView.addConstraints(vcs)
}
else
{
// use left/right edges of the label and button to control the width of the divView
// H: pin label 20 from left
// pin button 20 from right
// use ">=0" spacing between label and button, so they use intrinsic widths
// also use .alignAllCenterY to vertically align them
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:|-20-[label]-(>=0)-[button]-20-|", options: .alignAllCenterY, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin divView to the top, with a height of 10
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:|[divView(10)]", options: [], metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin label to bottom of divView (plus spacing of 8)
// using .alignAllLeft will pin the divView's left to the label's left
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-8-[label]", options: .alignAllLeft, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin button to bottom of divView (plus spacing of 8)
// using .alignAllRight will pin the divView's right to the button's right
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-8-[button]", options: .alignAllRight, metrics: nil, views: views)
headerView.addConstraints(vcs)
}
编辑: 这是另一种变体。
这一次,"header view" 将只有 x,y 位置设置...其宽度和高度将由其内容自动确定。
灰色 "div" 视图的位置和宽度将通过将其约束到标签和按钮来控制,这将使用您指定的值:
"H:|-19-[label]-60-[button]-22-|"
同样,您可以 copy/paste 将其放入 playground 页面...
import UIKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 600, height: 600))
container.backgroundColor = UIColor.green
PlaygroundPage.current.liveView = container
// at this point, we have a 600 x 600 green square to use as a playground "canvas"
var label: UILabel = {
let l = UILabel()
l.backgroundColor = UIColor.yellow
l.text = "This is a longer Label"
return l
}()
var button: UIButton = {
let l = UIButton()
l.backgroundColor = UIColor.red
l.setTitle("The Button", for: .normal)
return l
}()
var divView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.lightGray
return v
}()
var headerView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.blue
return v
}()
// add our header view
container.addSubview(headerView)
// add div, label and button as subviews in headerView
headerView.addSubview(divView)
headerView.addSubview(label)
headerView.addSubview(button)
// disable Autoresizing Masks
headerView.translatesAutoresizingMaskIntoConstraints = false
divView.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
var vcs: [NSLayoutConstraint]
var views = ["divView": divView, "label": label, "button": button, "headerView": headerView]
// init "header view" - we'll let its contents determine its width and height
// these two formats will simply put the header view at 20,20
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:|-20-[headerView]", options: [], metrics: nil, views: views)
container.addConstraints(vcs)
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:|-20-[headerView]", options: [], metrics: nil, views: views)
container.addConstraints(vcs)
// H: pin label 19 from left
// pin button 22 from right
// use 60 spacing between label and button
// width of label and button auto-determined by text
// also use .alignAllCenterY to vertically align them
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:|-19-[label]-60-[button]-22-|", options: .alignAllCenterY, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin divView to the top, with a height of 10
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:|[divView(10)]", options: [], metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin label to bottom of divView (plus spacing of 20)
// using .alignAllLeft will pin the divView's left to the label's left
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-20-[label]", options: .alignAllLeft, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin button to bottom of divView (plus spacing of 20)
// using .alignAllRight will pin the divView's right to the button's right
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-20-[button]|", options: .alignAllRight, metrics: nil, views: views)
headerView.addConstraints(vcs)
我有一个带有标签和按钮的 header 视图。我正在添加这样的约束:
//create a view, button and label
view.addSubview(label)
view.addSubview(button)
label.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
let views = ["label": label, "button": button, "view": view]
let horizontallayoutContraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-19-[label]-60-[button]-22-|", options: .alignAllCenterY, metrics: nil, views: views)
view.addConstraints(horizontallayoutContraints)
let verticalLayoutContraint = NSLayoutConstraint(item: label, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)
view.addConstraint(verticalLayoutContraint)
return view
这非常有效,但现在我想添加一个跨越标签和按钮上方宽度的分隔视图。像这样:
let frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: 10)
let divView = UIView(frame: frame)
divView.backgroundColor = UIColor.lightGray
我似乎无法弄清楚实现这一目标的约束组合。基本上我希望 divView
跨越 tableview 的宽度,现有视图位于其下方。理想情况下,我可以这样嵌套它:
V:|[divView]-20-[H:|-19-[label]-60-[button]-22-|]-20-|
有没有专家可以帮我解决这个问题?我可以制作一个 NIB,但我更愿意以编程方式进行。
诚然,我对视觉格式语言所做的工作非常非常少,但据我所知,您不能以这种方式 nest。
具体取决于您尝试获得的最终结果,以及可能添加到此视图的其他内容(标签?图像?等),您可能找到它使用一个 UIStackView
或两个
但是,这里是一个使用 VFL 的示例...这将 运行 在 Playground 中保持原样,因此很容易进行调整以查看效果。另请注意,我采用两种方式 - 将标签和按钮与分隔线对齐,或将分隔线与标签和按钮对齐。评论和 if
块应该是不言自明的。这些颜色只是为了便于查看元素的框架结束的位置。
import UIKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 600, height: 600))
container.backgroundColor = UIColor.green
PlaygroundPage.current.liveView = container
// at this point, we have a 600 x 600 green square to use as a playground "canvas"
// create a 400x100 view at x: 40 , y: 40 as a "header view"
let headerView = UIView(frame: CGRect(x: 40, y: 40, width: 400, height: 100))
headerView.backgroundColor = UIColor.blue
// add the Header View to our "main container view"
container.addSubview(headerView)
var label: UILabel = {
let l = UILabel()
l.backgroundColor = UIColor.yellow
l.text = "The Label"
return l
}()
var button: UIButton = {
let l = UIButton()
l.backgroundColor = UIColor.red
l.setTitle("The Button", for: .normal)
return l
}()
var divView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.lightGray
return v
}()
headerView.addSubview(divView)
headerView.addSubview(label)
headerView.addSubview(button)
divView.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
var vcs: [NSLayoutConstraint]
var views = ["divView": divView, "label": label, "button": button, "headerView": headerView]
let bAlignToDivider = true
if bAlignToDivider {
// use the width of the divView to control the left/right edges of the label and button
// V: pin divView to the top, with a height of 10
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:|[divView(10)]", options: [], metrics: nil, views: views)
headerView.addConstraints(vcs)
// H: pin divView 20 from the left, and 20 from the right
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:|-20-[divView]-20-|", options: [], metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin label to bottom of divView (plus spacing of 8)
// using .alignAllLeft will pin the label's left to the divView's left
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-8-[label]", options: .alignAllLeft, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin button to bottom of divView (plus spacing of 8)
// using .alignAllRight will pin the button's right to the divView's right
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-8-[button]", options: .alignAllRight, metrics: nil, views: views)
headerView.addConstraints(vcs)
// H: add ">=0" spacing between label and button, so they use intrinsic widths
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:[label]-(>=0)-[button]", options: .alignAllCenterY, metrics: nil, views: views)
headerView.addConstraints(vcs)
}
else
{
// use left/right edges of the label and button to control the width of the divView
// H: pin label 20 from left
// pin button 20 from right
// use ">=0" spacing between label and button, so they use intrinsic widths
// also use .alignAllCenterY to vertically align them
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:|-20-[label]-(>=0)-[button]-20-|", options: .alignAllCenterY, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin divView to the top, with a height of 10
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:|[divView(10)]", options: [], metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin label to bottom of divView (plus spacing of 8)
// using .alignAllLeft will pin the divView's left to the label's left
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-8-[label]", options: .alignAllLeft, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin button to bottom of divView (plus spacing of 8)
// using .alignAllRight will pin the divView's right to the button's right
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-8-[button]", options: .alignAllRight, metrics: nil, views: views)
headerView.addConstraints(vcs)
}
编辑: 这是另一种变体。
这一次,"header view" 将只有 x,y 位置设置...其宽度和高度将由其内容自动确定。
灰色 "div" 视图的位置和宽度将通过将其约束到标签和按钮来控制,这将使用您指定的值:
"H:|-19-[label]-60-[button]-22-|"
同样,您可以 copy/paste 将其放入 playground 页面...
import UIKit
import PlaygroundSupport
let container = UIView(frame: CGRect(x: 0, y: 0, width: 600, height: 600))
container.backgroundColor = UIColor.green
PlaygroundPage.current.liveView = container
// at this point, we have a 600 x 600 green square to use as a playground "canvas"
var label: UILabel = {
let l = UILabel()
l.backgroundColor = UIColor.yellow
l.text = "This is a longer Label"
return l
}()
var button: UIButton = {
let l = UIButton()
l.backgroundColor = UIColor.red
l.setTitle("The Button", for: .normal)
return l
}()
var divView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.lightGray
return v
}()
var headerView: UIView = {
let v = UIView()
v.backgroundColor = UIColor.blue
return v
}()
// add our header view
container.addSubview(headerView)
// add div, label and button as subviews in headerView
headerView.addSubview(divView)
headerView.addSubview(label)
headerView.addSubview(button)
// disable Autoresizing Masks
headerView.translatesAutoresizingMaskIntoConstraints = false
divView.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
button.translatesAutoresizingMaskIntoConstraints = false
var vcs: [NSLayoutConstraint]
var views = ["divView": divView, "label": label, "button": button, "headerView": headerView]
// init "header view" - we'll let its contents determine its width and height
// these two formats will simply put the header view at 20,20
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:|-20-[headerView]", options: [], metrics: nil, views: views)
container.addConstraints(vcs)
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:|-20-[headerView]", options: [], metrics: nil, views: views)
container.addConstraints(vcs)
// H: pin label 19 from left
// pin button 22 from right
// use 60 spacing between label and button
// width of label and button auto-determined by text
// also use .alignAllCenterY to vertically align them
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"H:|-19-[label]-60-[button]-22-|", options: .alignAllCenterY, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin divView to the top, with a height of 10
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:|[divView(10)]", options: [], metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin label to bottom of divView (plus spacing of 20)
// using .alignAllLeft will pin the divView's left to the label's left
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-20-[label]", options: .alignAllLeft, metrics: nil, views: views)
headerView.addConstraints(vcs)
// V: pin button to bottom of divView (plus spacing of 20)
// using .alignAllRight will pin the divView's right to the button's right
vcs = NSLayoutConstraint.constraints(withVisualFormat:
"V:[divView]-20-[button]|", options: .alignAllRight, metrics: nil, views: views)
headerView.addConstraints(vcs)