You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自定义带按钮间隙的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

通过修改系统控件的分割线、背景图来模拟间隙效果,步骤如下:

  1. 隐藏默认分割线
  2. 给每个分段设置带内边距的背景图,实现视觉上的间隙
  3. 自定义选中/未选中状态的样式

完整代码:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 04:22:19