A Short Introduction Of SMSegmentView
I know many of you may not be interested in the background, so please feel free to skip this part :)
Due to the standard look and the relatively low customisability of UISegmentedControl, the author of SMSegmentView wrote this framework for both developers and designer to make their apps more outstanding, which in another way, might also improve the user experience.
The framework supports both image and text in a single segment. Besides of the traditional horizontal look, it also allows you to arrange your segments in a vertical way. You can change the attributes of the text and segment as well. It is written in Swift and very easy to expand with new features, which is perfect to be used in your next project.
Now let start building something.
Build A Custom SegmentedView
First of all, you will need to find the framework here.
Then, create a new Xcode project with the Single View Application template. Name the project whatever you like, but in this tutorial, let's use SegmentedControlSample. Choose Swift as the language and iPhone as the devices.
Now drag the SMSegmentView folder inside the framework to your project and tick "Copy items if needed". And we are now ready to create our very customised segmented control. Pretty easy, huh? :D
Now let's create a property of the instance of SMSegmentView in ViewController.swift:
var segmentedControl: SMSegmentView!
ViewController.swift: Using the exclamation symbol is to tell the compiler: "Don't worry. I'll assign it a value later".
In viewDidLoad() we initialise the segmentedControl by using its frame:dividerColour:dividerWidth: segmentAppearance: method. Add the following code after super.viewDidLoad():
// 4
(1) Create an instance of SMSegmentAppearance, where you can specify some UI attributes.
(2) Call the initialiser of SMSegmentView. The parameter segmentAppearance reads the SMSegmentAppearance instance we created in step 1.
(3)We add three segments to our segmentedControler. In this demo, we do not need to assign images to our segment. But in real practice, you can add them if you want.
(4)We set an action to react to UIControlEvents.ValueChanged. After this step, don't forget to add the selector selectSegmentInSegmentView(_:) in the ViewController class:
Now you can give it a try. If everything goes well, you will see the simulator show this when you select a segment:
Hmm... It looks not too bad, but not as good as it is expected, either :(
Don't worry. We haven't finished, yet.
Add the following code right above you added new segment to the segmentedControl:
As SMSegmentView is a subclass of UIView, we can use UIView's methods to give it a border.
And add the following code at the bottom of viewDidLoad().
You'll know how we are gonna use this view right as follows. Change the SMSegmentView delegate method to:
(1) We are using a closure to calculate where our bar should be placed.
(2) If no segment has been selected, yet, we will need to add the selectionBar view to our segmentedControl before put the bar on the right place. Otherwise, we animatedly reposition the bar view.
Let's run the app and see how it goes now.
It looks more dynamic now. Pretty easy stuff to make the change, right? ;)
In viewDidLoad() we initialise the segmentedControl by using its frame:dividerColour:dividerWidth: segmentAppearance: method. Add the following code after super.viewDidLoad():
self.view.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
// 1
let appearance = SMSegmentAppearance()
appearance.segmentOnSelectionColour = UIColor(red: 245.0/255.0, green: 174.0/255.0, blue: 63.0/255.0, alpha: 1.0)
appearance.segmentOffSelectionColour = UIColor.whiteColor()
appearance.titleOnSelectionFont = UIFont.systemFontOfSize(12.0)
appearance.titleOffSelectionFont = UIFont.systemFontOfSize(12.0)
appearance.contentVerticalMargin = 10.0
// 2
let segmentFrame = CGRect(x: self.margin, y: 120.0, width: self.view.frame.size.width - self.margin*2, height: 40.0)
self.segmentView = SMSegmentView(frame: segmentFrame, dividerColour: UIColor(white: 0.95, alpha: 0.3), dividerWidth: 1.0, segmentAppearance: appearance)
// 3
self.segmentedControl.addSegmentWithTitle("Segment 1", onSelectionImage: nil, offSelectionImage: nil)
self.segmentedControl.addSegmentWithTitle("Segment 2", onSelectionImage: nil, offSelectionImage: nil)
self.segmentedControl.addSegmentWithTitle("Segment 3", onSelectionImage: nil, offSelectionImage: nil)
// 4
self.segmentView.addTarget(self, action: #selector(ViewController.selectSegmentInSegmentView(_:)), forControlEvents: .ValueChanged)
self.view.addSubview(self.segmentedControl)
(2) Call the initialiser of SMSegmentView. The parameter segmentAppearance reads the SMSegmentAppearance instance we created in step 1.
(3)We add three segments to our segmentedControler. In this demo, we do not need to assign images to our segment. But in real practice, you can add them if you want.
(4)We set an action to react to UIControlEvents.ValueChanged. After this step, don't forget to add the selector selectSegmentInSegmentView(_:) in the ViewController class:
func selectSegmentInSegmentView(segmentView: SMSegmentView) {
print("Select segment at index: \(segmentView.selectedSegmentIndex)")
}
Now you can give it a try. If everything goes well, you will see the simulator show this when you select a segment:
Hmm... It looks not too bad, but not as good as it is expected, either :(
Don't worry. We haven't finished, yet.
Add the following code right above you added new segment to the segmentedControl:
self.segmentedControl.layer.borderColor = UIColor(white: 0.9, alpha: 1.0).CGColor
self.segmentedControl.layer.borderWidth = 1.0
Expanding The Framework A Little...
Now let's expand it a little bit to give it some animation when we select at a segment. First of all, let add another property to our ViewController:
var seletionBar: UIView = UIView()
self.seletionBar.frame = CGRect(x: 0.0, y: 0.0, width: self.segmentedControl.frame.size.width/CGFloat(self.segmentedControl.numberOfSegments), height: 5.0)
self.seletionBar.backgroundColor = UIColor(white: 0.5, alpha: 0.6)
func segmentView(segmentView: SegmentView didSelectSegmentAtIndex index: Int) {
// 1
let placeSelectionBar = { () -> () in
var barFrame = self.seletionBar.frame
barFrame.origin.x = barFrame.size.width * CGFloat(segmentIndex)
self.seletionBar.frame = barFrame
}
// 2
if self.seletionBar.superview == nil {
self.segmentedControl.addSubview(self.seletionBar)
placeSelectionBar()
}
else {
UIView.animateWithDuration(0.3, animations: {
placeSelectionBar()
})
}
}
(2) If no segment has been selected, yet, we will need to add the selectionBar view to our segmentedControl before put the bar on the right place. Otherwise, we animatedly reposition the bar view.
Let's run the app and see how it goes now.
It looks more dynamic now. Pretty easy stuff to make the change, right? ;)
A Little Bit More Words At The End
If you feel sick of the traditional horizontal arrangement of the segments, this framework also provides you a vertical way. All you need to do is to assign its organiseMode with .SegmentOrganiseVertical value after the view gets initialised.
Find the sample comes with the framework and you will know more about it.
Happy Coding :D
Hi,
ReplyDeleteI would like when the number of segment is greater than 3, the user could scroll the segmentedControl.
Is this posible with this framework? How could I do this?
Thanks.
Hi Buk,
DeleteThe framework itself doesn't support scrolling. But you can easily implement that feature by putting the SegmentView instance into a UIScrollView and adjust the scrollView's contentSize.
Something like:
scrollView.addSubView(segmentView)
scrollView.contentSize = segmentView.frame.size
Hope it helps.
Cheers,
Si
It works.
DeleteThank you very much.
Do you have an email address?
ReplyDeleteYou can reach me via allenbryan11@gmail.com. ;)
Deletehiiii
ReplyDeletewhen i add scrollview in this . its not working and show errors in the lines below
scrollView.addSubView(segmentView)
scrollView.contentSize = segmentView.frame.size
plsss explain
thanx
Hi there,
DeleteIt's a bit hard to tell with just these two lines. Mind sending more code to my email address?
Cheers,
Si
Hi,
ReplyDeleteFirstly thank you for this wonderful custom segmented control. However, I have two questions in mind.
1) How do I change the font of the segmented Control?
2) After performing the 'Expanding the framework', I've uncommented the
segmentView.selectSegmentAtIndex(1)
However, upon selecting index 1, the "selected bar" is still at 0. Is there a way to work around this?
Thanks in advance!
Hi there,
DeleteFor your 1), you can set font in init(frame: CGRect, separatorColour: UIColor, separatorWidth: CGFloat, segmentProperties: Dictionary?) method, where you need add your font into segmentProperties dictionary with key "keySegmentTitleFont"
For your 2) Honestly I can't tell much without seeing your code, but maybe check if you have set a delegate for your segmentView?
organiseMode how to use?segmentView.organiseMode = .SegmentOrganiseVertical cant
ReplyDeleteThis is what I did in the sample, and it worked fine to me:
Deleteself.segmentView.organiseMode = .SegmentOrganiseVertical
I couldn't tell more without seeing more of your code, I'm afraid.
Hi,
ReplyDeleteI want to customize segment size, here we have 3 segments with same size but i want to change the size. how can i do that?
Hi there,
ReplyDeleteI'm afraid this framework doesn't support multi-sized segments.
Hi how replace segment?
ReplyDeleteHi Si Ma,
ReplyDeleteYour Segmented View looks cool! However, I am having an Objective C project, and after installing it through Cocoa Pods, I cannot find any header files. Is the framework compatible with Objective C as well?
Erwin
Hi Erwin,
DeleteSorry for the late reply. You can find the answer here https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
Is there a way to add a badge counter to each of these segment views? Also, need the flexibility to place the badge counter right after the text and not on the top right corner
ReplyDeleteNot at the moment, I'm afraid. I'll see if I can add this feature into the next version.
Delete"Change the SMSegmentView delegate method" - I think now it doesn't work. How can we do that in new version?
ReplyDeleteIs it possible to put buttons under first segment and table under second?