Swift - Segmented control - Switch multiple views - ios

Until now I still can't figure how to switch multiple views in one view controller. My storyboard is like this one.
Right now I want to embed two views inside my view controller.
My code for segmented control to switch two views in one view controller so far.
import UIKit
class PopularHistoryViewController: UIViewController {
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBAction func indexChanged(sender: UISegmentedControl) {
switch segmentedControl.selectedSegmentIndex
{
case 0:
NSLog("Popular selected")
//show popular view
case 1:
NSLog("History selected")
//show history view
default:
break;
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Another thing, If I put two views inside my controller, what is best practice to differentiate it?

If you want to do UI layout in Xcode for the two overlapping subviews, a better solution is to use two UIContainerViewController, and use the same way of setting the hidden property as suggested in the above answer.

You can use the isHidden property of the UIView to show/hide your required views.
First you have to link both views to IBOutlets through the Interface builder
#IBOutlet weak var historyView: UIView!
#IBOutlet weak var popularView: UIView!
#IBAction func indexChanged(_ sender: UISegmentedControl) {
switch segmentedControl.selectedSegmentIndex {
case 0:
historyView.isHidden = true
popularView.isHidden = false
case 1:
historyView.isHidden = false
popularView.isHidden = true
default:
break;
}
}
Note: it was named hidden in Swift 1 and 2.

First of all create two outlets and connect hose to the views in your ViewController.
#IBOutlet weak var firstView: UIView!
#IBOutlet weak var secondView: UIView!
And Change the code like:
#IBAction func indexChanged(sender: UISegmentedControl)
{
switch segmentedControl.selectedSegmentIndex
{
case 0:
firstView.hidden = false
secondView.hidden = true
case 1:
firstView.hidden = true
secondView.hidden = false
default:
break;
}
}
If you don't want to create Outlets, assign the views individual tags (Say 101 and 102) and you can do it like:
#IBAction func indexChanged(sender: UISegmentedControl)
{
switch segmentedControl.selectedSegmentIndex
{
case 0:
self.view.viewWithTag(101)?.hidden = false
self.view.viewWithTag(102)?.hidden = true
case 1:
self.view.viewWithTag(101)?.hidden = true
self.view.viewWithTag(102)?.hidden = false
default:
break;
}
}

Add both views to the view controller in the story board and set one of them to be hidden = yes or alpha = 0. When your index changed function gets called set the current view on screen to hidden = yes/alpha of 0 and set the previously hidden view to hidden = no/alpha = 1. This should achieve what you want.

If it is a simple view, not a part of the screen, you can indeed use isHidden property of two subviews of your view controller view. But I don't like this approach because it's hard to understand latter what is going on with your nib when all of the subviews are in one pile.
I would add and remove those two views as child view controllers programmatically. It's the cleanest way there is, in my opinion.
But even if you decided to go with just views, don't put them directly on view controller's view. Use nibs, preferably with owner class. And in many cases consider adding and constraint them programmatically. It's more code, but also cleaner and conserves resources.

#IBAction func acSegmentAction(_ sender: Any) {
switch acSegmentedControl.selectedSegmentIndex {
case 0:
// print("addressview selected")
addressView.isHidden = false
contactProviderView.isHidden = true
case 1:
//print("contact provider selected")
addressView.isHidden = true
contactProviderView.isHidden = false
default:
break;
}
}

So what is written above does not work for me, so I figured out myself in Xcode 11 with Swift 5.
(view1 = historyView, view2 = popularView)
#IBOutlet weak var view1: UITableView!
#IBOutlet weak var view2: UITableView!
#IBAction func segmentedControlChanged(_ sender: Any) {
func showView1() {
view1.isHidden = false
view2.isHidden = true
}
func showView2() {
view1.isHidden = true
view2.isHidden = false
}
guard let segmentedControl = sender as?
UISegmentedControl else { return }
if segmentedControl.selectedSegmentIndex == 0 {
showView1()
}
else {
showView2()
}
}
Maybe this helps anyone.

Related

Use Segmented Control Swift 3

