I cannot get the tabbar delegate to call - ios

I have asked this problem b4 and have tried for so long...
Problem: I have 4 tabs, when the third one is selected i want to wait until the tab has been changed, then send info to a Stringbuilder etc. But I can never seem to get it to call properly, I have tried delegating the genreViewController and tried to use the protocols but it never calls...
Please tell me what I need to put inside of the tab bar function since I do not know what should be there. I have tried putting the tabbarcontroller there and select viewcontroller. I am very new to all this so please dont be harsh :(
TabBarController:
import UIKit
class TabBarViewController: UITabBarController{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tabBar(_ tabBar: TabBarViewController, didSelect item: UITabBarItem) {
print("he")
//ask where it is first tab bar item
if self.tabBarController?.selectedIndex == 1 {
print("genres")
}
}
}
GenreViewController:
import UIKit
class GenreViewController: UIViewController, UITableViewDelegate,UITableViewDataSource, UITabBarControllerDelegate {
#IBOutlet weak var genreSwitch: UISwitch!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
//self.tabBarController?.delegate = self
tableView.isScrollEnabled = false
let tabBarViewController = TabBarViewController()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
let genreArray = ["Action","Adventure","Comedy","Fantasy","Drama","Horror","Romance","Thriller","Family"]
let genreIds = [28,12,35,14,18,27,10749,53,10751]
//LATER!!: Save this Array until next time they log into the app
var activeGenreArray = [Int!](repeating: 0, count: 9)
//Number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return genreArray.count
}
//What to do with tableview
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! GenreCustomcell
cell.genreSwitch.isEnabled = true
cell.genreName.text = self.genreArray[indexPath.row]
cell.genreSwitch.setOn(false, animated: true)
cell.genreSwitch.addTarget(self, action: #selector(GenreViewController.switchChange(sender:)), for: UIControlEvents.valueChanged)
cell.genreSwitch.restorationIdentifier = "\(genreIds[indexPath.row])"
cell.genreSwitch.tag = indexPath.row
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func switchChange(sender: UISwitch){
let id = Int(sender.restorationIdentifier!)
let row = Int(sender.tag)
print(row)
if sender.isOn == true{
activeGenreArray[row] = id!
print(activeGenreArray)
}
else{
activeGenreArray[row] = 0
print(activeGenreArray)
}
}
}
class GenreCustomcell: UITableViewCell{
#IBOutlet weak var genreName: UILabel!
#IBOutlet weak var genreSwitch: UISwitch!
}

It's not clear to me what you are trying to do here. You've declared your GenreViewController to be a UITabBarControllerDelegate but then implemented a UITabBarControllerDelegate method (tabBar:didSelect:) on your TabBarViewController subclass of UITabBarController.
I would expect to see you have:
a reference to some UITabBarController (this could be more specifically a TabBarViewController)
a reference to some UITabBarControllerDelegate (this could be a GenreViewController, a TabBarViewController if it should be its own delegate, or some other object entirely)
to set the UITabBarController instance's delegate property to be the UITabBarControllerDelegate instance.
Things that may have confused you:
A view controller can exist (and be associated with a tab) but does
not load it's view until it is needed (i.e. you are switching to its
tab). If you added your GenreViewController to a tab bar controller
it's viewDidLoad method will not be called until the tab bar
controller switches to that tab the first time. If your
GenreViewController was setting its own tab bar controller's
delegate in viewDidLoad (e.g. self.tabbarController?.delegate =
self) it may not become the tab bar controller's delegate when you
expected and so would not be notified of changes in the selected tab
until after it had been selected once.
It's not clear what the let tabBarViewController =
TabBarViewController() line is meant to do but that is creating a
new TabBarViewController which is not the one you might already be
seeing in your window. Creating a new instance of a class is not equivalent to referencing some existing instance of that same class.

Related

How can i use uitableview cell for performing segue

