UITableView doesn't fill custom cells inside of UITabBarController - ios

I have UITabBarController and on one of the tabs I have UIViewController with UITableView inside.
I had created custom cell class.
If I fill tableView custom cells without UITabBarController (I put entry point to UIViewController) everything works fine, but if I use UITabBarController firstly, my cells are not populated with any data when I reach UITableView tab.
Where could be a bug here?
import UIKit
class blaViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
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 tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int {
return 3
}
func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! blaTableViewCell
cell.myLabel.text = "123"
return cell
}
}
import UIKit
class blaTableViewCell: UITableViewCell {
#IBOutlet weak var myLabel: 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
}
}

Try tick install checkbox for all the constraints, not only wh wr. It worked for me. It is also described here: https://stackoverflow.com/a/32052154/2064500

#IBOutlet weak var myLabel: UILabel!
Try to do strong variable
#IBOutlet var myLabel: UILabel!

Related

Getting Nil While Trying to Set Class Label Property | UITableView

I keep getting unexpectedly found nil while unwrapping an Optional value when I run this code.
What I am trying to do is get the first property in an array of objects so I use .map to get the first property which is "workoutName"
Then I try to set the label in the CellData class to this value but keep getting that it found nil.
I appreciate all help I can get.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var workoutName = workouts.map { $0.workoutName }
let cell = CellData()
cell.workoutNameLabel.text! = workoutName[indexPath.row]
return cell
}
Cell Data Class
import UIKit
class CellData: UITableViewCell {
#IBOutlet weak var workoutNameLabel: UILabel!
#IBOutlet weak var numberOfSetsNumberLabel: UILabel!
#IBOutlet weak var numberOfRepsNumberLabel: 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
}
}
I would set class variable into CellData class and set it in awakeFromNib since awakeFromNib won't happen before the cellForRow is finished, so you can be almost 100% sure that the label will be nil.. try this:
import UIKit
class CellData: UITableViewCell {
//someLableValue string
var labelValueText: String?
#IBOutlet weak var workoutNameLabel: UILabel!
#IBOutlet weak var numberOfSetsNumberLabel: UILabel!
#IBOutlet weak var numberOfRepsNumberLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
workoutNameLabel.text = labelValueText
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
then you jsut set the variable labelValueText in cellForRowMethod in UITableViewController class

UITableView Crashes on Segue to View Controller

Cause of Crash
Click the image above for a brief view of my Segue. My app is a workout tracker. I have a class that makes the workout object with properties such as workout name, description, etc. I have another object that creates the workoutlist, so just an array of Workout objects.
I have a UITableView and a button above it that lets the user create a new workout and adds it to the workoutList array.
The crash happens whenever I click this "create new workout" which segues to a new view controller. I get this error
Crash Error
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var setStepper: UILabel!
#IBOutlet weak var repStepper: UILabel!
#IBOutlet weak var workoutName: UITextField!
#IBOutlet weak var workoutDescription: UITextField!
#IBOutlet weak var tableView: UITableView!
var workoutList = WorkoutList().listOfWorkouts
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func stepperCounter(_ sender: UIStepper) {
if sender.tag == 1 {
setStepper.text = "\(Int(sender.value))"
}
else if sender.tag == 2 {
repStepper.text = "\(Int(sender.value))"
}
}
#IBAction func addToWorkout(_ sender: Any) {
//I know this is terrible fixing later. Just for Testing right now
let newWorkout : Workout = Workout(Name: workoutName.text!, Description: workoutDescription.text!, Sets: Int(setStepper.text!)!, Reps: Int(repStepper.text!)!)
workoutList.append(newWorkout)
print(workoutList.count)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return workoutList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "Hello"
return cell
}
}
it looks like you are using the same ViewController for both Views in your storyboard.
Which means, when you push to the "AddNewWorkoutController" the viewDidLoad expects a tableView - which is not there, because the storyboard didn't build any for this view.
You should create 2 VCS. One for the OverView with the "add new button" and the tableview and another one for actually creating it.
here is a full solution:
create 2 new swift files:
OverViewViewController.swift & NewWorkoutViewController.swift
I just separated your code into 2 VC's:
import UIKit
class OverViewViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var workoutList = WorkoutList().listOfWorkouts
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return workoutList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "Hello"
return cell
}
}
and
import UIKit
class NewWorkoutViewController: UIViewController {
#IBOutlet weak var setStepper: UILabel!
#IBOutlet weak var repStepper: UILabel!
#IBOutlet weak var workoutName: UITextField!
#IBOutlet weak var workoutDescription: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func stepperCounter(_ sender: UIStepper) {
if sender.tag == 1 {
setStepper.text = "\(Int(sender.value))"
}
else if sender.tag == 2 {
repStepper.text = "\(Int(sender.value))"
}
}
#IBAction func addToWorkout(_ sender: Any) {
//I know this is terrible fixing later. Just for Testing right now
let newWorkout : Workout = Workout(Name: workoutName.text!, Description: workoutDescription.text!, Sets: Int(setStepper.text!)!, Reps: Int(repStepper.text!)!)
workoutList.append(newWorkout)
print(workoutList.count)
}
}
Now go into the Storyboard, select the controllers and assign the right classes:
After assigning go and connect your tableView to the tableView in the OverView and the 2 actions in your NewWorkout.
This will work.
I'd guess that tableView outlet isn't wired up in your storyboard - and that property is an implicitly unwrapped optional. You're promising that it won't be nil.