I have searched the web and youtube about segmented control, but I only find examples of how to change to a different view.
My goal is to take the selected value from the segmented control and use it in the action on another button.
I will try to explain what I mean :
#IBAction func segmentetControll(_ sender: UISegmentedControl) {
value 1
value 2
value 3
}
#IBAction func calculateButton(_ sender: Any) {
if value 1 {
do this
}
else if value 2 {
Do that
}
else if value 3 {
Do thids
}
You need to connect it via #IBOutlet to file.
Then you can use its index to do whatever you want.
#IBOutlet var segmentedControl: UISegmentedControl!
#IBAction func calculateButton(_ sender: Any) {
let index = segmentedControl.selectedSegmentIndex
//...
}
Putting the code in the IBAction method connected via the storyboard or Xib file is the correct approach. You can use strong types when hooking up the action to the control and write code such as:
#IBAction func calculateButton(_ sender: UISegmentedControl) {
switch(sender.selectedSegmentIndex){
case 1:
// Do something
case 2:
// Do something
case 3:
// Do something
default:
break
}
}
You would need to create an #IBOutlet var segmentedControl: UISegmentedControl? property that connects to the segmented control in the Interface Builder, and then in the calculateButton method you can switch over segmentedControl.selectedSegmentIndex. Let me know if you need further explanation.

Swift 3 changing the title of a UIButton

I have been looking around the web and I am unable to find a solution for my problem. I want my "START" button text to change to "RESTART" once the game has ended. If any additional information is needed please let me know. Thanks, all help is appreciated. Below is my code.
class ViewController: UIViewController {
var gameOver = false
var stopFuncs = false
var gameTimer = Timer()
var counter = 10
var selected = "NONE"
var score = 0
var colour = "NONE"
#IBOutlet weak var colourLabel: UILabel!
let colourProvider = ColourProvider()
#IBOutlet weak var scoreLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
colour = colourProvider.randomColour()
print(colour)
colourLabel.text = colour
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBOutlet weak var counterLabel: UILabel!
func updateCounter() {
if counter >= 0 {
counterLabel.text = String(counter)
counter -= 1
}
}
#IBAction func startButton() {
gameOver = false
counter = 10
stopFuncs = false
var _ = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateCounter), userInfo: nil, repeats: true)
}
#IBAction func greenButton() {
if stopFuncs { return }
compareColours(selectedColour: "GREEN")
changeColourLabel()
colourLabel.textColor = UIColor.green
}
#IBAction func blueButton() {
if stopFuncs { return }
compareColours(selectedColour: "BLUE")
changeColourLabel()
colourLabel.textColor = UIColor.blue
}
#IBAction func yellowButton() {
if stopFuncs { return }
compareColours(selectedColour: "YELLOW")
changeColourLabel()
colourLabel.textColor = UIColor.yellow
}
#IBAction func pinkButton() {
if stopFuncs { return }
compareColours(selectedColour: "GREEN")
changeColourLabel()
colourLabel.textColor = UIColor.red
}
func compareColours(selectedColour: String) {
if gameOver == false && counter >= 1 {
if selectedColour == colour {
score += 1
let scoreString = String(score)
scoreLabel.text = scoreString
}
else {
gameOver = true
}
}
else {
stopFuncs = true
}
}
func changeColourLabel() {
colour = colourProvider.randomColour()
colourLabel.text = colour
print(colour)
}
}
You already have IBOutlets to some of your labels.
For buttons you may need to set up both an IBaction and an IBOutlet.
An IBOutlet is a thing. A noun. It's a reference to a view object.
An IBAction is a verb. An action. It's code that gets called when something happens to a UIControl (tapping a button, moving a slider, changing the selected value on a segmented control, etc.)
The IBaction is a function that gets called when the user interacts with a control (For buttons you almost always link the action to the .touchUpInside control event.)
Naming your actions with names like pinkButton is a bad idea, and leads to confusion. I suggest renaming your actions to verbs, like handlePinkButton (or perhaps "pinkButtonTapped", but I prefer action names to be verbs.)
Note that when you rename an IBAction and/or IBOutlet you have to go into IB (IB = Interface Builder), select the connections inspector, remove the now-incorrect IBOutlet/IBAction and control-drag from the object to the updated outlet/action.
If you don't do that you'll get very confusing crashes at runtime:
Renaming IBOutlets in code without also changing them in IB gives the error "this class is not key value coding-compliant for the key old_outlet_name" (where "old_outlet_name" will be the old name of your outlet.) when you try to load the view controller from the storyboard. Renaming actions )
Renaming IBActions in code without also changing them in IB gives an error "unrecognized selector sent to instance big_hex_number" when you try to tap the button (or trigger the action for other controls.)
You should open your storyboard in the main editor and open the source in the assistant editor, then control-drag from your button in IB into your source code just below your other outlet at the top of your class.
Your button needs to be an IBOutlet rather than an IBAction. When you ctrl-drag from Interface Builder to your code, make sure you select the 'Outlet' option as shown below:
Then you can change the button title like this:
else {
startButton.setTitle("Button Title", for: .normal)
stopFuncs = true
}