I started to learn Swift 2 weeks ago. With some courses and youtube videos, I learn basics of uitableview but I couldn't get how can I use specific table view cell like a button.
What I wanna do?
I am trying to use the sidebar menu(https://github.com/yannickl/FlowingMenu) for my project, everything is cool and working okay instead of using cells.
In every basic tutorial people showing how to use segue with uitableview but they only use 2 pages and independent from cell value, no matter what text in your cell it calls segue for the second controller.
I want to use the sidebar menu with cells and if my cell value is "main menu" I want to perform "toMainMenu" segue.
I only know using cells with arrays don't know is there any other way for work with cells, and my main goal is cell-specific segue.
I watched a lot of tutorials but can't find anything useful for me.
import UIKit
import FlowingMenu
class SideBarMenu: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var surnameLabel: UILabel!
#IBOutlet weak var topBar: UINavigationBar!
#IBOutlet weak var backButtonItem: UIBarButtonItem!
#IBOutlet weak var userTableView: UITableView!
#IBOutlet weak var profilePic: UIImageView!
let CellName = "Menu"
let mainColor = UIColor(hex: 0xC4ABAA)
var sections = ["Main Menu", "Settings", "Profile"]
override func viewDidLoad() {
super.viewDidLoad()
userTableView.delegate = self
userTableView.dataSource = self
nameLabel.text = Helper.name
surnameLabel.text = Helper.surname
topBar.tintColor = .black
topBar.barTintColor = mainColor
topBar.titleTextAttributes = [
NSAttributedString.Key.font: UIFont(name: "HelveticaNeue-Light", size: 22)!,
NSAttributedString.Key.foregroundColor: UIColor.black
]
userTableView.backgroundColor = mainColor
view.backgroundColor = mainColor
}
// MARK: - Managing the Status Bar
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
// MARK: - UITableView DataSource Methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = sections[indexPath.row]
cell.contentView.backgroundColor = mainColor
return cell
}
// MARK: - UITableView Delegate Methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Select row at")
}
#IBAction func profileButton(_ sender: Any) {
performSegue(withIdentifier: "toProfile", sender: nil)
}
}
I want to perform "toSettings" segue when "Settings" cell clicked.
Implement the UITableViewDelegate protocol method tableView(_:didSelectRowAt:) as below
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch sections[indexPath.row] {
case "Main Menu":
performSegue(with: "toMainMenu", sender: nil)
case "Settings":
performSegue(with: "toSettings", sender: nil)
case "Profile":
performSegue(with: "toProfile", sender: nil)
}
}
If you need to configure or pass some data to the destination controller, you can override the controller's prepare(for:sender:) method to get a reference, for example:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toMainMenu" {
let dvc = segue.destination as! MenuViewController
// now you have a reference
}
}
If I understand, your menu is a tableView, so you need to segue to the view you wanna open in the table didSelectAt
You can use override prepare for segue. so when you link your views with a segue you set an identifier for each one and can call whatever you need from there.

iOS - UITableView inside a UIView

