0

I have seen the other question, Swift - Segmented control - Switch multiple views, which would work if I had the segment control in the view. However, I have a segment control created programmatically in my viewDidLoad function and that segment control is set as the titleView for the navigation bar. It will not let me reference that segment control since it isn't in the nib file. I also tried the whole string of referencing it by self.navigationController?.navigationBar.topItem?.titleView.customSC.selectedSegmentIndex but that didn't work either.

Code:

class MyVC: UIViewController {

var webviewURL: NSURL = NSURL(string: "myURL")!
var webviewURL2: NSURL = NSURL(string: "myURL")!
var webviewURL3: NSURL = NSURL(string: "myURL")!
var webviewURL4: NSURL = NSURL(string: "myURL")!

@IBOutlet weak var webView: UIWebView!

@IBAction func indexChanged(sender: UISegmentedControl) {
    switch customSC.selectedSegmentIndex { //error here
    case 0:
        let requestObj = NSURLRequest(URL: webviewURL)
        webView.loadRequest(requestObj)
    case 1:
        let requestObj = NSURLRequest(URL: webviewURL2)
        webView.loadRequest(requestObj)
    case 2:
        let requestObj = NSURLRequest(URL: webviewURL3)
        webView.loadRequest(requestObj)
    case 3:
        let requestObj = NSURLRequest(URL: webviewURL4)
        webView.loadRequest(requestObj)
    default:
        break;
    }
}

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    super.init(nibName: "myVC", bundle: nil)
}
convenience init() {
    self.init(nibName: "myVC", bundle: nil)
    //set the tab bar item's title
    tabBarItem.title = "title"

    //put an image on the tab bar item
    tabBarItem.image = UIImage(named: "image")
}
required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}


override func viewDidLoad() {
    super.viewDidLoad()

    let logo = UIImage(named: "image")
    let logoView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 80))
    logoView.contentMode = .ScaleAspectFit
    logoView.image = logo
    let leftLogo = UIBarButtonItem(customView: logoView)
    self.navigationItem.leftBarButtonItem = leftLogo

    let items = ["Tab 1", "Tab 2", "Tab 3", "Tab 4"]
    let customSC = UISegmentedControl(items: items)
    customSC.selectedSegmentIndex = 0
    navigationController?.navigationBar.topItem?.titleView = customSC
    navigationController?.navigationBar.translucent = false

    let requestObj = NSURLRequest(URL: webviewURL)
    webView.loadRequest(requestObj)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}
}

2 Answers 2

3

At first, if you're not using Storyboards/XIBs/InterfaceBuilder and doing everything programmatically, you can remove @IBAction, as it means Interface Builder Action.