UISegmentedControl with multiple row

I need a UISegmentedControl with 2 row, I could not implement it.
I dont want to use 2 different UISegmentedControl. Are there any solution to this problem? How to implement UISegmentedControl with two rows?
'UISegmentedControl' does not support multiple rows. If you need it you'll have to build it yourself or find a third party library that offers it.
As Helium suggests in his/her comment, you could create a custom control that internally manages 2 or more segmented controls and handles changing states between them.
Truth be told a segmented control is not that complicated. It wouldn't be that hard to create a custom 2D segmented control. If this is a key need then you might want to go that route.
Basically, you will need to have an IBAction and IBOutlet on each of your segmented control.
Code the IBAction such that clicking on one of the segmented control will set the IBOutlet's selectedSegmentIndex of the other segmented controls(which are not selected) to UISegmentedControlNoSegment. This creates the illusion that the segmented controls are linked.
Then getting the correct info of which segmented control value is selected is simply gathering all the index of the segmented control into one constant.
#IBOutlet weak var segment1: UISegmentedControl!
#IBOutlet weak var segment2: UISegmentedControl!
#IBOutlet weak var segment3: UISegmentedControl!
#IBAction func FirstSecond(_ sender: UISegmentedControl) {
segment2.selectedSegmentIndex = UISegmentedControlNoSegment
segment3.selectedSegmentIndex = UISegmentedControlNoSegment
}
#IBAction func ThirdFourth(_ sender: UISegmentedControl) {
segment1.selectedSegmentIndex = UISegmentedControlNoSegment
segment3.selectedSegmentIndex = UISegmentedControlNoSegment
}
#IBAction func FifthSixth(_ sender: UISegmentedControl) {
segment1.selectedSegmentIndex = UISegmentedControlNoSegment
segment2.selectedSegmentIndex = UISegmentedControlNoSegment
}
let segmentvalue = ["First","Second","Third","Fourth","Fifth","Sixth"]
#IBAction func FirstSecond(_ sender: UISegmentedControl) {
segment2.selectedSegmentIndex = UISegmentedControlNoSegment
segment3.selectedSegmentIndex = UISegmentedControlNoSegment
let valueINeed = segmentvalue[segment1.selectedSegmentIndex]
}
#IBAction func ThirdFourth(_ sender: UISegmentedControl) {
segment1.selectedSegmentIndex = UISegmentedControlNoSegment
segment3.selectedSegmentIndex = UISegmentedControlNoSegment
let valueINeed = segmentvalue[segment2.selectedSegmentIndex + 2]
}
#IBAction func FifthSixth(_ sender: UISegmentedControl) {
segment1.selectedSegmentIndex = UISegmentedControlNoSegment
segment2.selectedSegmentIndex = UISegmentedControlNoSegment
let valueINeed = segmentvalue[segment3.selectedSegmentIndex + 4]
}
Full Details availabe here: multi row segemented control in swift

Default UIView using Segmented Control?