I want to display a UITableView inside a UIViewController. This View Controller contains a UISegmentedControl with two screens (FirstViewControllerand SecondViewController).
The first View Controller is the one that contains the UIViewTable (please don't mind the second).
When I execute the app in the simulator, everything works fine, but when I try to scroll the table view in the first ViewController, the cells disappear. The only way to make them reappear is to kill the app and reopen it again.
I'm new to iOS development (I come from Android), and I'm obviously missing something here.
I already tried adding a UIViewTable outside a container UIView and it works fine. So I'm guessing the problem has to do with the container or the segmented control...
Here's my implementation:
Storyboard
UIViewController with UISegmentedControl and UIView (which will contain the two screens of the segmented control).
View Controller
#IBOutlet weak var container: UIView!
var sectionViews:[UIView]!
override func viewDidLoad() {
super.viewDidLoad()
sectionViews = [UIView]()
sectionViews.append(FirstViewController().view)
sectionViews.append(SecondViewController().view)
for v in sectionViews {
container.addSubview(v)
}
container.bringSubviewToFront(sectionViews[0])
}
#IBAction func switchViewsAction(_ sender: UISegmentedControl) {
self.container.bringSubviewToFront(self.sectionViews[sender.selectedSegmentIndex])
}
First View Controller
The FirstViewController has a swift and a xib files, and has two files Cell.swift and Cell.xib for the table cell.
FirstViewController.swift
#IBOutlet weak var tableView: UITableView!
let cellID = "CellId"
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "Cell", bundle: nil), forCellReuseIdentifier: cellID)
self.tableView.dataSource = self
self.tableView.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath) as! Cell
cell.label.text = "\(indexPath.row)"
return cell
}
FirstViewController.xib
Cell.xib
Any help will be appreciated!
Thanks
One obvious problem is that you are saying container.addSubview(v) without giving v any frame or constraints. Since you use autolayout to position container, you ought to use autolayout to position v as well. You should set its top, bottom, leading, and trailing anchors to equal those of container with a constant of zero. (And set its translates... to false.) Do that for both cases of v in the loop.
However, there is much more serious problem, which is that the view controllers that you create by saying FirstViewController() and SecondViewController() are not retained. Therefore they vanish in a puff of smoke. They thus lose their functionality; for example, the table view no longer has any data source or delegate so it has no cells.
What you are doing is totally illegal. You cannot simply use a view controller to "dumpster-dive" as a way of grabbing its view and shove its view, willy-nilly, into the interface. You must make the view controller a child view controller of your parent view controller (Item in this case). There is an elaborate dance you must do in order to ensure that the child view controller has its proper place in the view controller hierarchy and receives in good order all the messages that a view controller must receive, and you are not doing the dance.
For examples of how to do the dance, see for instance my answers
https://stackoverflow.com/a/41898819/341994
and
https://stackoverflow.com/a/52666843/341994
import UIKit
class TestViewController: UIViewController , UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var segmentControlOutlet: UISegmentedControl!
#IBOutlet weak var tableView: UITableView!
var arrayName = ["Label1", "Label2", "Label3","Label4","Label5","Label6","Label7","Label8","Label9","Label10"]
var arrayName2 = ["Label1", "Label2", "Label3","Label4","Label5"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func segmentControlAction(_ sender: UISegmentedControl) {
self.tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if segmentControlOutlet.selectedSegmentIndex == 0 {
return arrayName.count
}else{
return arrayName2.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as! TestTableViewCell
if segmentControlOutlet.selectedSegmentIndex == 0 {
cell.textLabel?.text = arrayName[indexPath.row]
}else{
cell.textLabel?.text = arrayName2[indexPath.row]
}
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
}
And this code is for UITableViewCell Class:-
import UIKit
class TestTableViewCell: UITableViewCell {
#IBOutlet weak var labelName: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}

Can't pass data via segue

I make app with news feed which has to open on other ViewController. But can't pass data via segue.
Viewcontroller with newsfeed
class SecondViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var titlenews = ""
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "newsfeedCell", for: indexPath) as! NewsFeedCell
cell.newsfeed_title.text = self.news?[indexPath.item].headline
cell.newsfeed_topic.text = self.news?[indexPath.item].topic
cell.newsfeed_time.text = timetime(from: (self.news?[indexPath.item].time)!)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("tableview")
let vc = storyboard?.instantiateViewController(withIdentifier: "newsBody") as? NewsBody
vc?.labeltext = (self.news?[indexPath.item].headline)!
print((self.news?[indexPath.item].headline)!)
self.navigationController?.pushViewController(vc!, animated: true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.news!.count
} //number of rows
#IBOutlet weak var tableview: UITableView!
var news: [Newsfeed]? = []
override func viewDidLoad() {
super.viewDidLoad()
getJSON()
}
func getJSON(){
///Here all do right
}
}
Viewcontroller which has to receive data from news feed
class NewsBody: UIViewController {
#IBOutlet weak var testLabel: UILabel!
var labeltext = ""
override func viewDidLoad() {
super.viewDidLoad()
print(labeltext)
testLabel.text = labeltext
}
}
print(labeltext) shows that NewsBody receive empty value or nothing.
But print((self.news?[indexPath.item].headline)!) inside of SecondViewController shows that I try to push proper value.
What I do incorrect between this actions? What wrong with segue and pass of data?
It seems that instantiateViewController(withIdentifier: "newsBody") triggers view load under the hood. It should not (in theory) but it might do just that in your case.
This means that viewDidLoad() will be called before the vc?.labeltext = (self.news?[indexPath.item].headline)! is executed.
I'd recommend you to do the following.
class NewsBody: UIViewController {
#IBOutlet weak var testLabel: UILabel!
var labeltext: String? {
didSet { updateUI() }
}
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
}
private func updateUI() {
testLabel.text = labeltext
}
}
This way if you set the labeltext property after the view is loaded, it will still trigger the UI update. And if you set the labeltext property before the view is loaded, as soon as viewDidLoad() is called.
BTW, you are not using segues here. But even if you do, you can easily use the same method as I proposed, because it allows you to stop thinking about whether property updates will update the UI.
Also please note that I made the property optional. It will allow you to avoid force casts and just do
vc?.labeltext = self.news?[indexPath.item].headline
UILabel.text is also an optional String property, so they will play well together.

