iOS - 在堆栈视图中以编程方式添加垂直线
iOS - Add vertical line programatically inside a stack view
我正在尝试以编程方式在堆栈视图内的标签之间添加垂直线。
所需的完成将类似于此图像:
我可以添加标签,所有标签都具有所需的间距;我可以添加水平线,但我不知道如何在中间添加那些分隔符垂直线。
我想这样做:
let stackView = UIStackView(arrangedSubviews: [label1, verticalLine, label2, verticalLine, label3])
有什么提示吗?
谢谢
您可以尝试以下方法。
- 首先获取一个 UIView 并将 UIStackView 的相同约束应用于此 UIView。
- 将此 UIView 的背景色设为黑色(线条的颜色)
- 现在获取一个 UIStackView 并将其添加为上述 UIView 的子项。
- 添加 UIStackView 的约束,即将其绑定到父 UIView 的所有边缘。
- 现在将 UIStackView 的背景颜色设置为 Clear Color。
- 将 UIStackView 的间距设置为 1 或 2(线条的宽度)
- 现在将 3 个标签添加到堆栈视图中。
- 确保标签的背景颜色为白色,文本颜色为黑色。
至此,您将获得所需的场景。请参阅这些图片以供参考。
您不能在两个地方使用同一个视图,因此您需要创建两个单独的垂直线视图。您需要像这样配置每个垂直线视图:
- 设置它的背景颜色。
- 将其宽度限制为 1(这样您得到的是一条线,而不是矩形)。
- 限制它的高度(这样它就不会拉伸到堆栈视图的整个高度)。
因此,将标签一次一个地添加到堆栈视图中,并在将每个标签添加到堆栈视图之前执行如下操作:
if stackView.arrangedSubviews.count > 0 {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = .black
stackView.addArrangedSubview(separator)
separator.heightAnchor.constraint(equalTo: stackView.heightAnchor, multiplier: 0.6).isActive = true
}
请注意,您不希望垂直线与标签的宽度相同,因此您必须不设置distribution
属性 的堆栈视图到 fillEqually
。相反,如果您希望所有标签具有相同的宽度,则必须自己在标签之间创建宽度约束。例如,添加每个新标签后,执行此操作:
if let firstLabel = stackView.arrangedSubviews.first as? UILabel {
label.widthAnchor.constraint(equalTo: firstLabel.widthAnchor).isActive = true
}
结果:
完整的 playground 代码(由 Federico Zanetello 更新至 Swift 4.1):
import UIKit
import PlaygroundSupport
extension UIFont {
var withSmallCaps: UIFont {
let feature: [UIFontDescriptor.FeatureKey: Any] = [
UIFontDescriptor.FeatureKey.featureIdentifier: kLowerCaseType,
UIFontDescriptor.FeatureKey.typeIdentifier: kLowerCaseSmallCapsSelector]
let attributes: [UIFontDescriptor.AttributeName: Any] = [UIFontDescriptor.AttributeName.featureSettings: [feature]]
let descriptor = self.fontDescriptor.addingAttributes(attributes)
return UIFont(descriptor: descriptor, size: pointSize)
}
}
let rootView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
rootView.backgroundColor = .white
PlaygroundPage.current.liveView = rootView
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.alignment = .center
stackView.frame = rootView.bounds
rootView.addSubview(stackView)
typealias Item = (name: String, value: Int)
let items: [Item] = [
Item(name: "posts", value: 135),
Item(name: "followers", value: 6347),
Item(name: "following", value: 328),
]
let valueStyle: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 12).withSmallCaps]
let nameStyle: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12).withSmallCaps,
NSAttributedStringKey.foregroundColor: UIColor.darkGray]
let valueFormatter = NumberFormatter()
valueFormatter.numberStyle = .decimal
for item in items {
if stackView.arrangedSubviews.count > 0 {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = .black
stackView.addArrangedSubview(separator)
separator.heightAnchor.constraint(equalTo: stackView.heightAnchor, multiplier: 0.4).isActive = true
}
let richText = NSMutableAttributedString()
let valueString = valueFormatter.string(for: item.value)!
richText.append(NSAttributedString(string: valueString, attributes: valueStyle))
richText.append(NSAttributedString(string: "\n" + item.name, attributes: nameStyle))
let label = UILabel()
label.attributedText = richText
label.textAlignment = .center
label.numberOfLines = 0
stackView.addArrangedSubview(label)
if let firstLabel = stackView.arrangedSubviews.first as? UILabel {
label.widthAnchor.constraint(equalTo: firstLabel.widthAnchor).isActive = true
}
}
UIGraphicsBeginImageContextWithOptions(rootView.bounds.size, true, 1)
rootView.drawHierarchy(in: rootView.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let png = UIImagePNGRepresentation(image)!
let path = NSTemporaryDirectory() + "/image.png"
Swift.print(path)
try! png.write(to: URL(fileURLWithPath: path))
这里有一个更灵活的 UIStackView
子类,支持任意添加已排列的子视图,适用于那些需要在其 UIStackView
和子视图上放置清晰背景的人 UIVisualEffectView
,如下图
import UIKit
@IBDesignable class SeparatorStackView: UIStackView {
@IBInspectable var separatorColor: UIColor? = .black {
didSet {
invalidateSeparators()
}
}
@IBInspectable var separatorWidth: CGFloat = 0.5 {
didSet {
invalidateSeparators()
}
}
@IBInspectable private var separatorTopPadding: CGFloat = 0 {
didSet {
separatorInsets.top = separatorTopPadding
}
}
@IBInspectable private var separatorBottomPadding: CGFloat = 0 {
didSet {
separatorInsets.bottom = separatorBottomPadding
}
}
@IBInspectable private var separatorLeftPadding: CGFloat = 0 {
didSet {
separatorInsets.left = separatorLeftPadding
}
}
@IBInspectable private var separatorRightPadding: CGFloat = 0 {
didSet {
separatorInsets.right = separatorRightPadding
}
}
var separatorInsets: UIEdgeInsets = .zero {
didSet {
invalidateSeparators()
}
}
private var separators: [UIView] = []
override func layoutSubviews() {
super.layoutSubviews()
invalidateSeparators()
}
override func awakeFromNib() {
super.awakeFromNib()
invalidateSeparators()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
invalidateSeparators()
}
private func invalidateSeparators() {
guard arrangedSubviews.count > 1 else {
separators.forEach({[=10=].removeFromSuperview()})
separators.removeAll()
return
}
if separators.count > arrangedSubviews.count {
separators.removeLast(separators.count - arrangedSubviews.count)
} else if separators.count < arrangedSubviews.count {
separators += Array<UIView>(repeating: UIView(), count: arrangedSubviews.count - separators.count)
}
separators.forEach({[=10=].backgroundColor = self.separatorColor; self.addSubview([=10=])})
for (index, subview) in arrangedSubviews.enumerated() where arrangedSubviews.count >= index + 2 {
let nextSubview = arrangedSubviews[index + 1]
let separator = separators[index]
let origin: CGPoint
let size: CGSize
if axis == .horizontal {
let originX = (nextSubview.frame.maxX - subview.frame.minX)/2 + separatorInsets.left - separatorInsets.right
origin = CGPoint(x: originX, y: separatorInsets.top)
let height = frame.height - separatorInsets.bottom - separatorInsets.top
size = CGSize(width: separatorWidth, height: height)
} else {
let originY = (nextSubview.frame.maxY - subview.frame.minY)/2 + separatorInsets.top - separatorInsets.bottom
origin = CGPoint(x: separatorInsets.left, y: originY)
let width = frame.width - separatorInsets.left - separatorInsets.right
size = CGSize(width: width, height: separatorWidth)
}
separator.frame = CGRect(origin: origin, size: size)
}
}
}
结果?
如果竖线字符“|”适合您想要的外观,那么您可以将标签添加到堆栈视图中您需要分隔线的位置。然后使用:
myStackView.distribution = .equalSpacing
您还可以在 Interface Builder 中更改 Stack View Distribution。
这是一个简单的扩展,用于在每行之间添加分隔符(注意!行,而不是所要求的列!对于这种情况也很容易修改)。与接受的答案基本相同,但采用可重复使用的格式。
通过调用例如
yourStackViewObjectInstance.addHorizontalSeparators(color : .black)
分机:
extension UIStackView {
func addHorizontalSeparators(color : UIColor) {
var i = self.arrangedSubviews.count
while i >= 0 {
let separator = createSeparator(color: color)
insertArrangedSubview(separator, at: i)
separator.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1).isActive = true
i -= 1
}
}
private func createSeparator(color : UIColor) -> UIView {
let separator = UIView()
separator.heightAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
}
@GOR 答案扩展仅适用于垂直线且仅位于中心
Stackview 设置:设置每个子视图和父 stackview 的宽度约束应填充
这是在每行之间添加垂直分隔符的简单扩展。
func addVerticalSeparators(color : UIColor) {
var i = self.arrangedSubviews.count
while i > 1 {
let separator = verticalCreateSeparator(color: color)
insertArrangedSubview(separator, at: i-1) // (i-1) for centers only
separator.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 1).isActive = true
i -= 1
}
}
private func verticalCreateSeparator(color : UIColor) -> UIView {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
@mark-bourke 的回答仅适用于单个分隔符。我修复了多个分隔符的 invalidateSeparators()
方法。我没有用水平堆栈视图测试它,但它适用于垂直视图:
private func invalidateSeparators() {
guard arrangedSubviews.count > 1 else {
separators.forEach({[=10=].removeFromSuperview()})
separators.removeAll()
return
}
if separators.count > arrangedSubviews.count {
separators.removeLast(separators.count - arrangedSubviews.count)
} else if separators.count < arrangedSubviews.count {
for _ in 0..<(arrangedSubviews.count - separators.count - 1) {
separators.append(UIView())
}
}
separators.forEach({[=10=].backgroundColor = self.separatorColor; self.addSubview([=10=])})
for (index, subview) in arrangedSubviews.enumerated() where arrangedSubviews.count >= index + 2 {
let nextSubview = arrangedSubviews[index + 1]
let separator = separators[index]
let origin: CGPoint
let size: CGSize
if axis == .horizontal {
let originX = subview.frame.maxX + (nextSubview.frame.minX - subview.frame.maxX) / 2.0 + separatorInsets.left - separatorInsets.right
origin = CGPoint(x: originX, y: separatorInsets.top)
let height = frame.height - separatorInsets.bottom - separatorInsets.top
size = CGSize(width: separatorWidth, height: height)
} else {
let originY = subview.frame.maxY + (nextSubview.frame.minY - subview.frame.maxY) / 2.0 + separatorInsets.top - separatorInsets.bottom
origin = CGPoint(x: separatorInsets.left, y: originY)
let width = frame.width - separatorInsets.left - separatorInsets.right
size = CGSize(width: width, height: separatorWidth)
}
separator.frame = CGRect(origin: origin, size: size)
separator.isHidden = nextSubview.isHidden
}
}
一个很好的 cocoa-pod,可以很好地完成工作。
它使用 Swizzeling,编码良好。
https://github.com/barisatamer/StackViewSeparator
我对@FrankByte.com的回答投了赞成票,因为他帮助我找到了我的解决方案,这与 OP 想要做的非常相似:
extension UIStackView {
func addVerticalSeparators(color : UIColor, multiplier: CGFloat = 0.5) {
var i = self.arrangedSubviews.count - 1
while i > 0 {
let separator = createSeparator(color: color)
insertArrangedSubview(separator, at: i)
separator.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: multiplier).isActive = true
i -= 1
}
}
private func createSeparator(color: UIColor) -> UIView {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
}
您可以使用界面生成器来执行此操作。将父UIStackview的分布设置为Equal Centering,并在父UIStackView的开头和结尾添加两个宽度为0的UIView。然后在中间添加一个宽度为1的UIView,并设置UIView的背景色。
@OwlOCR 解决方案的另一个版本,如果我们不希望在开头和结尾处使用分隔符。
extension UIStackView {
func addHorizontalSeparators(color : UIColor) {
let separatorsToAdd = self.arrangedSubviews.count - 1
var insertAt = 1
for _ in 1...separatorsToAdd {
let separator = createSeparator(color: color)
insertArrangedSubview(separator, at: insertAt)
insertAt += 2
separator.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 1).isActive = true
}
}
private func createSeparator(color : UIColor) -> UIView {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
}
我的解决方法很简单。您可以在 UIStackView 中的轴 属性 上使用 switch 语句来检查当前正在使用哪个轴,而不是为垂直轴和水平轴创建方法。
要添加分隔符,只需添加此扩展名并传递分隔符应放置的位置和颜色即可。
extension UIStackView {
func addSeparators(at positions: [Int], color: UIColor) {
for position in positions {
let separator = UIView()
separator.backgroundColor = color
insertArrangedSubview(separator, at: position)
switch self.axis {
case .horizontal:
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 1).isActive = true
case .vertical:
separator.heightAnchor.constraint(equalToConstant: 1).isActive = true
separator.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1).isActive = true
@unknown default:
fatalError("Unknown UIStackView axis value.")
}
}
}
}
示例用例:
stackView.addSeparators(at: [2], color: .black)
我正在尝试以编程方式在堆栈视图内的标签之间添加垂直线。
所需的完成将类似于此图像:
我可以添加标签,所有标签都具有所需的间距;我可以添加水平线,但我不知道如何在中间添加那些分隔符垂直线。
我想这样做:
let stackView = UIStackView(arrangedSubviews: [label1, verticalLine, label2, verticalLine, label3])
有什么提示吗?
谢谢
您可以尝试以下方法。
- 首先获取一个 UIView 并将 UIStackView 的相同约束应用于此 UIView。
- 将此 UIView 的背景色设为黑色(线条的颜色)
- 现在获取一个 UIStackView 并将其添加为上述 UIView 的子项。
- 添加 UIStackView 的约束,即将其绑定到父 UIView 的所有边缘。
- 现在将 UIStackView 的背景颜色设置为 Clear Color。
- 将 UIStackView 的间距设置为 1 或 2(线条的宽度)
- 现在将 3 个标签添加到堆栈视图中。
- 确保标签的背景颜色为白色,文本颜色为黑色。
至此,您将获得所需的场景。请参阅这些图片以供参考。
您不能在两个地方使用同一个视图,因此您需要创建两个单独的垂直线视图。您需要像这样配置每个垂直线视图:
- 设置它的背景颜色。
- 将其宽度限制为 1(这样您得到的是一条线,而不是矩形)。
- 限制它的高度(这样它就不会拉伸到堆栈视图的整个高度)。
因此,将标签一次一个地添加到堆栈视图中,并在将每个标签添加到堆栈视图之前执行如下操作:
if stackView.arrangedSubviews.count > 0 {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = .black
stackView.addArrangedSubview(separator)
separator.heightAnchor.constraint(equalTo: stackView.heightAnchor, multiplier: 0.6).isActive = true
}
请注意,您不希望垂直线与标签的宽度相同,因此您必须不设置distribution
属性 的堆栈视图到 fillEqually
。相反,如果您希望所有标签具有相同的宽度,则必须自己在标签之间创建宽度约束。例如,添加每个新标签后,执行此操作:
if let firstLabel = stackView.arrangedSubviews.first as? UILabel {
label.widthAnchor.constraint(equalTo: firstLabel.widthAnchor).isActive = true
}
结果:
完整的 playground 代码(由 Federico Zanetello 更新至 Swift 4.1):
import UIKit
import PlaygroundSupport
extension UIFont {
var withSmallCaps: UIFont {
let feature: [UIFontDescriptor.FeatureKey: Any] = [
UIFontDescriptor.FeatureKey.featureIdentifier: kLowerCaseType,
UIFontDescriptor.FeatureKey.typeIdentifier: kLowerCaseSmallCapsSelector]
let attributes: [UIFontDescriptor.AttributeName: Any] = [UIFontDescriptor.AttributeName.featureSettings: [feature]]
let descriptor = self.fontDescriptor.addingAttributes(attributes)
return UIFont(descriptor: descriptor, size: pointSize)
}
}
let rootView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
rootView.backgroundColor = .white
PlaygroundPage.current.liveView = rootView
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.alignment = .center
stackView.frame = rootView.bounds
rootView.addSubview(stackView)
typealias Item = (name: String, value: Int)
let items: [Item] = [
Item(name: "posts", value: 135),
Item(name: "followers", value: 6347),
Item(name: "following", value: 328),
]
let valueStyle: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 12).withSmallCaps]
let nameStyle: [NSAttributedStringKey: Any] = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 12).withSmallCaps,
NSAttributedStringKey.foregroundColor: UIColor.darkGray]
let valueFormatter = NumberFormatter()
valueFormatter.numberStyle = .decimal
for item in items {
if stackView.arrangedSubviews.count > 0 {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = .black
stackView.addArrangedSubview(separator)
separator.heightAnchor.constraint(equalTo: stackView.heightAnchor, multiplier: 0.4).isActive = true
}
let richText = NSMutableAttributedString()
let valueString = valueFormatter.string(for: item.value)!
richText.append(NSAttributedString(string: valueString, attributes: valueStyle))
richText.append(NSAttributedString(string: "\n" + item.name, attributes: nameStyle))
let label = UILabel()
label.attributedText = richText
label.textAlignment = .center
label.numberOfLines = 0
stackView.addArrangedSubview(label)
if let firstLabel = stackView.arrangedSubviews.first as? UILabel {
label.widthAnchor.constraint(equalTo: firstLabel.widthAnchor).isActive = true
}
}
UIGraphicsBeginImageContextWithOptions(rootView.bounds.size, true, 1)
rootView.drawHierarchy(in: rootView.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let png = UIImagePNGRepresentation(image)!
let path = NSTemporaryDirectory() + "/image.png"
Swift.print(path)
try! png.write(to: URL(fileURLWithPath: path))
这里有一个更灵活的 UIStackView
子类,支持任意添加已排列的子视图,适用于那些需要在其 UIStackView
和子视图上放置清晰背景的人 UIVisualEffectView
,如下图
import UIKit
@IBDesignable class SeparatorStackView: UIStackView {
@IBInspectable var separatorColor: UIColor? = .black {
didSet {
invalidateSeparators()
}
}
@IBInspectable var separatorWidth: CGFloat = 0.5 {
didSet {
invalidateSeparators()
}
}
@IBInspectable private var separatorTopPadding: CGFloat = 0 {
didSet {
separatorInsets.top = separatorTopPadding
}
}
@IBInspectable private var separatorBottomPadding: CGFloat = 0 {
didSet {
separatorInsets.bottom = separatorBottomPadding
}
}
@IBInspectable private var separatorLeftPadding: CGFloat = 0 {
didSet {
separatorInsets.left = separatorLeftPadding
}
}
@IBInspectable private var separatorRightPadding: CGFloat = 0 {
didSet {
separatorInsets.right = separatorRightPadding
}
}
var separatorInsets: UIEdgeInsets = .zero {
didSet {
invalidateSeparators()
}
}
private var separators: [UIView] = []
override func layoutSubviews() {
super.layoutSubviews()
invalidateSeparators()
}
override func awakeFromNib() {
super.awakeFromNib()
invalidateSeparators()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
invalidateSeparators()
}
private func invalidateSeparators() {
guard arrangedSubviews.count > 1 else {
separators.forEach({[=10=].removeFromSuperview()})
separators.removeAll()
return
}
if separators.count > arrangedSubviews.count {
separators.removeLast(separators.count - arrangedSubviews.count)
} else if separators.count < arrangedSubviews.count {
separators += Array<UIView>(repeating: UIView(), count: arrangedSubviews.count - separators.count)
}
separators.forEach({[=10=].backgroundColor = self.separatorColor; self.addSubview([=10=])})
for (index, subview) in arrangedSubviews.enumerated() where arrangedSubviews.count >= index + 2 {
let nextSubview = arrangedSubviews[index + 1]
let separator = separators[index]
let origin: CGPoint
let size: CGSize
if axis == .horizontal {
let originX = (nextSubview.frame.maxX - subview.frame.minX)/2 + separatorInsets.left - separatorInsets.right
origin = CGPoint(x: originX, y: separatorInsets.top)
let height = frame.height - separatorInsets.bottom - separatorInsets.top
size = CGSize(width: separatorWidth, height: height)
} else {
let originY = (nextSubview.frame.maxY - subview.frame.minY)/2 + separatorInsets.top - separatorInsets.bottom
origin = CGPoint(x: separatorInsets.left, y: originY)
let width = frame.width - separatorInsets.left - separatorInsets.right
size = CGSize(width: width, height: separatorWidth)
}
separator.frame = CGRect(origin: origin, size: size)
}
}
}
结果?
如果竖线字符“|”适合您想要的外观,那么您可以将标签添加到堆栈视图中您需要分隔线的位置。然后使用:
myStackView.distribution = .equalSpacing
您还可以在 Interface Builder 中更改 Stack View Distribution。
这是一个简单的扩展,用于在每行之间添加分隔符(注意!行,而不是所要求的列!对于这种情况也很容易修改)。与接受的答案基本相同,但采用可重复使用的格式。
通过调用例如
yourStackViewObjectInstance.addHorizontalSeparators(color : .black)
分机:
extension UIStackView {
func addHorizontalSeparators(color : UIColor) {
var i = self.arrangedSubviews.count
while i >= 0 {
let separator = createSeparator(color: color)
insertArrangedSubview(separator, at: i)
separator.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1).isActive = true
i -= 1
}
}
private func createSeparator(color : UIColor) -> UIView {
let separator = UIView()
separator.heightAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
}
@GOR 答案扩展仅适用于垂直线且仅位于中心
Stackview 设置:设置每个子视图和父 stackview 的宽度约束应填充
这是在每行之间添加垂直分隔符的简单扩展。
func addVerticalSeparators(color : UIColor) {
var i = self.arrangedSubviews.count
while i > 1 {
let separator = verticalCreateSeparator(color: color)
insertArrangedSubview(separator, at: i-1) // (i-1) for centers only
separator.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 1).isActive = true
i -= 1
}
}
private func verticalCreateSeparator(color : UIColor) -> UIView {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
@mark-bourke 的回答仅适用于单个分隔符。我修复了多个分隔符的 invalidateSeparators()
方法。我没有用水平堆栈视图测试它,但它适用于垂直视图:
private func invalidateSeparators() {
guard arrangedSubviews.count > 1 else {
separators.forEach({[=10=].removeFromSuperview()})
separators.removeAll()
return
}
if separators.count > arrangedSubviews.count {
separators.removeLast(separators.count - arrangedSubviews.count)
} else if separators.count < arrangedSubviews.count {
for _ in 0..<(arrangedSubviews.count - separators.count - 1) {
separators.append(UIView())
}
}
separators.forEach({[=10=].backgroundColor = self.separatorColor; self.addSubview([=10=])})
for (index, subview) in arrangedSubviews.enumerated() where arrangedSubviews.count >= index + 2 {
let nextSubview = arrangedSubviews[index + 1]
let separator = separators[index]
let origin: CGPoint
let size: CGSize
if axis == .horizontal {
let originX = subview.frame.maxX + (nextSubview.frame.minX - subview.frame.maxX) / 2.0 + separatorInsets.left - separatorInsets.right
origin = CGPoint(x: originX, y: separatorInsets.top)
let height = frame.height - separatorInsets.bottom - separatorInsets.top
size = CGSize(width: separatorWidth, height: height)
} else {
let originY = subview.frame.maxY + (nextSubview.frame.minY - subview.frame.maxY) / 2.0 + separatorInsets.top - separatorInsets.bottom
origin = CGPoint(x: separatorInsets.left, y: originY)
let width = frame.width - separatorInsets.left - separatorInsets.right
size = CGSize(width: width, height: separatorWidth)
}
separator.frame = CGRect(origin: origin, size: size)
separator.isHidden = nextSubview.isHidden
}
}
一个很好的 cocoa-pod,可以很好地完成工作。 它使用 Swizzeling,编码良好。
https://github.com/barisatamer/StackViewSeparator
我对@FrankByte.com的回答投了赞成票,因为他帮助我找到了我的解决方案,这与 OP 想要做的非常相似:
extension UIStackView {
func addVerticalSeparators(color : UIColor, multiplier: CGFloat = 0.5) {
var i = self.arrangedSubviews.count - 1
while i > 0 {
let separator = createSeparator(color: color)
insertArrangedSubview(separator, at: i)
separator.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: multiplier).isActive = true
i -= 1
}
}
private func createSeparator(color: UIColor) -> UIView {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
}
您可以使用界面生成器来执行此操作。将父UIStackview的分布设置为Equal Centering,并在父UIStackView的开头和结尾添加两个宽度为0的UIView。然后在中间添加一个宽度为1的UIView,并设置UIView的背景色。
@OwlOCR 解决方案的另一个版本,如果我们不希望在开头和结尾处使用分隔符。
extension UIStackView {
func addHorizontalSeparators(color : UIColor) {
let separatorsToAdd = self.arrangedSubviews.count - 1
var insertAt = 1
for _ in 1...separatorsToAdd {
let separator = createSeparator(color: color)
insertArrangedSubview(separator, at: insertAt)
insertAt += 2
separator.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 1).isActive = true
}
}
private func createSeparator(color : UIColor) -> UIView {
let separator = UIView()
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.backgroundColor = color
return separator
}
}
我的解决方法很简单。您可以在 UIStackView 中的轴 属性 上使用 switch 语句来检查当前正在使用哪个轴,而不是为垂直轴和水平轴创建方法。
要添加分隔符,只需添加此扩展名并传递分隔符应放置的位置和颜色即可。
extension UIStackView {
func addSeparators(at positions: [Int], color: UIColor) {
for position in positions {
let separator = UIView()
separator.backgroundColor = color
insertArrangedSubview(separator, at: position)
switch self.axis {
case .horizontal:
separator.widthAnchor.constraint(equalToConstant: 1).isActive = true
separator.heightAnchor.constraint(equalTo: self.heightAnchor, multiplier: 1).isActive = true
case .vertical:
separator.heightAnchor.constraint(equalToConstant: 1).isActive = true
separator.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1).isActive = true
@unknown default:
fatalError("Unknown UIStackView axis value.")
}
}
}
}
示例用例:
stackView.addSeparators(at: [2], color: .black)