I'm using a UISegmentControl to switch between 2 UIViews, one to select a user and the second to show information about that user.
The issue I am having is the UIView that shows initially when navigating to this page is dependant on the placement in the storyboard hierarchy.
I would like the "selectionView" to always be the default screen that comes up when navigating to this page.
The segment tab itself is correct with selectedSegmentIndex = 0 and works as expected once I switch between them, but the only I've been able to correct this issue is to change the position of the views in the storyboard.
How would I be able to do this programmatically?
import UIKit
#IBOutlet weak var segmentController: UISegmentedControl!
#IBAction func segmentIndexChanged(sender: UISegmentedControl) {
switch segmentController.selectedSegmentIndex {
case 0: selectionView.hidden = false; infoView.hidden = true
case 1: selectionView.hidden = true; infoView.hidden = false
default: break
}
}
#IBOutlet weak var selectionView: UIView!
#IBOutlet weak var infoView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
segmentController.setTitle("Select User", forSegmentAtIndex: 0)
segmentController.setTitle("User Info", forSegmentAtIndex: 1)
segmentController.selectedSegmentIndex = 0
}
In viewDidLoad Add below code
override func viewDidload
{
......
selectionView.hidden = false
infoView.hidden = true
.......
}

Is it possible to get button title from IB in class?

I have several buttons with titles 0 - 9 and in my custom UIButton class I want to be able to use a switch to check the button title. The buttons are created in the storyboard. The UIButton class looks like:
protocol NumpadDelegate: class {
func changeValue()
}
class NumpadButton: UIButton, NumpadDelegate {
let initial = String() // I want to assign the title of the button here
var alternative = String()
var change: Bool = false {
didSet {
let title = change ? alternative : initial
setTitle(title, forState: .Normal)
}
}
func changeValue() {
change = !change
}
}
Is there a way to do that or I'll have to programmatically create the buttons? I'm trying to limit the use of a custom UIButton class so I don't have to create one for each of my buttons.
EDIT:
As requested, the code of when the button is pressed:
#IBAction func buttonNumber(sender: UIButton) {
let value = sender.titleLabel!.text!
switch value {
case "C": orderLabel.text = ""
default: orderLabel.text = orderLabel.text.map { count($0) < 10 ? $0 + value : $0 } ?? value
}
if let check = orderLabel.text {
inputResult.removeAll(keepCapacity: false)
for index in 0..<items.count {
let id = items[index].id
if id.lowercaseString.hasPrefix(check.lowercaseString) {
inputResult += [items[index]]
}
}
numpadTableView.reloadData()
}
}
What I'm trying to do is, when I swipe the buttons I want their titles to change to an alternative String i.e. button with title 1 will change to A and button with title 2 will change to B.
Instead of assigning your buttons to a 'referencing outlet' in Interface Builder, assign them to a 'referencing outlet collection' which you can declare as
#IBOutlet var buttons: [NumpadButton]
Don't know exactly what you're going for but here's an example where I've created outlets for each NumpadButton and added them to an array.
class ViewController: UIViewController {
#IBOutlet weak var button1: NumpadButton!
#IBOutlet weak var button2: NumpadButton!
#IBOutlet weak var button3: NumpadButton!
#IBOutlet weak var button4: NumpadButton!
var array: NSMutableArray = []
override func viewDidLoad() {
super.viewDidLoad()
array.addObject(button1)
array.addObject(button2)
array.addObject(button3)
array.addObject(button4)
}
Each button has a title of either "1", "2", "3" or "4". Then you can check the title via a case statement like so:
override func viewWillAppear(animated: Bool) {
for item in array {
var button = item as! UIButton
switch button.currentTitle! {
case "1":
println("This button has title of 1")
case "2":
println("This button has title of 2")
case "3":
println("This button has title of 3")
case "4":
println("This button has title of 4")
default:
println("default case")
}
}
}
}
This would be one way you could use a case statement to access the titles of UIButtons and then just adjust the application to whatever functionality you want (e.g. through an IBAction, etc.).

Resources