UISegmentedControl remake that supports selecting multiple segments, vertical stacking, combining text and images.

Overview

MultiSelectSegmentedControl

Swift Version Build Status License CocoaPods Compatible
Platform PRs Welcome

UISegmentedControl remake that supports selecting multiple segments, vertical stacking, combining text and images.

Features

  • Single or multiple selection.
  • Horizontal or vertical stacking.
  • Can show text and images together.
  • Use from either storyboard or code.
  • UIAppearance support.

Usage

Very similar to UISegmentedControl, can be used as a drop-in replacement in most cases.

If you use Interface Builder, add a regular UIView and then set its class to MultiSelectSegmentedControl.

SwiftUI Usage

MultiSegmentPicker(
    selectedSegmentIndexes: $indexSet,
    items: ["One", "Two", image, [image2, "Text"], "Last"]
)

The properties mentioned below can be passed as arguments to the MultiSegmentPicker initializer, or used as view modifiers (e.g., .borderWidth(3)).

Creating Segments

Each segment can contain an image, a text, or both:

let multiSelect = MultiSelectSegmentedControl()
multiSelect.items = ["One", "Two", image, [image2, "Text"], "Last"]

Images are shown in full color (unlike UISegmentedControl). To make them render in the same tintColor as the control, use template mode:

multiSelect.items = [image1, image2, image3].map { $0.withRenderingMode(.alwaysTemplate) }

Selecting Segments

multiSelect.selectedSegmentIndexes = [1, 2, 4]

Or just single selection:

multiSelect.allowsMultipleSelection = false
multiSelect.selectedSegmentIndex = 3

Getting Selected Segments

let selectedIndices: IndexSet = multiSelect.selectedSegmentIndexes

Or to get the titles:

let titles: [String] = multiSelect.selectedSegmentTitles

Handling User Selection Changes

You can use standard target-action:

multiSelect.addTarget(self, action: #selector(selectionChanged), for: .valueChanged)

Or conform to the delegate protocol:

extension MyViewController: MultiSelectSegmentedControlDelegate {
    func multiSelect(_ multiSelectSegmentedControl: MultiSelectSegmentedControl, didChange value: Bool, at index: Int) {
        print("selected \(value) at \(index)")
    }
}

... and set the delegate:

multiSelect.delegate = self

Changing Appearance

Color:

multiSelect.tintColor = .green

Background Color (optional - use if background color should be different from tint color):

multiSelect.selectedBackgroundColor = .blue

Shape:

multiSelect.borderWidth = 3 // Width of the dividers between segments and the border around the view.
multiSelect.borderRadius = 32 // Corner radius of the view.

Stack the segments vertically:

multiSelect.isVertical = true

Stack each segment contents vertically when it contains both image and text:

multiSelect.isVerticalSegmentContents = true

Text styling:

multiSelect.setTitleTextAttributes([.foregroundColor: UIColor.yellow], for: .selected)
multiSelect.setTitleTextAttributes([.obliqueness: 0.25], for: .normal)

More label styling:

multiSelect.titleConfigurationHandler = {
    $0.numberOfLines = 0
    $0.lineBreakMode = .byWordWrapping
}

Installation

CocoaPods:

pod 'MultiSelectSegmentedControl'

Swift Package Manager:

dependencies: [
    .package(url: "https://github.com/yonat/MultiSelectSegmentedControl", from: "2.3.5")
]

TODO

  • foreground color of selected segment should be/appear transparent
  • configure segment layoutMargins, stackView.spacing

Meta

@yonatsharon

https://github.com/yonat/MultiSelectSegmentedControl

Comments
  • Ability to set selected segment's label's text color

    Ability to set selected segment's label's text color

    When the segment is selected, it sets the label color to .background.

    This is fine for the default colors, but my tint is a shade of blue. I'd prefer that when the segment is selected, the text color is white, but in night mode the text will be black.

    enhancement 
    opened by StainlessStlRat 34
  • Segment visual order gets out of sync with index order

    Segment visual order gets out of sync with index order

    Something may have changed in how the underlying UISegmentedControl works but I am now having the following problem/

    If you start with MultiSelectSegmentedControl outlet in interface builder all the initial UISegment frame.origin.x are all zero, so initSortedSegmentsArray doesn't do much - just results in the same order as the subviews were added.

    But if you now add an extra segment programatically it will get a non-zero frame.origin.x and now sortedSegments is no longer in sync with the subview order.

    The segments will display as expected but when you try and setSelectedSegmentIndexes: with index 0 visually this will result in the second segment being selected.

    opened by reganApe 10
  • SPM Archive Error

    SPM Archive Error

    Description of the problem: When Archiving my project I am getting these errors. I am not using SwiftUI.

    /MultiSelectSegmentedControl/Sources/MultiSegmentPicker.swift Cannot find type 'UIViewRepresentable' in scope Unknown attribute 'Binding' Cannot find type 'Binding' in scope Cannot find '_selectedSegmentIndexes' in scope Cannot find type 'UIViewRepresentableContext' in scope

    bug 
    opened by willm132 7
  • Setting the MultiSegmentPicker to be fully justified

    Setting the MultiSegmentPicker to be fully justified

    Description: I am trying to have the MultiSegmentPicker extend the width of the screen. Before I tried to implement this in SwiftUI, I was able to achieve this:

    IMG_76297A361A7F-1

    by embedding the control inside a view, and then, after viewDidAppear, setting the frame of the control to the size of the view which it was inside.

    I am now migrating my app to SwiftUI and trying to achieve similar behavior. I am not as knowledgable in it, and am having some difficulty. The code I am using is:

    struct GradePickSegmentedControl: View {
        let items: [String]
        @Binding var indexSet: IndexSet
        var body: some View {
            MultiSegmentPicker(
                selectedSegmentIndexes: $indexSet,
                items: items
            )
            .background(Color.red)
            .fixedSize(horizontal: false, vertical: true)
        }
    }
    

    which is producing:

    Simulator Screen Shot - iPhone 11 - 2020-07-20 at 21 34 52

    From what I understand about SwiftUI, the parent view gives the available size to the child, but ultimately the child decides how large it will be. It thus seems that it is up to the control to expand each cell to take up the full width of the screen. But I am unsure how to do this.

    Any help would be appreciated.

    enhancement 
    opened by avicooper1 6
  • Builds and runs, but can not archive using interface builder

    Builds and runs, but can not archive using interface builder

    I am using this multi-select segmented control using Interface Builder and not SwiftUI, and everything runs smoothly. However, when I go to archive the project to publish in the App Store, it seems to give compiler errors in the MultiSegmentPicker.swift file, although this code should not run since I am not using SwiftUI. Is there a way to get around this issue?

    Screen Shot 2020-07-02 at 7 46 35 PM bug 
    opened by RaaghavM 6
  • Old Cocoapod version

    Old Cocoapod version

    Description of the problem: When using the cocoapods default entry. The base version offered is 1.2.1 which fails the tests etc.

    Minimal project that reproduces the problem (so I'll be able to figure out how to fix it):

    • Create a new app.
    • Git init the project.
    • pod 'MultiSelectSegmentedControl' in the Podfile
    • pod install
    • check the version is 1.2.1 not the most current which should. e the default behavior.
    bug 
    opened by jamiedaniel 6
  • SwiftUI Host View is too big (segmented control has lots of white space below it)

    SwiftUI Host View is too big (segmented control has lots of white space below it)

    I want to put a bar at the top of the screen, like the demo. The non-swiftui version works perfectly, but the SwiftUI version has a lot of space below the actual bar: Screen Shot 2020-04-16 at 11 21 23 AM

    How do I shrink the blue area down to the height of the control?

    Minimal project that reproduces the problem (so I'll be able to figure out how to fix it): I have opened a question on StackOverflow with more details.

    StackOverflow

    bug 
    opened by Mozahler 5
  • Crashing on iOS 12.2-12.4 when using OneSignal

    Crashing on iOS 12.2-12.4 when using OneSignal

    Description of the problem: Hello, I've started seeing some crash reports in my app with the latest version of this control. The issue seems to be limited to iOS 12.2, 12.3, & 12.4; this does not occur on iOS 13. When I run a simulator using a pod of the latest version I get the following errors immediately on load:

    dyld: lazy symbol binding failed: can't resolve symbol _$s7SwiftUI7BindingVMa in /Users/.../Library/Developer/CoreSimulator/Devices/7E3DD44E-.../data/Containers/Bundle/Application/72E31CE6-.../MyApp.app/Frameworks/MultiSelectSegmentedControl.framework/MultiSelectSegmentedControl because dependent dylib #1 could not be loaded

    dyld: can't resolve symbol _$s7SwiftUI7BindingVMa in /Users/.../Library/Developer/CoreSimulator/Devices/7E3DD44E-.../data/Containers/Bundle/Application/72E31CE6.../MyApp.app/Frameworks/MultiSelectSegmentedControl.framework/MultiSelectSegmentedControl because dependent dylib #1 could not be loaded

    The crash log shows "type metadata accessor for Binding + 52" as the break point.

    Minimal project that reproduces the problem (so I'll be able to figure out how to fix it): Tested with iOS Simulator using iPhone 8 on iOS 12.4

    bug 
    opened by ibuprofane 5
  • MultiSelectSegmentedControl not recognized class in interface builder since upgrade

    MultiSelectSegmentedControl not recognized class in interface builder since upgrade

    Hello, I just recently ran a pod update from 1.2.1 to 2.0.1. For whatever reason, Interface Builder is not recognizing the class anymore. It refuses to be set. So while the project compiles (after updating headers to -Swift.h, updating a couple of API calls), interface builder falls back to creating a UISegmentedControl and then the properties (like selected indexes) fail as unrecognized selectors.

    iOS 13 release is right around the corner. Any help? Something I'm doing wrong in the transition from obj C to swift?

    opened by StainlessStlRat 5
  • Runtime error upon startup on iOS 14 devices when compiled with Xcode 13.3+

    Runtime error upon startup on iOS 14 devices when compiled with Xcode 13.3+

    Description of the problem: This module has been very helpful for me and my project, but starting with Xcode 13.3, I began receiving many reports from iOS 14 users stating that my app had begun crashing immediately at startup, and this module has been identified as the culprit. More specifically, the issue is in the SweeterSwift dependency which this package relies on.

    My colleague forked the SweeterSwift package to devise a workaround here: https://github.com/zabolotiny/SweeterSwift

    Similarly, I can see another fork of this dependency has been created by another user ( @Janek-CL ) that claims to fix the issue in a different way: https://github.com/CONVELOP-GbR/SweeterSwift

    (Issue cross-posted in the SweeterSwift module as https://github.com/yonat/SweeterSwift/issues/7 )

    bug 
    opened by lukemmtt 4
  • Adjust Text Size

    Adjust Text Size

    For UISegmentedControl's you are able to use .apportionsSegmentWidthsByContent = true, this will automatically decrease the text size so everything fits in the UISegmentedControl's width.

    When using a MultiSelectSegmentedControl and using .apportionsSegmentWidthsByContent = true it will cut off elements since the test size is not decreasing to fit.

    Example of what it should do: Normal Segment

    What it currently does: MultiSegment

    bug 
    opened by willm132 3
  • Autolayout Issues with SwiftUI Usage

    Autolayout Issues with SwiftUI Usage

    When using SPM and a barebones project I get auto layout warnings.
    When I set uiView.translatesAutoresizingMaskIntoConstraints to false in MultiSegmentPicker.init() the project runs fine without warnings or errors. But if I debug the ViewHierarchy there is a warning due to ambiguous contraints. I have opened a question on StackOverflow regarding this issue in which I documented the process and provided screen shots.

    StackOverflow Issue

    If I get any useful response from anyone there, I will post it here.

    bug 
    opened by Mozahler 2
Owner
Yonat Sharon
Freelance iOS developer. Agile, TDD, and all that jazz. 🎸
Yonat Sharon
A minimalistic looking banner library for iOS. It supports multiple customizable kinds of Banner types

A minimalistic looking banner library for iOS. It supports multiple customizable kinds of Banner types

Emre Armagan 12 Oct 10, 2022
DGFadingLabel - A custom UILabel that fades away the end of your text when text is too large to fit within the label's frame

A custom UILabel that fades away the end of your text when text is too large to fit within the label's frame.

donggyu 4 Jun 10, 2022
ScrollView that supports a parallax header image and static overlay.

ScrollViewReactiveHeader A replacement ScrollView that provides a header with subtle scroll animations. example-video.mov Using ScrollViewReactiveHead

null 49 Dec 29, 2022
A SwiftUI Views for wrapping HStack elements into multiple lines

SwiftUI WrappingStack A SwiftUI Views for wrapping HStack elements into multiple lines. List of supported views WrappingHStack - provides HStack that

Denis 50 Jan 6, 2023
SheetPresentation for SwiftUI. Multiple devices support: iOS, watchOS, tvOS, macOS, macCatalyst.

SheetPresentation for SwiftUI. Multiple devices support: iOS, watchOS, tvOS, macOS, macCatalyst.

Aben 13 Nov 17, 2021
Pull up controller with multiple sticky points like in iOS Maps

PullUpController Create your own pull up controller with multiple sticky points like in iOS Maps Features Multiple sticky points Landscape support Scr

Mario Iannotta 1.2k Dec 22, 2022
A custom UIControl which functions like UISlider where you can set multiple intervals with different step values for each interval.

MultiStepSlider A custom UIControl which functions like UISlider where you can set multiple intervals with different step values for each interval. Th

Susmita Horrow 25 Apr 28, 2022
An iOS text field that represents tags, hashtags, tokens in general.

WSTagsField An iOS text field that represents tags, hashtags, tokens in general. Usage let tagsField = WSTagsField() tagsField.layoutMargins = UIEdgeI

Whitesmith 1.2k Dec 29, 2022
Compose views using enums swiftly: `let label: UILabel = [.text("Hello"), .textColor(.red)]`

ViewComposer Style views using an enum array with its attributes: let label: UILabel = [.text("Hello World"), .textColor(.red)] Table of Contents Inst

Alexander Cyon 28 Jul 5, 2022
Growing text view in SwiftUI

Growing text view in SwiftUI If you are planning to write a messaging feature or you are just a SwiftUI enthusiast, this repository can be interesting

Maciej Gomółka 60 Jan 8, 2023
Fetch the star wars api from all the planets and list and show details using Swift UI and Combine

Star Wars Planets Fetch the star wars planet data by using stat war api, list and show details using SwiftUI and Combine frameworks ?? Swift UI Framew

null 1 Aug 10, 2022
A custom stretchable header view for UIScrollView or any its subclasses with UIActivityIndicatorView and iPhone X safe area support for content reloading. Built for iOS 10 and later.

Arale A custom stretchable header view for UIScrollView or any its subclasses with UIActivityIndicatorView support for reloading your content. Built f

Putra Z. 43 Feb 4, 2022
🏞 A simple iOS photo and video browser with optional grid view, captions and selections written in Swift5.0

Introduction ?? MediaBrowser can display one or more images or videos by providing either UIImage objects, PHAsset objects, or URLs to library assets,

Kyle Yi 631 Dec 29, 2022
MZFormSheetPresentationController provides an alternative to the native iOS UIModalPresentationFormSheet, adding support for iPhone and additional opportunities to setup UIPresentationController size and feel form sheet.

MZFormSheetPresentationController MZFormSheetPresentationController provides an alternative to the native iOS UIModalPresentationFormSheet, adding sup

Michał Zaborowski 979 Nov 17, 2022
Step-by-step progress view with labels and shapes. A good replacement for UIActivityIndicatorView and UIProgressView.

StepProgressView Step-by-step progress view with labels and shapes. A good replacement for UIActivityIndicatorView and UIProgressView. Usage let progr

Yonat Sharon 340 Dec 16, 2022
High performance and lightweight UIView, UIImage, UIImageView, UIlabel, UIButton, Promise and more.

SwiftyUI High performance and lightweight UIView, UIImage, UIImageView, UIlabel, UIButton and more. Features SwiftyView GPU rendering Image and Color

Haoking 336 Nov 26, 2022
UIPheonix is a super easy, flexible, dynamic and highly scalable UI framework + concept for building reusable component/control-driven apps for macOS, iOS and tvOS

UIPheonix is a super easy, flexible, dynamic and highly scalable UI framework + concept for building reusable component/control-driven apps for macOS, iOS and tvOS

Mohsan Khan 29 Sep 9, 2022
BulletinBoard is an iOS library that generates and manages contextual cards displayed at the bottom of the screen

BulletinBoard is an iOS library that generates and manages contextual cards displayed at the bottom of the screen. It is especially well

Alexis (Aubry) Akers 5.3k Jan 2, 2023
Custom segue for OSX Storyboards with slide and cross fade effects (NSViewControllerTransitionOptions)

CustomSegue Custom segue for OSX Storyboards. Slide and cross fade effects, new customized window. class MyViewController: NSViewController { overr

Eric Marchand 123 May 21, 2022