How to Create Multi Line Uisegmentedcontrol

Is it possible to create multi line UISegmentedControl?

if you have a standard UISegmentedControl you can use the following idea:

[_segmentedControl.subviews enumerateObjectsUsingBlock:^(UIView * obj, NSUInteger idx, BOOL *stop) {
[obj.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UILabel class]]) {
UILabel *_tempLabel = (UILabel *)obj;
[_tempLabel setNumberOfLines:0];
}
}];
}];

you may need to set the height of your instance as well.


NOTE: I need to add a little warning about – as rmaddy has also pointed out correctly – this is a quite fragile solution, because if the segmented control's view hierarchy would be changed in the future iOS versions that code may not work properly anymore.

Swift iOS -How to Achieve Multi line SegmentedControl with different Font Sizes

You'll want to create a custom control by subclassing UIControl. Here's a quick example:

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
}

}

Sample Image

Multiline Text On UIsegment Control

Hi friend segment controller already have the label as the subview, so this code is helpful to achieve the multiline text to segment control

for (id segment in [segmentedControl subviews]) 
{
for (id label in [segment subviews])
{
if ([label isKindOfClass:[UILabel class]])
{
//hear u add any of delegate function to increase the height and other label functionality in this
[label setTextAlignment:UITextAlignmentCenter];
[label setFont:[UIFont boldSystemFontOfSize:12]];
//to adjust the label size manually with respect to text use below code
CGSize labelSize = CGSizeMake(100, 80);
CGSize theStringSize = [label.text sizeWithFont:label.font constrainedToSize:labelSize];
CGRect frame = label.frame;
frame.size = theStringSize;

}
}
}

Have a Good day

How would I fit all elements into a Segmented Control?

TL;DR

You can't fit all your text "as is" into a segmented picker, because the designers of SwiftUI didn't build segmented pickers to have that many options. You have a couple alternatives.

Images

As dasblinkenlight suggested, you can replace the text with small images, which should fit up to 6 choices:

struct BundleView: View {

let areas = ["a.circle", "b.circle", "c.circle", "d.circle", "e.circle", "f.circle"]

@State var selectedArea = 0

var body: some View {
Picker(selection: $selectedArea, label: EmptyView()) {
ForEach(0..<areas.count) { index in
Image(systemName: self.areas[index]).tag(index)
}
}.pickerStyle(SegmentedPickerStyle())
}
}

Sample Image

Custom Control

You can build a custom control. Here's a cool design with animation from swiftui-lab.com:

Custom Control

But why can't I use Picker?

SwiftUI tends to have two types of Views:

  1. Those designed to be easily modifiable and composable, providing unlimited customizability for a unique look and feel.
  2. Those designed to provide a standard, consistent feel to some type of interaction, regardless of what app they are used in.

An example of type 1 would be Text. You can change font size, weight, typeface, color, background, padding, etc. It is designed for you to modify it.

An example of type 2 would be Picker. You are not in direct control of item width, you can't change the padding around views, you can't tell it wrap to multiple lines, etc. They don't want it to be very customizable, because then each app's pickers would behave differently, defeating the purpose of a standard control.

Segmented Pickers work best with 2-4 items, whose descriptions are relatively short. If you want something outside that, you need to make a custom control.



Related Topics



Leave a reply



Submit