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.6")
]

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
Custom UISegmentedControl replacement for iOS, written in Swift

TwicketSegmentedControl Custom UISegmentedControl replacement for iOS, written in Swift, used in the Twicket app. It handles the inertia of the moveme

Pol Quintana 1.7k Dec 31, 2022
A highly customizable drop-in replacement for UISegmentedControl.

HMSegmentedControl A highly customizable drop-in replacement for UISegmentedControl, used by more than 22,000 apps, including TikTok, PayPal, Imgur an

Hesham Abd-Elmegid 3.9k Dec 21, 2022
MacSegmentedControl - UISegmentedControl on macOS

Mac Segmented Control An implementation of iOS' UISegmentedControl on macOS. Add

Jeff Dlouhy 0 Dec 30, 2021
A customizable Segmented Control for iOS. Supports text and image.

YUSegment 中文文档 A customizable segmented control for iOS. Features Supports both (Attributed)text and image Supports show separator Supports hide indic

YyGgQq 112 Jun 10, 2022
A segment switcher with dynamic text mask effect

DynamicMaskSegmentSwitch A segment switcher with dynamic text mask effect ##Preview: Indicator will bounce when progress less than 0 or larger than 1.

Qitao Yang 309 Oct 7, 2022
A segmented control with custom appearance and interactive animations. Written in Swift 3.0.

SJFluidSegmentedControl About If you are bored with using the default UISegmentedControl, this might save your day. SJFluidSegmentedControl is a custo

Sasho Jadrovski 904 Dec 30, 2022
UISegmentedControl remake that supports selecting multiple segments, vertical stacking, combining text and images.

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

Yonat Sharon 286 Dec 15, 2022
Multiple UI adaptable vertical segments slider

SSVerticalSegmentsSlider It's a vertical segment slider developed in SwiftUI. It offers many properties to create different UI that fit your needs. We

smartSense Solutions 18 Oct 16, 2022
ImagePicker - selecting images from the photo albums, with allowed permissions /on real device accesing the camera

ImagePicker - selecting images from the photo albums, with allowed permissions /on real device accesing the camera, permission also needed/after picking an image it has the possibility to rename it

Pavel Surový 0 Jan 26, 2022
UISlider clone with multiple thumbs and values, range highlight, optional snap intervals, optional value labels, either vertical or horizontal.

MultiSlider UISlider clone with multiple thumbs and values, range highlight, optional snap intervals, optional value labels, either vertical or horizo

Yonat Sharon 326 Dec 29, 2022
Easily add vertical and horizontal pull to refresh to any UIScrollView. Can also add multiple pull-to-refesh views at once.

This is a fork from the famous SVPullToRefresh pod with 2 additional functionalities: Can add multiple pull-to-refresh views into one single UIScrollV

Hoang Tran 42 Dec 28, 2022
UISlider clone with multiple thumbs and values, range highlight, optional snap intervals, optional value labels, either vertical or horizontal.

MultiSlider UISlider clone with multiple thumbs and values, range highlight, optional snap intervals, optional value labels, either vertical or horizo

Yonat Sharon 326 Dec 29, 2022
Lint anything by combining the power of Swift & regular expressions.

Installation • Getting Started • Configuration • Xcode Build Script • Donation • Issues • Regex Cheat Sheet • License AnyLint Lint any project in any

Flinesoft 116 Sep 24, 2022
TapLetterGuess - Find the answer by combining words

TapLetterGuess Find the answer by combining words.

Ahmet Onur Sahin 3 Apr 18, 2022
add text(multiple line support) to imageView, edit, rotate or resize them as you want, then render the text on image

StickerTextView is an subclass of UIImageView. You can add multiple text to it, edit, rotate, resize the text as you want with one finger, then render the text on Image.

Textcat 478 Dec 17, 2022
An Apple Watch remake of the Poketch from Pokemon Diamond and Pearl made with SwiftUI

Apple Watch Poketch What is it? It's an Apple Watch remake of the "Poketch" from Pokemon Diamond and Pearl made with SwiftUI! Check out the YouTube vi

André Arko 1 Nov 19, 2021
A scroll pager that displays a list of tabs (segments) and manages paging between given views

ScrollPager A scroll pager similar to the one in Flipboard. The control creates a tabbar given a title or an image, and has the option of connecting t

Aryan Ghassemi 512 Aug 31, 2022
CookieCrunch game (a Candy Crush-like remake)

CookieCrunch CookieCrunch game (a Candy Crush-like remake) Why? Because I like games! Why not trying to understand such a successfull app like Candy C

Renato Camilio 25 Dec 8, 2022
Remake of Nintendo's Octopus Game & Watch device for Apple TV 4

Octopus Remake of Nintendo's Octopus Game & Watch device for Apple TV 4. Written in Swift. Simulator controls Use keyboard and remote Press Play/Pause

Roger Wetzel 15 Aug 27, 2022
iOS camera engine with Vine-like tap to record, animatable filters, slow motion, segments editing

SCRecorder A Vine/Instagram like audio/video recorder and filter framework in Objective-C. In short, here is a short list of the cool things you can d

Simon Corsin 3.1k Dec 25, 2022