ios - Swift iOS - 如何实现多行不同字号的SegmentedControl

标签 ios swift nsattributedstring uisegmentedcontrol

我有 2 行的 SegmentedControl 使用:

// AppDelegate  
UILabel.appearanceWhenContainedInInstancesOfClasses([UISegmentedControl.self]).numberOfLines = 0

enter image description here

问题是线条字体的大小完全相同。 我需要更改每一行的 titleTextAttributes,以便第二行比第一行小。

我知道我可以将它用于两条线:

segmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17))

我该怎么做?

// The SegmentedControl
let segmentedControl: UISegmentedControl = {
    let segmentedControl = UISegmentedControl(items: ["Pizza\n123.1K", "Turkey Burgers\n456.2M", "Gingerale\n789.3B"])
    segmentedControl.translatesAutoresizingMaskIntoConstraints = false
    segmentedControl.tintColor = UIColor.orange
    segmentedControl.backgroundColor = .white
    segmentedControl.isHighlighted = true
    segmentedControl.addTarget(self, action: #selector(selectedIndex(_:)), for: .valueChanged)
    return segmentedControl
}()

最佳答案

您需要通过子类化 UIControl 来创建自定义控件。这是一个简单的例子:

CustomSegmentedControl.swift

import UIKit
import CoreImage

public class CustomSegmentedControl: UIControl {

    public var borderWidth: CGFloat = 1.0
    public var selectedSegementIndex = 0 {
        didSet {
            self.styleButtons()
        }
    }

    public var numberOfSegments: Int {
        return self.segments.count
    }

    private var buttons: [UIButton] = []
    private var stackView = UIStackView(frame: CGRect.zero)
    private var stackBackground = UIView(frame: CGRect.zero)
    private var segments: [NSAttributedString] = [] {
        didSet {
            for subview in self.stackView.arrangedSubviews {
                subview.removeFromSuperview()
            }
            self.buttons = []
            for i in 0..<segments.count {
                let segment = segments[i]
                self.createAndAddSegmentButton(title: segment)
            }
            self.styleButtons()
        }
    }

    override public init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    private func setup() {

        self.addSubview(stackBackground)
        self.stackBackground.constrainToBounds(of: self)
        self.addSubview(stackView)
        self.stackView.constrainToBounds(of: self)
        self.stackView.axis = .horizontal
        self.stackView.distribution = .fillEqually
        self.stackView.spacing = borderWidth

        self.layer.cornerRadius = 5.0
        self.layer.borderWidth = borderWidth
        self.clipsToBounds = true
        self.stackBackground.backgroundColor = tintColor
    }

    private func createAndAddSegmentButton(title: NSAttributedString) {
        let button = createSegmentButton(title: title)
        self.buttons.append(button)
        self.stackView.addArrangedSubview(button)
    }

    private func createSegmentButton(title: NSAttributedString) -> UIButton {
        let button = UIButton(frame: CGRect.zero)
        button.titleLabel?.numberOfLines = 0
        button.titleLabel?.textAlignment = .center
        button.setAttributedTitle(title, for: .normal)
        button.addTarget(self, action: #selector(self.actSelected(button:)), for: .touchUpInside)
        return button
    }

    override public var tintColor: UIColor! {
        willSet {
            self.layer.borderColor = newValue.cgColor
            self.stackBackground.backgroundColor = newValue
        }
    }

    public func setSegments(_ segments: [NSAttributedString]) {
        self.segments = segments
    }

    @objc private func actSelected(button: UIButton) {
        guard let index = self.buttons.index(of: button) else {
            print("invalid selection should never happen, would want to handle better than this")
            return
        }
        self.selectedSegementIndex = index
        self.sendActions(for: .valueChanged)
    }

    private func styleButtons() {
        for i in 0..<self.buttons.count {
            let button = self.buttons[i]
            if i == selectedSegementIndex {
                button.backgroundColor = self.tintColor
                button.titleLabel?.textColor = self.backgroundColor ?? .white
            } else {
                button.backgroundColor = self.backgroundColor
                button.titleLabel?.textColor = self.tintColor
            }
        }
    }
}

extension UIView {
    func constrainToBounds(of view: UIView) {
        self.translatesAutoresizingMaskIntoConstraints = false
        let attrs: [NSLayoutAttribute] = [.leading, .top, .trailing, .bottom]
        let constraints = attrs.map { (attr) -> NSLayoutConstraint in
            return NSLayoutConstraint(item: self,
                                      attribute: attr,
                                      relatedBy: .equal,
                                      toItem: view,
                                      attribute: attr,
                                      multiplier: 1.0,
                                      constant: 0)
        }
        NSLayoutConstraint.activate(constraints)
    }
}

ViewController.swift

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var customSegment: CustomSegmentedControl!
    private var segments: [NSAttributedString] = []

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.customSegment.backgroundColor = .white
        self.customSegment.tintColor = .orange

        let pizza = createText(title: "Pizza", subTitle: "123K")
        let turkey = createText(title: "Turkey Burgers", subTitle: "456.2M")
        let gingerAle = createText(title: "Gingerale", subTitle: "789.3B")
        self.segments = [pizza, turkey, gingerAle]
        self.customSegment.setSegments(self.segments)

        self.customSegment.addTarget(self, action: #selector(self.segmentSelectionChanged(control:)), for: .valueChanged)
    }

    @objc private func segmentSelectionChanged(control: CustomSegmentedControl) {
        let segment = self.segments[control.selectedSegementIndex]
        print("selected segment = \(segment.string)")
    }

    func createText(title: String, subTitle: String) -> NSAttributedString {
        let titleStr = NSMutableAttributedString(string: "\(title)\n", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 16)])
        let subStr = NSAttributedString(string: subTitle, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 10)])
        titleStr.append(subStr)
        return titleStr
    }

}

enter image description here

关于ios - Swift iOS - 如何实现多行不同字号的SegmentedControl,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50931693/

相关文章:

URL 中的 SwiftUI 图像未显示

swift - 如何在 SwiftUI 中将 NSAttributedString 与 ScrollView 一起使用?

ios - 不使用界面生成器时是否需要IBAction

swift - 捕获多个错误类型?

ios - UIStackView - 拖动以重新排列排列的 subview ?

arrays - 查询解析数组数据到本地数组,并添加到 TableView (Swift)

ios - UIButton NSAttributedString 不会更新字体颜色

iOS 属性文本删除/n 换行符

iphone - iOS:在单独的线程中反序列化 JSON?

ios - 多进程iOS应用程序