I wanted to display 6 icons on a tab bar in a specific order. For that in the storyboard I have dragged a tab-controller and added 6 viewcontrollers to it. I also added an image to each of the tab bars and also made different viewcontrollers for each of the tab bars.
But the issue is the images/viewcontrollers aren't displaying as per the order specified in the storyboard.
I have a .swift file also for the tabbarcontroller. It has some code for configuring the tab bars given like so...
class SampleTabBarController: UITabBarController, UIGestureRecognizerDelegate {
// MARK: - Shared Instance
static var shared: SampleTabBarController?
// MARK: - Public Properties
var homeTitle = "Home"
var connectionsTitle = "Connections"
var flightTitle = "Flights"
var messagesTitle = "Messages"
var companyProfileTitle = "Company Profile"
var myCalendarTitle = "My Calendar"
// MARK: - Private Properties
fileprivate var homeNavigation = "HomeNavigation"
fileprivate var home: HomeViewController? = nil
fileprivate var homeIndex = 0
fileprivate var connectionsNavigation = "ConnectionsNavigation"
fileprivate var connections: ConnectionsViewController? = nil
fileprivate var connectionsIndex = 1
fileprivate var flightsNavigation = "FlightsNavigation"
fileprivate var flights: FlightsViewController? = nil
fileprivate var flightsIndex = 2
fileprivate var messagesNavigation = "MessagesNavigation"
fileprivate var messages: MessagesViewController? = nil
fileprivate var messagesIndex = 3
fileprivate var companyProfileNavigation = "CompanyProfileNavigation"
fileprivate var compProfile: CompanyProfileViewController? = nil
fileprivate var compProfileIndex = 4
fileprivate var myCalendarNavigation = "MyCalendarNavigation"
fileprivate var myCalendar: MyCalendarViewController? = nil
fileprivate var myCalendarIndex = 5
fileprivate var previousIndex = 0
override func viewDidLoad() {
super.viewDidLoad()
SampleTabBarController.shared = self
initialConfiguration()
}
}
extension SampleTabBarController {
private func initialConfiguration() {
self.tabBarController(SampleTabBarController.shared!,
didEndCustomizing: SampleTabBarController.shared!.viewControllers!,
changed: true)
configureViewControllers()
}
fileprivate func configureViewControllers() {
// Home
if let viewControllers = self.viewControllers {
let navigationControllers = viewControllers.filter { $0.restorationIdentifier == homeNavigation }
if let navigation = navigationControllers.first as? UINavigationController ,
let homeView = navigation.viewControllers[0] as? HomeViewController {
self.home = homeView
}
}
// Connections
if let viewControllers = self.viewControllers {
let navigationControllers = viewControllers.filter { $0.restorationIdentifier == connectionsNavigation }
if let navigation = navigationControllers.first as? UINavigationController ,
let connectionView = navigation.viewControllers[0] as? ConnectionsViewController {
self.connections = connectionView
}
}
// Flight
if let viewControllers = self.viewControllers {
let navigationControllers = viewControllers.filter { $0.restorationIdentifier == flightsNavigation }
if let navigation = navigationControllers.first as? UINavigationController ,
let flightView = navigation.viewControllers[0] as? FlightsViewController {
self.flights = flightView
}
}
// Messages
if let viewControllers = self.viewControllers {
let navigationControllers = viewControllers.filter { $0.restorationIdentifier == messagesNavigation }
if let navigation = navigationControllers.first as? UINavigationController ,
let messagesView = navigation.viewControllers[0] as? MessagesViewController {
self.messages = messagesView
}
}
// Company Profile
if let viewControllers = self.viewControllers {
let navigationControllers = viewControllers.filter { $0.restorationIdentifier == companyProfileNavigation }
if let navigation = navigationControllers.first as? UINavigationController ,
let companyProfileView = navigation.viewControllers[0] as? CompanyProfileViewController {
self.compProfile = companyProfileView
}
}
// My Calendar
if let viewControllers = self.viewControllers {
let navigationControllers = viewControllers.filter { $0.restorationIdentifier == myCalendarNavigation }
if let navigation = navigationControllers.first as? UINavigationController ,
let calendarView = navigation.viewControllers[0] as? MyCalendarViewController {
self.myCalendar = calendarView
}
}
}
}
// MARK: - UITabBarControllerDelegate
extension SampleTabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
if previousIndex != tabBarController.selectedIndex {
previousIndex = tabBarController.selectedIndex
var elementText = ""
var elementValue = ""
switch previousIndex {
case homeIndex:
elementText = homeTitle
elementValue = BottomBarItems.home.rawValue
case connectionsIndex:
elementText = connectionsTitle
elementValue = BottomBarItems.connections.rawValue
case flightsIndex:
elementText = flightTitle
elementValue = BottomBarItems.flight.rawValue
case messagesIndex:
elementText = messagesTitle
elementValue = BottomBarItems.messages.rawValue
case compProfileIndex:
elementText = companyProfileTitle
elementValue = BottomBarItems.companyProfile.rawValue
case myCalendarIndex:
elementText = myCalendarTitle
elementValue = BottomBarItems.myCalendar.rawValue
default:
break
}
}
}
func tabBarController(_ tabBarController: UITabBarController, didEndCustomizing viewControllers: [UIViewController], changed: Bool) {
for (index, viewcontroller) in viewControllers.enumerated() {
// Home
if viewcontroller.restorationIdentifier == homeNavigation {
homeIndex = index
}
// Connections
if viewcontroller.restorationIdentifier == connectionsNavigation {
connectionsIndex = index
}
// Flight
if viewcontroller.restorationIdentifier == flightsNavigation {
flightsIndex = index
}
// Messages
if viewcontroller.restorationIdentifier == messagesNavigation {
messagesIndex = index
}
// Company Profile
if viewcontroller.restorationIdentifier == companyProfileNavigation {
compProfileIndex = index
}
// My Calendar
if viewcontroller.restorationIdentifier == myCalendarNavigation {
myCalendarIndex = index
}
}
}
}
But this doesn't seem to work...what is it that I'm doing wrong...?
Related
I have a Tab bar Controller to manage all the views. The problem I'm having is when I call a function in searchViewController (doAThing()) from HomeViewController which reloads the tableView in searchViewController, the tableView is empty when the Tab bar controller switches views.
Why does calling the doAThing method in my searchViewController not refresh my tableView?
How can I fill my tableView with values.
HomeViewController.swift
import UIKit
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UISearchBarDelegate{
#IBOutlet weak var productCollectionView: UICollectionView!
#IBOutlet weak var storesCollectionView: UICollectionView!
#IBOutlet var searchQ: UISearchBar!
#IBOutlet weak var scrollView: UIScrollView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if(collectionView == storesCollectionView) {
return storesImages.count
}
return productsImages.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if(collectionView == storesCollectionView) {
let cell2 = storesCollectionView.dequeueReusableCell(withReuseIdentifier: "storesCell", for: indexPath) as! StoreCollectionViewCell
cell2.compstoreImage.image = UIImage(named: storesImages[indexPath.row])
return cell2
}
else{
let cell = productCollectionView.dequeueReusableCell(withReuseIdentifier: "productsCell", for: indexPath) as! ProductCollectionViewCell
cell.pillImage.image = UIImage(named: productsImages[indexPath.row])
return cell
}
}
var productsImages:[String] = ["pcPic", "picturePC"]
var storesImages:[String] = ["newarkStore", "compeStore"]
override func viewDidLoad() {
super.viewDidLoad()
searchQ.delegate = self
scrollView.contentSize = CGSize(width: self.view.frame.width, height: self.view.frame.height)
// searchQ.delegate = self
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//self.tabBarController?.tabBar.isHidden = false
}
func searchBarSearchButtonClicked(_ searchQ: UISearchBar)
{
print("*********")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let resultViewController = storyBoard.instantiateViewController(withIdentifier: "SearchViewController") as! searchViewController
// resultViewController.searchQuery = searchQ
resultViewController.doAthing(searchQ)
self.tabBarController?.selectedIndex = 3
// NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
resultViewController.resultsView.reloadData()
}
// func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
//
// print("*********")
//
// let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "SearchViewController") as! searchViewController
// secondViewController.doAthing(searchBar)
// self.navigationController!.pushViewController(secondViewController, animated: true)
//
//
//
// //let titles: Elements = try doc.select("a[product-thumb]")
// //let titles: String = try doc.select("a").attr("product-thumb")
//
// // print(titles
//
// hideKeyboardWhenTappedAround()
//
// }
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
searchViewController.swift
import UIKit
import SwiftSoup
class searchViewController: UIViewController{
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var resultsView: UITableView!
#IBOutlet var searchQuery: UISearchBar!
// let content = try! String(contentsOf: URL(string: "https://www.locally.com/search/all/activities/depts?q=bottle")!)
// let doc: Document = try! SwiftSoup.parse(content)
var products: [String] = []
//let products = ["Computer", "PC", "Laptop"]
let stores = ["Computer Central", "Fry's Electronics", "Best Buy"]
//var stores: [String] = []
let into = ["Custom PC with high performanc. Perfect for gaming and streaming. Great condition", "Custom PC with high performanc. Perfect for gaming and streaming. Great condition", "Custom PC with high performanc. Perfect for gaming and streaming. Great condition"]
var dollars: [String] = []
//let dollars = [100, 220, 129, 100, 220, 129]
let likes = [10, 24, 24, 24 , 456, 46, 46]
//let miles = ["3.2 mi", "4.1 mi", "6.3 mi", "3.2 mi", "4.1 mi", "6.3 mi"]
var miles: [String] = []
// let names = ["Central Computers", "CompE", "geekStore", "Central Computers", "CompE", "geekStore"]
var names: [String] = []
let numbers = ["510-329-0172", "510-456-7345", "510-329-0172", "510-329-0172", "510-456-7345", "510-329-0172"]
var images: [UIImage] = []
var categories: [String] = []
var descriptions: [String] = []
var stars: [String] = []
var ratings: [Double] = []
var ratingImage: [[UIImage]] = [[]]
var phones: [String] = []
var cities: [String] = []
//var webCounter:Int = 0
var x:Double = 0
var y:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "load"), object: nil)
searchQuery.delegate = self
resultsView.delegate = self
resultsView.dataSource = self
//set the height of each row in tableview
self.resultsView.rowHeight = 200.0
// Do any additional setup after loading the view.
}
#objc func loadList(notification: NSNotification) {
//load data here
self.resultsView.reloadData()
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
//searchBar.inputViewController?.dismissKeyboard()
searchBar.inputViewController?.dismiss(animated: true)
doAthing(searchBar)
self.searchQuery.endEditing(true)
self.resultsView.keyboardDismissMode = .onDrag
hideKeyboardWhenTappedAround()
}
func doAthing(_ searchBar: UISearchBar) {
do {
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.medium
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
print("reached here")
let html = try String(contentsOf: URL(string: "https://www.locally.com/search/all/activities/depts?q=" + searchBar.text!)!)
//let doc: Document = try SwiftSoup.parse(html)
guard let titles: Elements = try? SwiftSoup.parse(html).getElementsByClass("product-thumb ") else {return}//select("a") else {return}
guard let prices: Elements = try? SwiftSoup.parse(html).getElementsByClass("product-thumb-price dl-price") else {return}
guard let Stores: Elements = try? SwiftSoup.parse(html).getElementsByClass("filter-label-link") else {return}
guard let Images: Elements = try? SwiftSoup.parse(html).getElementsByClass("product-thumb-img") else {return}
products = []
miles = []
dollars = []
names = []
images = []
stars = []
for title: Element in titles.array() {
print("title" + String(titles.size()))
products.append(try! title.attr("data-product-name"))
guard let url: URL = try? URL(string: "https://www.locally.com/" + String(try! title.attr("href")))
else
{
miles.append("-")
continue
}
let html1 = try String(contentsOf: url)
guard let distances: Element = try? SwiftSoup.parse(html1).getElementsByClass("conv-section-distance dl-store-distance").first()
else
{
miles.append("-")
continue
}
print(try! distances.ownText())
miles.append(try! distances.ownText())
guard let K: Array<String> = try? SwiftSoup.parse(html1).getElementsByClass("breadcrumbs container").eachText() else {return}
let str = (try! K.last!.components(separatedBy: "/") )
categories.append(str[str.count - 2])
print(str[str.count - 2])
guard let desc: Array<String> = try? SwiftSoup.parse(html1).getElementsByClass("pdp-information").eachText() else {return}
guard let s:String = try? desc[1]
else {
return
}
descriptions.append( String( s.suffix(s.count - 19) ) )
print( String( s.suffix(s.count - 19) ) )
guard let star: Element = try? SwiftSoup.parse(html1).getElementsByClass("stars").first()
else
{
print("no reviews")
ratings.append(0)
continue
}
print(try! star.attr("data-rating"))
//ratings.append(try! star.attr("data-rating")) ?? ()
let x = Double(try! star.attr("data-rating")) ?? 0
print("y: " + String(Int(x)))
ratings.append(x)
guard let locations: Element = try? SwiftSoup.parse(html1).getElementsByClass("conv-section-store-address section-subtitle dl-store-address js-store-location").first()
else
{
print("cant find city")
return
}
let string = locations.ownText()
print("location: " + String(string.prefix(string.count - 10)) )
cities.append(try! String(string.prefix(string.count - 10)))
guard let phoneNums: Element = try? SwiftSoup.parse(html1).getElementsByClass("selected-retailer-info-link btn-action-sm tooltip").first()
else
{
print("link not found")
return
}
guard let urls:URL = try? URL(string: "https://www.locally.com/" + phoneNums.attr("href") )
else
{
return
}
let html2 = try String(contentsOf: urls )
guard let storePage:Element = try? SwiftSoup.parse(html2).getElementsByClass("landing-page-phone-label").first() else {
print("Phone Number not found")
return
}
let sp = try? storePage.ownText
if let s = sp {
phones.append(try! s())
}
else {
phones.append("N/A")
}
print(try! storePage.ownText())
//
// let html1 = try String(contentsOf: url)
}
for price: Element in prices.array() {
print("prices" + String(prices.size()))
print(String(try! price.ownText()))
dollars.append(try! price.ownText())
}
for store: Element in Stores.array() {
print("Stores" + String(Stores.size()))
names.append(try! store.ownText()) ?? names.append("N/A")
}
for image: Element in Images.array() {
guard let url = URL(string: try! image.attr("src") ) else { return }
let data = try? Data(contentsOf: url)
if let imageData = data {
images.append( UIImage(data: imageData)! )
}
else {
images.append(UIImage(named: "pcPic")!)
}
//images.append(try! image.downloaded(from: image.attr("src")))
}
dismiss(animated: false, completion: nil)
resultsView.reloadData()
//let titles: Elements = try doc.select("a[product-thumb]")
//let titles: String = try doc.select("a").attr("product-thumb")
// print(titles)
} catch Exception.Error(type: let type, Message: let message) {
print(type)
print(message)
} catch {
print("")
}
}
// override func viewWillAppear(_ animated: Bool) {
// super.viewWillAppear(animated)
// self.tabBarController?.tabBar.isHidden = false
// }
//When user changes segment, tableview is reloaded
#IBAction func segmentChanged(_ sender: Any) {
resultsView.reloadData();
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
func imageWithImage(image: UIImage, scaledToSize newSize: CGSize) -> UIImage {
UIGraphicsBeginImageContext(newSize)
image.draw(in: CGRect(x: 0 ,y: 20 ,width: newSize.width ,height: newSize.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!.withRenderingMode(.alwaysOriginal)
}
}
extension searchViewController: UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("reached here")
switch segmentedControl.selectedSegmentIndex {
case 0:
return products.count
case 1:
return stores.count
case 2:
return (products.count + stores.count)
default:
break
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "resultsTableViewCell") as! resultsTableViewCell
switch segmentedControl.selectedSegmentIndex {
case 0:
cell.titleLabel?.text = products[indexPath.row]
cell.productImage?.image = images[indexPath.row]
// cell.descriptionLabel?.text = into[indexPath.row]
// cell.descriptionLabel?.numberOfLines = 0
cell.price?.text = String(dollars[indexPath.row])
cell.distance?.text = miles[indexPath.row]
cell.storeName?.numberOfLines = 0
cell.storeName?.text = names[indexPath.row]
cell.phoneNumber?.text = phones[indexPath.row]
let ratNumber = ratings[indexPath.row]
if( ratNumber == 0 )
{
cell.rating?.text = "No Reviews"
}
else
{
cell.rating?.text = String(ratNumber)
}
if(ratNumber > 4.5)
{
cell.star1.image = UIImage(named: "regular_5")
}
else if(ratNumber > 4.0)
{
cell.star1.image = UIImage(named: "regular_4_half")
}
else if(ratNumber == 4.0)
{
cell.star1.image = UIImage(named: "regular_4")
}
else if(ratNumber > 3.0)
{
cell.star1.image = UIImage(named: "regular_3_half")
}
else if(ratNumber == 3.0)
{
cell.star1.image = UIImage(named: "regular_3")
}
else if(ratNumber > 2.0)
{
cell.star1.image = UIImage(named: "regular_2_half")
}
else if(ratNumber == 2.0)
{
cell.star1.image = UIImage(named: "regular_2")
}
else if(ratNumber > 1.0)
{
cell.star1.image = UIImage(named: "regular_1_half")
}
else if(ratNumber == 1.0)
{
cell.star1.image = UIImage(named: "regular_1")
}
else
{
cell.star1.image = UIImage(named: "regular_0")
}
cell.rating?.numberOfLines = 0
// cell.phoneNumber?.text = numbers[indexPath.row]
case 1:
cell.titleLabel?.text = stores[indexPath.row]
cell.productImage?.image = imageWithImage(image: UIImage.init(named: "pcPic")!, scaledToSize: CGSize(width: 400, height: 300))
// cell.descriptionLabel?.text = into[indexPath.row]
// cell.descriptionLabel?.numberOfLines = 0
cell.price?.text = String(dollars[indexPath.row])
cell.distance?.text = miles[indexPath.row]
cell.storeName?.text = names[indexPath.row]
cell.phoneNumber?.text = numbers[indexPath.row]
case 2:
var all = products + stores
all.shuffle()
let alls = into + into
cell.titleLabel?.text = all[indexPath.row]
cell.productImage?.image = imageWithImage(image: UIImage.init(named: "pcPic")!, scaledToSize: CGSize(width: 400, height: 300))
// cell.descriptionLabel?.text = alls[indexPath.row]
// cell.descriptionLabel?.numberOfLines = 0
cell.price?.text = String(dollars[indexPath.row])
cell.distance?.text = miles[indexPath.row]
cell.storeName?.text = names[indexPath.row]
cell.phoneNumber?.text = numbers[indexPath.row]
default:
break
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// let vc = self.storyboard?.instantiateViewController(withIdentifier: "ShopViewController") as! ShopViewController
// self.present(vc, animated: true, completion: nil)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationVC = storyboard.instantiateViewController(withIdentifier: "ProductViewController") as! ProductViewController
destinationVC.ktitle = products[indexPath.row]
destinationVC.kprice = dollars[indexPath.row]
// destinationVC.kdescription = [indexPath.row]
destinationVC.kimage = images[indexPath.row]
// destinationVC.klikes = likes[indexPath.row]
destinationVC.kcategory = categories[indexPath.row]
destinationVC.kdescription = descriptions[indexPath.row]
destinationVC.kname = names[indexPath.row]
destinationVC.kmiles = miles[indexPath.row]
destinationVC.klocation = cities[indexPath.row]
self.present(destinationVC, animated: true, completion: nil)
}
}
extension UIViewController {
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard(_:)))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
}
#objc func dismissKeyboard(_ sender: UITapGestureRecognizer) {
view.endEditing(true)
if let nav = self.navigationController {
nav.view.endEditing(true)
}
}
}
First, it's probably not a good idea to switch to a different tab in that manner. Users understand that tapping a tab-bar icon/button takes them to a new tab. If you're jumping around in the tabs via code, it can be very confusing for the user.
But... it's your app.
The reason your code does not "refresh my tableView" is because you never actually reference that view controller.
In your code:
func searchBarSearchButtonClicked(_ searchQ: UISearchBar)
{
print("*********")
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
// here you create a NEW instance of searchViewController
let resultViewController = storyBoard.instantiateViewController(withIdentifier: "SearchViewController") as! searchViewController
// you call a func in that instance
resultViewController.doAthing(searchQ)
// you switch to tab index 3, which holds an OLD instance of searchViewController
self.tabBarController?.selectedIndex = 3
// you tell resultsView in the NEW instance to reloadData()
resultViewController.resultsView.reloadData()
// as soon as you exit this func, the NEW instance is deleted
}
You could do something like this:
func searchBarSearchButtonClicked(_ searchQ: UISearchBar)
print("*********")
// get a reference to the searchViewController that is already
// part of the tab bar controller
if let tb = self.tabBarController,
let controllers = tb.viewControllers,
let resultVC = controllers[3] as? searchViewController {
// call the func
resultVC.doAthing("new")
tb.selectedIndex = 3
}
}
However, that's really not a great approach... Just for one example, Suppose you change the order of the tabs at some point?
You're better off using either a custom tab bar controller class, or using a "data manager" class.
I'd suggest you head over to google (or your favorite search engine) and search for swift pass data between tab bar view controllers -- you'll find plenty of discussions, articles, examples, etc.
Edit -- comment
In HomeViewController replace your func searchBarSearchButtonClicked with this:
func searchBarSearchButtonClicked(_ searchQ: UISearchBar)
{
print("1 - searchBarSearchButtonClicked")
guard let btnTitle = searchQ.largeContentTitle else {
print("2 - FAILED to get searchQ.largeContentTitle")
return
}
print("2 - got btnTitle", btnTitle)
if let tb = self.tabBarController {
print("3 - got a tab bar controller reference")
if let controllers = tb.viewControllers {
print("4 - got controllers reference")
if controllers.count == 4 {
print("5 - we have 4 controllers")
if let resultVC = controllers[3] as? searchViewController {
print("6 - got a reference to searchViewController")
// if we have not yet selected the 4th tab,
// the view will not yet have been loaded
// so make sure it is
resultVC.loadViewIfNeeded()
// call the func, passing the button title
resultVC.doAthing(searchQ)
// switch to the 4th tab
tb.selectedIndex = 3
print("7 - we should now be at searchViewController")
} else {
print("6 - FAILED TO GET a reference to searchViewController")
}
} else {
print("5 - FAILED TO GET a reference to searchViewController")
}
} else {
print("4 - FAILED TO GET a reference to searchViewController")
}
} else {
print("3 - FAILED TO GET a reference to searchViewController")
}
}
Then see what messages get printed to the debug console.
In my case I have UITableView and have View all button for the listing of all the items in separate screens. So I added target for UIButton action method in cellForRowAt. Now what I am doing in action method:
#IBAction func btnViewAllOffer(_ sender: UIButton) {
let buttonPosition = sender.convert(CGPoint.zero, to: self.tblOfferView)
let indexPath = self.tblOfferView.indexPathForRow(at: buttonPosition)
if indexPath != nil {
if let type = self.homeData[indexPath!.section].type {
if type == HomeDataType.SponserProduct.rawValue {
let vc1 = self.storyboard?.instantiateViewController(withIdentifier: "ViewController1") as! ViewController1
if let title = self.homeData[indexPath!.section].title {
vc1.title = title
}
self.navigationController?.pushViewController(vc1, animated: true)
} else if type == HomeDataType.Offer.rawValue {
let vc2 = self.storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
if let title = self.homeData[indexPath!.section].title {
vc2.title = title
}
self.navigationController?.pushViewController(vc2, animated: true)
} else if type == HomeDataType.BestSeller.rawValue {
let vc3 = self.storyboard?.instantiateViewController(withIdentifier: "ViewController3") as! ViewController3
if let title = self.homeData[indexPath!.section].title {
vc3.title = title
}
self.navigationController?.pushViewController(vc3, animated: true)
}
}
}
}
What I need, is there any way I can minimize the code and assign viewcontrollers dynamically so there is no need to instantiate each view controller and push them everytime?
Something like:
var vc = UIViewController()
if let type = self.homeData[indexPath!.section].type {
if type == HomeDataType.SponserProduct.rawValue {
vc = ViewController1()
}
else if type == HomeDataType.Offer.rawValue {
vc = ViewController2()
} else if type == HomeDataType.BestSeller.rawValue {
vc = ViewController3()
}
}
self.navigationController?.pushViewController(vc, animated: true)
Use a protocol (SimilarViewController) to define the common properties like title:
protocol SimilarViewController {
var title: String? { get set }
}
class ViewController1: UIViewController, SimilarViewController {
var title: String?
}
class ViewController2: UIViewController, SimilarViewController {
var title: String?
}
class ViewController3: UIViewController, SimilarViewController {
var title: String?
}
#IBAction func btnViewAllOffer(_ sender: UIButton) {
let buttonPosition = sender.convert(CGPoint.zero, to: self.tblOfferView)
let indexPath = self.tblOfferView.indexPathForRow(at: buttonPosition)
if indexPath != nil {
if let type = self.homeData[indexPath!.section].type {
var vcGeneric: SimilarViewController?
if type == HomeDataType.SponserProduct.rawValue {
vcGeneric = self.storyboard?.instantiateViewController(withIdentifier: "ViewController1") as! ViewController1
} else if type == HomeDataType.Offer.rawValue {
vcGeneric = self.storyboard?.instantiateViewController(withIdentifier: "ViewController2") as! ViewController2
} else if type == HomeDataType.BestSeller.rawValue {
vcGeneric = self.storyboard?.instantiateViewController(withIdentifier: "ViewController3") as! ViewController3
}
if let title = self.homeData[indexPath!.section].title {
vcGeneric?.title = title
}
if let vcGeneric = vcGeneric as? UIViewController {
self.navigationController?.pushViewController(vcGeneric, animated: true)
}
}
}
}
1: create a struct and assign the value to it.
struct TitleDetails {
static var title : String = ""
}
2: create an extension of viewController and use it to avoid code repetition.
extension UIViewController {
func pushVC(_ vcName : String) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: vcname)
self.navigationController?.pushViewController(vc, animated: true)
}
}
3: now you can call it directly as,
TitleDetails.title = yourTitleValue
self.pushVC("ViewController1")
and in your ViewDidLoad() method of your destination view controller,
self.title = TitleDetails.title
Create BaseViewController and derived other ViewController from BaseViewController
class BaseViewController: UIViewController {
var viewTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func pushVC(_ vcName : String) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: vcName)
self.navigationController?.pushViewController(vc, animated: true)
}
}
And use below code on ViewController you need like:
#IBAction func btnViewAllOffer(_ sender: UIButton) {
let buttonPosition = sender.convert(CGPoint.zero, to: self.tblOfferView)
let indexPath = self.tblOfferView.indexPathForRow(at: buttonPosition)
if indexPath != nil {
if let type = self.homeData[indexPath!.section].type {
self.viewTitle = self.homeData[indexPath!.section].title
if type == HomeDataType.SponserProduct.rawValue {
self.pushVC("ViewController1")
} else if type == HomeDataType.Offer.rawValue {
self.pushVC("ViewController2")
} else if type == HomeDataType.BestSeller.rawValue {
self.pushVC("ViewController3")
}
}
}
}
I am programming an application and it is supposed to go to another view controller on a button press but instead it crashes when I run it. The editor shows this: and with editor breakpointing on it shows this: but there is no error in the console. Here is the code for the viewcontroller that does the segue:
//
// DoTodayViewController.swift
// Paley Foundation: Starfish Support
//
// Created by Sa'ar Lipshitz on 10/28/17.
//
import UIKit
class DoTodayViewController: UIViewController {
#IBOutlet weak var navItem: UINavigationItem!
#IBOutlet weak var getBtn: UIButton!
#IBOutlet weak var suggestBtn: UIButton!
var name = "Sa'ar"
override func viewDidLoad() {
super.viewDidLoad()
getBtn.layer.cornerRadius = 8
getBtn.clipsToBounds = true
suggestBtn.layer.cornerRadius = 8
suggestBtn.clipsToBounds = true
navItem.prompt = "What would you like to do today, "+name+"?"
}
#IBAction func suggest(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "SuggestViewController") as! SuggestViewController
navigationController?.pushViewController(vc, animated: true)
}
//This crashes my app:
#IBAction func get(_ sender: Any) {
let vc = storyboard?.instantiateViewController(withIdentifier: "AccessViewController") as! AccessViewController
navigationController?.pushViewController(vc, animated: true)
}
}
and here is my code for the viewcontroller that gets opened:
//
// AccessViewController.swift
// Paley Foundation: Starfish Support
//
// Created by Sa'ar Lipshitz on 10/29/17.
//
import UIKit
import Firebase
import Material
class AccessViewController: UIViewController {
var adviceRef: DatabaseReference!
var advice: [[String: Any]] = []
var i = 0
var card: Card!
var loadingLbl: UILabel!
var panGesture = UIPanGestureRecognizer()
var cardOrigPos: CGPoint!
override func viewDidLoad() {
super.viewDidLoad()
adviceRef = Database.database().reference().child("Advice")
var advDict: NSDictionary?
adviceRef.observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
advDict = value
print(advDict as Any)
for (_, adv) in advDict! {
self.advice.append(adv as! [String : Any])
}
print(self.advice)
self.showNextCard()
}) { (error) in
print(error.localizedDescription)
}
let swipeRec = UIPanGestureRecognizer(target: self, action: #selector(swipe(sender:)))
card.isUserInteractionEnabled = true
card.addGestureRecognizer(swipeRec)
}
#objc func swipe(sender: UIPanGestureRecognizer) {
let translation = sender.translation(in: view)
let velocity = sender.velocity(in: view)
let direction = velocity
print("Translation: \(translation)")
if sender.state == .began {
cardOrigPos = card.center
} else if sender.state == .changed {
card.center = CGPoint(x: cardOrigPos.x+translation.x, y: cardOrigPos.y+translation.y)
} else if sender.state == .ended {
if velocity.y == 0 && velocity.x == 0 {
UIView.animate(withDuration: 0.3, animations: {
self.card.center = self.view.center
})
} else {
UIView.animate(withDuration: 0.3, animations: {
self.card.center = CGPoint(x: self.cardOrigPos.x+direction.x*5, y: self.cardOrigPos.y+direction.y)
})
self.showNextCard()
}
}
}
func showNextCard() {
if advice.count >= 0 {
let adv = advice[i]
setCard(adv["username"] as? String, adv["title"] as! String, adv["desc"] as! String, image: adv["image"] as? UIImage)
i += 1
if i >= advice.count {
i = 0
}
}
}
func setCard(_ username: String?, _ title: String, _ desc: String, image: UIImage?) {
let toolbar = setToolbar(username ?? "", title)
card = Card()
card.toolbar = toolbar
card.toolbarEdgeInsetsPreset = .square3
card.toolbarEdgeInsets.bottom = 0
card.toolbarEdgeInsets.right = 8
card.contentView = setContentView(content: desc)
card.contentViewEdgeInsetsPreset = .wideRectangle3
card.layer.borderWidth = 1.0
card.layer.borderColor = UIColor.black.cgColor
card.layer.cornerRadius = 2
view.layout(card).horizontally(left: 20, right: 20).center()
}
func setToolbar(_ username: String, _ title: String) -> Toolbar {
let toolbar = Toolbar(rightViews: [setMoreBtn()])
toolbar.title = title
toolbar.detail = username
toolbar.detailLabel.textColor = Color.grey.base
return toolbar
}
func setMoreBtn() -> IconButton {
let moreBtn = IconButton(image: Icon.cm.moreVertical, tintColor: Color.grey.base)
return moreBtn
}
func setContentView(content: String) -> UILabel {
let contentView = UILabel()
contentView.numberOfLines = 0
contentView.text = content
contentView.font = RobotoFont.regular(with: 14)
return contentView
}
}
When I tried using print statements to debug, the one in the DoTodayViewController.get(_ sender: Any) method ran but not the one in AccessViewController.viewDidLoad()
Make sure you changed the class of the view controller object in the storyboard to AccessViewController and that the Storyboard ID is correctly spelled. You may be encountering a nil when trying to force unwrap storyboard?.instantiateViewController(withIdentifier: "AccessViewController") as! AccessViewController. Try using if let wherever an optional needs to be unwrapped to handle the scenario of it being nil.
This question already has an answer here:
how to make tabbar based on my json response array (ios swift)
(1 answer)
Closed 7 years ago.
i am trying to make uitabbar based on my json response array.
this is my json response
tabs = (
{
id = 0;
name = Home;
},
{
id = 1;
name = Winkel;
},
{
id = 2;
name = Zoeken;
}
);
})
now this is my code which i tried::
func loadTabbarsWithArray(tabs: [AnyObject]) {
if self.tabBarController == nil {
let tabBarController = UITabBarController()
}
self.tabBarController!.viewControllers = [AnyObject]()
var viewControllers: [AnyObject] = [AnyObject](arrayLiteral: 0)
for record: [NSObject : AnyObject] in tabs {
var viewController: UIViewController = UIViewController(nibName: "CustomViewController", bundle: nil)
viewController.title = record["name"]
viewController.tabBarItem.title = record["name"]
viewControllers.append(viewController)
}
self.tabBarController!.viewControllers = viewControllers as? [UIViewController]
}
this is image which indicate errors
In your code you try to set the viewControllers property of your UITabBarViewController with an [AnyObject] object. According to Apple docs, this property must be an [UIViewController]?.
func loadTabbarsWithArray(tabs: [AnyObject]) -> Int {
guard let tabsOk = tabs as? [[String : String]] else {
return -1
}
if self.tabBarController == nil {
self.tabBarController = UITabBarController()
}
self.tabBarController!.viewControllers = nil
var viewControllers: [UIViewController] = []
for record: [String : String] in tabsOk {
var viewController: UIViewController = UIViewController(nibName: "CustomViewController", bundle: nil)
viewController.title = record["name"]
viewController.tabBarItem.title = record["name"]
viewControllers.append(viewController)
}
self.tabBarController!.viewControllers = viewControllers
return viewControllers.count
}
Try this snippet:
func loadTabbarsWithArray(let tabs:[[String: Any]]){
if (self.tabBarController == nil) {
self.tabBarController = UITabBarController();
}
tabBarController!.viewControllers = [UIViewController]();
var viewControllers = [UIViewController]();
for record:[String: Any] in tabs {
let viewController:UIViewController = UIViewController(nibName: "CustomViewController", bundle: nil);
viewController.title = record["name"] as? String;
viewController.tabBarItem.title = record["name"]as? String;
viewControllers.append(viewController);
}
tabBarController!.viewControllers = viewControllers;
}
I'm learning how to use a PageViewController and in the app I'm building I need to be able to refresh the content on the screen from the containing ViewController.
So in my storyboard I have:
a view controller, class RoomPageListViewController.
a view controller, class RoomContentViewController which has a number of labels which are update using CoreData.
a PageViewController
The setup is very simple, I can put in the entire code if needed but what I wanted to do is from RoomPageListViewController to be able to call a function within RoomContentViewController to update the labels and keep the user on the page that they are.
Whatever I tried has resulted in error, for example tried:
let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("RoomContentViewController") as! RoomContentViewController
pageContentViewController.updateScreen()
But no luck... how can I accomplish this or am I doing it the 'wrong' way?
Thanks!
EDIT v3: With a protocol implementation now working fully!
This is the code for the RoomPageListViewController:
class RoomPageListViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var roomContentVCAccess: RoomContentVCAccess!
var roomsList: Array<String> = ["Entire Home"]
var roomButtonClicked: String = ""
let activityInd: UIActivityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.WhiteLarge)
var showInd: Bool = true
let shadowLabel: UILabel = UILabel(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height))
var viewBySelection: Int = 1
var roomDeviceGroupID: Int = 0
var redrawBool: Bool = true
var displayRoom: String = ""
var pageViewController : UIPageViewController!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Devices By Room"
var backBtn : UIBarButtonItem = UIBarButtonItem(title: " ", style: UIBarButtonItemStyle.Plain, target: self, action: nil)
self.navigationItem.leftBarButtonItem = backBtn
self.navigationItem.leftBarButtonItem?.enabled = false
var settingsBtn : UIBarButtonItem = UIBarButtonItem(title: "Settings", style: UIBarButtonItemStyle.Plain, target: self, action: "goSettings")
self.navigationItem.rightBarButtonItem = settingsBtn
activityInd.stopAnimating()
if showInd == true {
startInd()
}
}
func startInd() {
shadowLabel.backgroundColor = UIColor.lightGrayColor()
shadowLabel.text = "Please Wait... Loading Data...\n\n\n\n\n"
shadowLabel.numberOfLines = 6
shadowLabel.textAlignment = NSTextAlignment.Center
let screenSize: CGRect = UIScreen.mainScreen().bounds
shadowLabel.center = CGPoint (x: screenSize.width/2 , y: screenSize.height/2)
shadowLabel.alpha = 0.5
shadowLabel.hidden = false
activityInd.center = CGPoint (x: screenSize.width/2 , y: screenSize.height/2)
activityInd.color = UIColor.blueColor()
activityInd.startAnimating()
activityInd.hidden = false
self.view.addSubview( shadowLabel )
self.view.addSubview( activityInd )
}
func stopInd() {
shadowLabel.hidden = true
activityInd.stopAnimating()
activityInd.hidden = true
showRooms()
if (redrawBool == true) {
//showRooms()
reset()
} else {
self.roomContentVCAccess.updateScreen()
}
redrawBool = false
}
func showRooms() {
roomsList = ["Entire Home"]
var serverSettings:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var managedContext: NSManagedObjectContext = serverSettings.managedObjectContext!
var request = NSFetchRequest(entityName: "Devices")
request.propertiesToFetch = NSArray(objects: "room") as [AnyObject]
request.resultType = NSFetchRequestResultType.DictionaryResultType
request.returnsDistinctResults = true
let deviceFilter = NSPredicate (format: "room <> %#", "Unknown")
request.predicate = deviceFilter
var roomsResults: Array<AnyObject> = managedContext.executeFetchRequest(request, error: nil)!
println("count: \(roomsResults.count)")
if roomsResults.count > 0 {
for room in roomsResults {
var theroom = room["room"] as! String
if (theroom != "Alarm") {
roomsList.append(theroom)
}
}
}
println(roomsList)
}
func reset() {
/* Getting the page View controller */
pageViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageViewController") as! UIPageViewController
self.pageViewController.dataSource = self
let pageContentViewController = self.viewControllerAtIndex(0)
self.pageViewController.setViewControllers([pageContentViewController!], direction: UIPageViewControllerNavigationDirection.Forward, animated: true, completion: nil)
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
self.addChildViewController(pageViewController)
self.view.addSubview(pageViewController.view)
self.pageViewController.didMoveToParentViewController(self)
//stopInd()
}
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
var index = (viewController as! RoomContentViewController).pageIndex!
index++
return self.viewControllerAtIndex(index)
}
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
var index = (viewController as! RoomContentViewController).pageIndex!
if (index <= 0) {
return nil
}
index--
return self.viewControllerAtIndex(index)
}
func viewControllerAtIndex(index : Int) -> UIViewController? {
if ((self.roomsList.count == 0) || (index >= self.roomsList.count)) {
return nil
}
let pageContentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("RoomContentViewController") as! RoomContentViewController
self.roomContentVCAccess = pageContentViewController
pageContentViewController.room = self.roomsList[index]
pageContentViewController.pageIndex = index
displayRoom = self.roomsList[index]
return pageContentViewController
}
func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
return roomsList.count
}
func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
return 0
}
}
and the code for RoomContentViewController:
protocol RoomContentVCAccess {
func updateScreen()
}
class RoomContentViewController: UIViewController, RoomContentVCAccess {
var pageIndex: Int?
var room : String!
#IBOutlet weak var screenScrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
screenScrollView.contentSize = CGSizeMake(screenScrollView.frame.size.width, 650)
roomName.text = room
btnViewAllRoomSensors.layer.cornerRadius = 10
btnViewAllRoomSensors.layer.masksToBounds = true
updateScreen()
}
override func viewDidAppear(animated: Bool) {
updateScreen()
}
func updateScreen() {
println(room)
let roomValues = getLabelValues(room)
println(roomValues)
var roomDevicesCount: Array<Int> = roomValues[0] as! Array<Int>
// more code here....
}
func getLabelValues(roomName: String) -> (Array<AnyObject>) {
var serverSettings:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var managedContext: NSManagedObjectContext = serverSettings.managedObjectContext!
var request = NSFetchRequest(entityName: "Devices")
let deviceFilter = NSPredicate (format: "room = %#", roomName)
// more CoreData code...
}
The overall picture is that the app, once it receives data calls the stopInd() within RoomPageListViewController. From within stopInd() I need to be able to call updateScreen() that is in RoomContentViewController.
You could create a protocol that the ViewController owning the labels conforms to. For example:
protocol RoomContentVCAccess
{
func updateLabels()
}
Then in your RoomContentViewController's class declaration:
class RoomContentViewController: UIViewController, RoomContentVCAccess
{
// ...
// MARK: - RoomContentVCAccess
func updateLabels()
{
// update your labels
}
}
Your RoomPageListViewController also has to know who his roomContentVCAccess is. For that, just create an instance variable in RoomPageListViewController: var roomContentVCAccess: RoomContentVCAccess! and then say self.roomContentVCAccess = viewController as! RoomContentViewController in your viewControllerAtIndex-function.
And then when stopInd() is called in RoomPageListViewController, say self.roomContentVCAccess.updateLabels().