Secondly, it is good idea to put segmented control as a variable, so you can access it easier when needed:
var customSC: UISegmentedControl - just after class MyVC: UIViewController {.
But Swift requires all variables to be initialized before calling a superclass designated initializer - init(nibName nibNameOrNil:, so, let's initialize it: it is possible to initialize variables in it's declaration:

Change var customSC: UISegmentedControl to

  var customSC: UISegmentedControl = {
    let items = ["Tab 1", "Tab 2", "Tab 3", "Tab 4"]
    let control = UISegmentedControl(items: items)
    control.selectedSegmentIndex = 0
    return control
    }()

So, we don't need to configure Segmented Control in viewDidLoad method.
The problem, why you your control wasn't working is that you haven't added target to it. So, when you change selected segment, the control does not sends any events to your viewcontroller. Let's fix it. Add this method to the end of viewDidLoad:
customSC.addTarget(self, action: "indexChanged:", forControlEvents: UIControlEvents.ValueChanged)
Now, when you press any of the segments, the method indexChanged(sender: UISegmentedControl) will be called and the view controller will be able to react.
It would be easier to put segmented control on top of navigation bar this way: navigationItem.titleView = customSC, it is much shorter than this: navigationController?.navigationBar.topItem?.titleView = customSC

When using Interface Builder it is OK to use weak modifiers for WebView as it is being automatically added to the main view of the controller and being held in memory. But if you add it programmatically, you should avoid using weak in this context in order to prevent undesired behavior (Web View will be deallocated as soon as there are no strong (i.e. not weak) links to it, so basically, it will be deallocated immediately after creation and you end up with BAD ACCESS error. To fix it, just change this: weak var webView: UIWebView! to this var webView: UIWebView!.

So, after small refactoring, we may finish with something close to this:

import UIKit
class MyVC: UIViewController {

  var webviewURL: NSURL = NSURL(string: "myURL")!
  var webviewURL2: NSURL = NSURL(string: "myURL")!
  var webviewURL3: NSURL = NSURL(string: "myURL")!
  var webviewURL4: NSURL = NSURL(string: "myURL")!

  var webView: UIWebView!

  var customSC: UISegmentedControl = {
    let items = ["Tab 1", "Tab 2", "Tab 3", "Tab 4"]
    let control = UISegmentedControl(items: items)
    control.selectedSegmentIndex = 0
    return control
    }()

   func indexChanged(sender: UISegmentedControl) {
    switch customSC.selectedSegmentIndex { //error here
    case 0:
      let requestObj = NSURLRequest(URL: webviewURL)
      webView.loadRequest(requestObj)
    case 1:
      let requestObj = NSURLRequest(URL: webviewURL2)
      webView.loadRequest(requestObj)
    case 2:
      let requestObj = NSURLRequest(URL: webviewURL3)
      webView.loadRequest(requestObj)
    case 3:
      let requestObj = NSURLRequest(URL: webviewURL4)
      webView.loadRequest(requestObj)
    default:
      break;
    }
  }

  override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
    super.init(nibName: "myVC", bundle: nil)
  }

  convenience init() {
    self.init(nibName: "myVC", bundle: nil)
    //set the tab bar item's title
    tabBarItem.title = "title"

    //put an image on the tab bar item
    tabBarItem.image = UIImage(named: "image")
  }
  required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }


  override func viewDidLoad() {
    super.viewDidLoad()

    let logo = UIImage(named: "image")
    let logoView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 80))
    logoView.contentMode = .ScaleAspectFit
    logoView.image = logo
    let leftLogo = UIBarButtonItem(customView: logoView)
    self.navigationItem.leftBarButtonItem = leftLogo

    navigationItem.titleView = customSC
    navigationController?.navigationBar.translucent = false

    let requestObj = NSURLRequest(URL: webviewURL)
    webView.loadRequest(requestObj)
  }
}
Sign up to request clarification or add additional context in comments.

Comments

1

You need create property for UISegmentedControl object, and initialize it in viewDidLoad method:

var segmentedControl: UISegmentedControl!

override func viewDidLoad() {
    super.viewDidLoad()

    let logo = UIImage(named: "image")
    let logoView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 80))
    logoView.contentMode = .ScaleAspectFit
    logoView.image = logo
    let leftLogo = UIBarButtonItem(customView: logoView)
    self.navigationItem.leftBarButtonItem = leftLogo

    let items = ["Tab 1", "Tab 2", "Tab 3", "Tab 4"]
    let customSC = UISegmentedControl(items: items)
    customSC.selectedSegmentIndex = 0
    navigationController?.navigationBar.topItem?.titleView = customSC
    navigationController?.navigationBar.translucent = false

    // Store instance of UISegmentedControl in property
    self.segmentedControl = customVC

    let requestObj = NSURLRequest(URL: webviewURL)
    webView.loadRequest(requestObj)
}

Then you will be able access it: self.segmentedControl.selectedSegmentIndex.

If you need handle user interaction with segmented control add this code to viewDidLoad:

self.segmentedControl.addTarget(self, action: "onSegmentedControlValueChanged:", forControlEvents: UIControlEvents.ValueChanged)

Also add this method to your class:

func onSegmentedControlValueChanged(segmentedControl: UISegmentedControl) {
    let newIndex = segmentedControl.selectedSegmentIndex
    // Use newIndex as needed
}

1 Comment

Okay, thanks for this I can now reference it. Do you know how I would connect my programmatic segmented control to the IBAction indexChanged? Nothing is currently happening for me when I select a different Tab on my SegmentedControl.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.