Swift - UIView inside custom UITableViewCell won't update buttons/labels

I'm just getting started with Swift and I'm having some weird issues with a custom UITableViewCell. The issue is I needed to have a white background inside each table cell as seen here.
I have created a custom UITableViewCell class and created IBOutlets for a image, label and button as shown below:
import UIKit
class LocationTableViewCell: UITableViewCell {
#IBOutlet var locationImageView: UIImageView!
#IBOutlet var locationButton: UIButton!
#IBOutlet var locationLabel: 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
}
My view controller code is:
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
#IBOutlet var locationTableView: UITableView!
var skiLocation = Location(name: "Banff", picture: UIImage(named: "ski.jpeg")!)
var tundraLocation = Location(name: "Yellowknife", picture: UIImage(named: "tundra.jpeg")!)
var beachLocation = Location(name: "Cancun", picture: UIImage(named: "beach.jpeg")!)
var locations: [Location] = []
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return locations.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: LocationTableViewCell = tableView.dequeueReusableCellWithIdentifier("location", forIndexPath: indexPath) as! LocationTableViewCell
cell.locationLabel.text = self.locations[indexPath.row].name
cell.locationImageView.image = self.locations[indexPath.row].picture
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.locations = [skiLocation, tundraLocation, beachLocation]
self.locationTableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I have approached this problem by making a UIView inside of each table view and then placing the button, label and image inside the UIView. This was just to get the white background. However, when I run my code everything shows as expected, except there is no UILabel and the UIButton is stylized as per storyboard. When I try to change the background of the label, or the button via storyboard, nothing changes. No label and just the generic button. The weird part is I am able to set the images to the UIImageView via the ViewController class.
Any thoughts?
EDIT: Here is the link to the storyboard view. I have used the identifier field in the storyboard view to link the cell.
You have UITableViewDataSource But UITableViewDelegateis missing.
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var locationTableView: UITableView!
var skiLocation = Location(name: "Banff", picture: UIImage(named: "ski.jpeg")!)
var tundraLocation = Location(name: "Yellowknife", picture: UIImage(named: "tundra.jpeg")!)
var beachLocation = Location(name: "Cancun", picture: UIImage(named: "beach.jpeg")!)
var locations: [Location] = []
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return locations.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: LocationTableViewCell = tableView.dequeueReusableCellWithIdentifier("location", forIndexPath: indexPath) as! LocationTableViewCell
cell.locationLabel.text = self.locations[indexPath.row].name
cell.locationImageView.image = self.locations[indexPath.row].picture
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.locations = [skiLocation, tundraLocation, beachLocation]
self.locationTableView.dataSource = self
self.locationTableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Changing the State of a Button from Another Class

