如何自定义带按钮间隙的UISegmentedControl?
自定义带间隙的UISegmentedControl实现方案
需求说明
需要实现如图所示的自定义分段控制器,要求按钮之间存在明显间隙,现有系统UISegmentedControl代码无法达到该效果。
效果截图:
尝试的初始代码:
let items = ["Dialy", "Weekly", "Monthly"] lazy var segmentControl: UISegmentedControl = { let control = UISegmentedControl(items: items) control.selectedSegmentIndex = 0 control.layer.cornerRadius = 9 control.layer.borderWidth = 1 control.layer.masksToBounds = true control.layer.borderColor = UIColor.white.cgColor control.addTarget(self, action: #selector(handleSegmentControl(sender:)), for:.touchUpInside) return control }()
可行解决方案
方案一:改造系统UISegmentedControl
通过修改系统控件的分割线、背景图来模拟间隙效果,步骤如下:
- 隐藏默认分割线
- 给每个分段设置带内边距的背景图,实现视觉上的间隙
- 自定义选中/未选中状态的样式
完整代码:
lazy var customSegmentControl: UISegmentedControl = { let items = ["Daily", "Weekly", "Monthly"] let control = UISegmentedControl(items: items) // 基础样式配置 control.selectedSegmentIndex = 0 control.layer.cornerRadius = 9 control.layer.borderWidth = 1 control.layer.borderColor = UIColor.white.cgColor control.layer.masksToBounds = true control.addTarget(self, action: #selector(handleSegmentControl(sender:)), for: .touchUpInside) // 隐藏默认分割线 control.setDividerImage(UIImage(), forLeftSegmentState: .normal, rightSegmentState: .normal, barMetrics: .default) // 生成未选中状态背景图(带左右内边距,模拟间隙) let normalBg = createSegmentBg(color: .clear, cornerRadius: 9, margin: UIEdgeInsets(top: 2, left: 4, bottom: 2, right: 4)) control.setBackgroundImage(normalBg, for: .normal, barMetrics: .default) // 生成选中状态背景图 let selectedBg = createSegmentBg(color: .white, cornerRadius: 9, margin: UIEdgeInsets(top: 2, left: 4, bottom: 2, right: 4)) control.setBackgroundImage(selectedBg, for: .selected, barMetrics: .default) // 设置文字颜色 control.setTitleTextAttributes([.foregroundColor: UIColor.white], for: .normal) control.setTitleTextAttributes([.foregroundColor: UIColor.black], for: .selected) return control }() // 辅助方法:生成带内边距的背景图 private func createSegmentBg(color: UIColor, cornerRadius: CGFloat, margin: UIEdgeInsets) -> UIImage { let baseSize = CGSize(width: 100, height: 30) let fullRect = CGRect(origin: .zero, size: baseSize) let contentRect = fullRect.inset(by: margin) UIGraphicsBeginImageContextWithOptions(baseSize, false, UIScreen.main.scale) color.setFill() UIBezierPath(roundedRect: contentRect, cornerRadius: cornerRadius).fill() let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image ?? UIImage() } @objc private func handleSegmentControl(sender: UISegmentedControl) { // 处理选择逻辑 print("选中索引:\(sender.selectedSegmentIndex)") }
方案二:完全自定义分段控制器(更高自由度)
如果系统控件的改造无法满足需求,直接用UIStackView搭配UIButton实现,完全控制每个按钮的样式和间隙:
lazy var customSegmentView: UIStackView = { let stackView = UIStackView() stackView.axis = .horizontal stackView.spacing = 8 // 按钮之间的间隙大小 stackView.distribution = .fillEqually stackView.layer.cornerRadius = 9 stackView.layer.borderWidth = 1 stackView.layer.borderColor = UIColor.white.cgColor stackView.layer.masksToBounds = true stackView.backgroundColor = .clear // 创建分段按钮 let titles = ["Daily", "Weekly", "Monthly"] for (index, title) in titles.enumerated() { let button = UIButton(type: .system) button.setTitle(title, for: .normal) button.tag = index button.layer.cornerRadius = 7 button.contentEdgeInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) button.addTarget(self, action: #selector(segmentButtonTapped(sender:)), for: .touchUpInside) // 默认选中第一个按钮 if index == 0 { button.backgroundColor = .white button.setTitleColor(.black, for: .normal) } else { button.backgroundColor = .clear button.setTitleColor(.white, for: .normal) } stackView.addArrangedSubview(button) } return stackView }() @objc private func segmentButtonTapped(sender: UIButton) { // 更新所有按钮的选中状态 for case let button as UIButton in customSegmentView.arrangedSubviews { let isSelected = button.tag == sender.tag button.backgroundColor = isSelected ? .white : .clear button.setTitleColor(isSelected ? .black : .white, for: .normal) } print("选中索引:\(sender.tag)") }
内容的提问来源于stack exchange,提问作者Naiyer Aghaz
相关产品推荐
相关产品推荐