how to modify an array from another class

Im making to do list-app which consist of 2 View Controllers
one with table view hold an array and display it and second one with a textField, a button function to append the text field's text to the array
to display the new appended string it in the first view controller
heres my code:
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var exmpArray = ["DDDD","rrr","TTT"]
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func addBtnBar(_ sender: Any) {
performSegue(withIdentifier: "showMe", sender: nil)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return exmpArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = exmpArray[indexPath.row]
return cell!
}}
and the second one is:
class SecondViewController: UIViewController {
#IBOutlet weak var myTextField: UITextField!
var realAry:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
let myObj = ViewController()
#IBAction func addBtn(_ sender: Any) {
myObj.exmpArray.append(myTextField.text!)
print(myObj.exmpArray)
}
after appending the new words it doesn't display in the first controller
One problem is this line:
let myObj = ViewController()
That creates a new instance of ViewController that has nothing to do with the view controller that called you. Get rid of that line. It's wrong.
You need to set up a delegate property in SecondViewController that points back to ViewController, define a protocol for that delegate to conform to, and use that protocol so that SecondViewController can notify it's delegate when the array changes.
In ViewController, add a prepareForSegue() function that takes the destination view controller, casts it to type SecondViewController, and sets SecondViewController's delegate to self.
Both arrays are different you are just copying first VC array to 2nd VC array. Array is value type in swift. Instead of creating 2 arrays make 1st VC array as static.
static var exmpArray = ["DDDD","rrr","TTT"]
Now you can use & update this in all VC by calling like below
FirstVC.exmpArray

UITableViewController showing white blank screen in simulator

I am trying to make a very simple iOS app that has begins at one UITableViewController and when you click on each cell (a different manner kids should learn) it pushes that specific data to a UIViewController. I have added placeholder information in the cells, but when I run the simulator only a blank screen shows up. I attach pictures and code below.
class TableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func backBtn(_ sender: AnyObject) {
dismiss(animated: true, completion: nil);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
}
class ViewController: UIViewController, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var names = ["Manner 1", "Manner 2", "Manner 3", "Manner 4", "Manner 6", "Manner 7", "Manner 8"]
override func viewDidLoad() {
super.viewDidLoad()
// 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.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 8
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath) as! Manner2Cell
cell.name.text = names[indexPath.row]
return cell
}
}
//Manner2Cell actually refers to the first cell. I know I know bad naming convention.
class Manner2Cell: UITableViewCell {
#IBOutlet weak var name: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
Looking at all of this it looks like I have some repeated code. I feel like this is an easy fix but I just can't figure it out!
You have to set your ViewController as a dataSource for your tableView and adopt the protocol UITableViewDataSource.
class ViewController: UITableViewDelegate, UITableViewDataSource, ... {
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self // Assigns ViewController as delegate for tableView
tableView.dataSource = self
}
The scene in the upper left of your Interface Builder screen snapshot looks like a UITableViewController (which is presumably the class you call TableViewController).
But your numberOfSections and numberOfRowsInSection both return zero. So you'll have a blank white screen. Frankly, it looks like the code intended for the first scene's UITableViewController subclass has been put in the second scene's view controller.
Your second scene (upper right of your snapshot) is a little confusing, because it looks like that's a UITableViewController, too, but it doesn't look like it should be a table at all. I'm not sure what you're trying to do there. Is it really a table with repeated occurrences or is it just supposed to be the details of the cell you selected in the first scene? If it's just supposed to be a "details" screen, then that should be a simple UIViewController, not a UITableViewController.

Resources