This is my goal: When the Start button is tapped, I want the Send buttons to be enabled. The following is what my view controller looks like when loaded:
As you can see, the Send buttons are disabled, which is desired. And I've disabled it by writing the following code in my TaskListTableViewCell.swift (I deleted some other irrelevant code for the sake of being succinct):
class TaskListTableViewCell: UITableViewCell {
#IBOutlet weak var sendButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
sendButton.enabled = false
}
}
And just in case it might help you see better, this is how the view controller looks like in the Main.storyboard:
And in my TaskListViewController, I have the following code:
#IBAction func startButtonTapped(sender: AnyObject) {
//write the code here to change sendButton.enabled = true
}
The problem is, I cannot seem to find a way to change the sendButton.enabled = true. I've tried:
#IBAction func startButtonTapped(sender: AnyObject) {
let taskListViewController = TaskListViewController()
taskListTableViewCell.sendButton.enabled = true
}
I've also tried:
class TaskListTableViewCell: UITableViewCell {
#IBOutlet weak var sendButton: UIButton!
func changeSendButtonEnabled() {
sendButton.enabled = true
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
sendButton.enabled = false
}
}
And in my TaskListViewController.swift, I wrote:
#IBAction func startButtonTapped(sender: AnyObject) {
let taskListViewController = TaskListViewController()
taskListTableViewCell.changeSendButtonEnabled()
}
But both of these solutions give this error: fatal error: unexpectedly found nil while unwrapping an Optional value.
I've read some other posts about accessing an IBOutlet from another class such as this post: How to access an IBOutlet from another class, Accessing IBOutlet from another class, Access an IBOutlet from another class in Swift, and using IBOutlet from another class in swift. However, none of these posts were able to solve my problem.
Please try this , It may solve your problem.
//CustomCell.swift
import UIKit
import Foundation
class CustomCell: UITableViewCell {
#IBOutlet weak var btnTap : UIButton!
#IBOutlet weak var lblTitle : UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
btnTap.enabled = false
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
//ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var table: UITableView!
#IBOutlet weak var btnSTART: UIButton!
var isStartBtnTapped : Bool = false
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.
}
#IBAction func startBtnTapped(sender: AnyObject) {
isStartBtnTapped = true
self.table.reloadData()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1;
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3;
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let reuseIdentifier:String="cell";
var cell:CustomCell!
cell=tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as! CustomCell;
if cell==nil
{
cell=CustomCell(style: UITableViewCellStyle.Default, reuseIdentifier: reuseIdentifier);
}
if(isStartBtnTapped == true){
cell.lblTitle.text = "Enabled"
cell.btnTap.enabled = true
}else{
cell.lblTitle.text = "Disabled"
cell.btnTap.enabled = false
}
return cell;
}
}
Your tableview that contains tableview cells needs a data source in order to fill it with data. The data in this case being if the buttons are enabled or not. You can create an array of objects (or structs) that contain a Bool to tell the button to be enabled or not. Change the bool when start button is enabled and then call tableView.reloadData(). Your data source will then reload the array of objects and cellForRowAtIndexPath will then be called reconfiguring your cells.
The reason that your getting the fatal error is probably because the reference to the tableviewcell that you are trying to access is nil. When you call the method to change the button status, it finds nothing there and crashes. Although it's hard to tell exactly what's happening because there is code missing

Swift - TableView of steppers, click one stepper in a cell and other steppers get activated?

I am new to IOS and basically I have a tableView and whenever it has 40 cells and each cell has a stepper and a label. the label displays the stepper's value. The tableview generates the cells fine, but the problem is that whenever I click the stepper in one cell, some other random cells also have their steppers activated.This is swift by the way. Here is the code for the cell:
import UIKit
class StudentTableViewCell: UITableViewCell {
#IBOutlet weak var studentNameAndValue: UILabel!
#IBOutlet weak var studentValueChanger: UIStepper!
let name:String?
let value:Int?
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
}
#IBAction func stepperValueChanged(sender: AnyObject) {
studentNameAndValue.text = "\(name): \(Int(studentValueChanger.value))"
}
}
Here is the code for the viewcontroller:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 40
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("studentCell") as StudentTableViewCell
return cell
}
}
The problem is in your table view controller. It would be better to put the value-changed method there. Alternatively, review your cellForRowAtIndexPath method. You are not updating the cells correctly when they are recycled.
You have to set the value of stepper and label explicitly in cellForRowAtIndexPath. You cannot read these values from the cell - they should be in your datasource (i.e. the table view controller should know what to display for a given index path).
Connect the stepper handler to the method in the view controller, then identify the proper index path via the sender argument.
#IBAction func stepperChanged(sender: UIStepper) {
let point = sender.convertPoint(CGPointZero, toView: tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(point)!
let myData = dataArray[indexPath.row] // or whatever your datasource
// if you need to update the cell
let cell = self.tableView.cellForRowAtIndexPath(indexPath)
}

Resources