I have created two custom labels in a table cell in order to be able to dynamically resize the cell(s) to it's content. I first tried using the "Subtitle" style and this worked out great except that the cell(s) didn't resize the way i wanted to and it looked really messy.
My question is: how do I access these labels in order to append my value's from my API to them?
View controller code:
import UIKit
class nyheterViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, APIControllerProtocol {
#IBOutlet weak var nyheterTableView: UITableView!
#IBOutlet weak var titleLabel: UILabel!
var searchResultsData: NSArray = []
var api: APIController = APIController()
func JSONAPIResults(results: NSArray) {
dispatch_async(dispatch_get_main_queue(), {
self.searchResultsData = results
print(self.searchResultsData)
self.nyheterTableView.reloadData()
})
}
override func viewDidLoad() {
super.viewDidLoad()
var APIBaseUrl: String = "http://*.se/*/*.php"
var urlString:String = "\(APIBaseUrl)"
//Call the API by using the delegate and passing the API url
self.api.delegate = self
api.GetAPIResultsAsync(urlString, elementName:"news")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//print(self.searchResultsData.count)
return self.searchResultsData.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier: String = "nyheterResultsCell"
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell
//nyheterTableViewCell.cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier);
//Create a variable that will contain the result data array item for each row
var cellData: NSDictionary = self.searchResultsData[indexPath.row] as NSDictionary
//Assign and display the Title field
var releaseDate: String = cellData["date"] as String
var titleVar: String = cellData["title"] as String
var titleMix: String = "\(titleVar)" + " - " + "\(releaseDate)"
cell.textLabel?.text = titleMix //textLabel worked out fine using "Subtitle" style.
// Get the release date string for display in the subtitle
cell.detailTextLabel?.text = cellData["content"] as String? //Same
return cell
}
}
I understand that I can't access these labels without somehow connecting them to the ViewController. Creating outlets to the ViewController generates an error about that I can't use connections from the prototype cell to the ViewController.
So, i created a new class, called nyheterTableViewCell which i connect to the table cell and connected outlets to my labels.
nyhterTableViewCell code:
import UIKit
class nyheterTableViewCell: UITableViewCell {
#IBOutlet weak var nyhetLabel: UILabel!
#IBOutlet weak var titleLabel: 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'm an beginner at Swift-programming and Xcode.
Cheers!
You don't need the labels connected to the view controller. Your example of the custom table view cell looks correct.
To access the label properties, you're going to want to change the line
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as UITableViewCell
to
let cell: nyheterTableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as nyheterTableViewCell
Related
I have a custom cell with some simple labels and a UIImage. Everything appears to be set correctly, and stepping through the debugger shows that everything is getting a value and even using the print in the debugger shows that the labels have text. However my table view is still empty when executed. I have been looking at this for too long and cannot figure out the problem.
Here is the cell code
class CurrentFileCell: UITableViewCell {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var statusImage: UIImageView!
#IBOutlet weak var dateLabel: UILabel!
var currentContent: AircraftContent! {
didSet{
setStyles(Constants.appStyleSetting)
self.nameLabel.text = currentContent.contentName
self.dateLabel.text = currentContent.contentStatus
self.statusImage.image = UIImage(named: "color_label_circle_green")
}
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
private func setStyles(settings: StyleSettings) {
let tableFont = UIFont(name: settings.bodyFont, size: CGFloat(settings.tableFontSize))
nameLabel.font = tableFont
dateLabel.font = tableFont
// Text color
let tableFontColor = settings.tableFontColor
nameLabel.textColor = tableFontColor
dateLabel.textColor = tableFontColor
}
Here is the ViewController code with a tableview inside.
class CurrentFilesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var content: AircraftContent?
#IBOutlet weak var currentFiles: UILabel!
#IBOutlet weak var downloadingLabel: UILabel!
#IBOutlet weak var readyLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.content = loadContent()
setStyles(Constants.appStyleSetting)
//self.tableView.reloadData()
// Do any additional setup after loading the view.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CurrentFileCell", forIndexPath: indexPath) as? CurrentFileCell
cell?.currentContent = content
return cell!
}
func loadContent() -> AircraftContent {
return (NSKeyedUnarchiver.unarchiveObjectWithFile(AircraftContent.ArchiveURL.path!) as? AircraftContent)!
}
private func setStyles(settings: StyleSettings) {
let titleFont = UIFont(name: settings.bodyFont, size: CGFloat(settings.titleFontSize))
let key = UIFont(name: settings.bodyFont, size: CGFloat(settings.tableFontSize))
currentFiles.font = titleFont
downloadingLabel.font = key
readyLabel.font = key
// Text color
let titleFontColor = settings.titleFontColor
currentFiles.textColor = titleFontColor
downloadingLabel.textColor = titleFontColor
readyLabel.textColor = titleFontColor
}
Here are some images showing the debug location where the cell is not empty, and also printing out the label which has a value, but isn't being shown during simulation.
http://imgur.com/a/dBkpe
This is an image showing the prototype cell. The cell has the correct class set as well as the identifier.
http://imgur.com/PKtFTeQ
Lastly another image showing that the prototype cell is linked to the labels within the CurrentFileCell.
http://imgur.com/nW0QUjM
Any help at all with this would be appreciated. I have tried recreating everything but continue to be stumped as it seems like everything is how it should be.
You have to implement the 'heightForRowAtIndexPath' method for the table view
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
let height:CGFloat = 75
return height
}
You may consider registering the custom class as it does not appear that you did. You can do that by using the following code in the viewDidLoad of your View Controller.
tableView.registerClass(CurrentFileCell.self, forCellReuseIdentifier: "cell")
If you are using an external nib you will want to use registerNib instead like so:
tableView.registerNib(UINib(name:"ReplaceWithYourNibName", bundle: nil), forCellReuseIdentifier: "ReuseIdentifier")
Of course, replace ReplaceWithYourNibName and ReuseIdentifier with the appropriate values. In addition, if your nib is in a different bundle specify that instead of nil (nil defaults to the main bundle).
However, do not use both registerClass and registerNib as whichever one you call last will be used and they were designed to be mutually exclusive. Whenever you make a custom UITableViewCell you must use either of the two for it to work unless you have set it explicitly in the storyboard.
Also, you could instead, use prototype cells to define your custom cell, which would, I believe, automatically register the cell. But only if you did not use prototype cells make sure to use registerClass or registerNib.
Good luck! Hope this helps!
If your cell is a static cell, then you need to comment out these methods in UITableViewDataSource:
/* override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
} */
I had the same issue.
Data has values and cell rows are showing empty.
I figured it by adding
contentView.addSubview(titleLabel)
where cell values are being set.
I have a table view with a custom cell build in it, the custom cell has two labels and one textfield. When the user edits the textfield then the label2 changes in the current cell. But I want that the value(Label2) in the second cell will also change based on the value in the current cell. Example: Value in the first cell is x, the Value in the second cell should be 2+x. How should I realize that? In my view controller I am using the following code:
var myString = [String]()
#IBOutlet weak var TableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
myString = ["a", "b"]
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myString.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CalculationsTableViewCell
tableView.tableFooterView = UIView(frame: CGRectZero) //hide empty cells in UITableView
cell.Label?.text = myString[indexPath.row]
return cell
}
My TableViewCell:
#IBOutlet weak var Label: UILabel!
#IBOutlet weak var Label2: UILabel!
#IBOutlet weak var TextField: UITextField!
#IBAction func TextFieldEditingChanged(sender: AnyObject) {
var value: Double
value = NSString(string: TextField.text!).doubleValue
Label2.text = "\(value)"
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
Firstly you should / could make use of the delegate pattern to communicate from the cell back to your controller:
create a protocol with a method named e.g. userEnteredNumber
let your view controller conform to that protocol
create a field in the TableViewCell called delegate - its type should the previously created protocol
when setting up the cell in cellForRowAtIndexPath assign self to the delegate property of the cell
as soon as the user enters a value call the delegate method on the previously set delegate
Secondly you have to somehow remember the state of your application. The thing with tableviews is that the iOS can decide to reload the contents of the tableView at any point (theoretically) and your controller has to keep track of the exact content each cell should have.
I do not know what the exact requirements are for your app but you should probably assign the value that gets transmitted via the protocol method to some variable in your viewController. Then you trigger a tableView reloadData and finally you have to alter the cellForRowAtIndexPath method to set the desired properties of some of the cells based on the stored value.
On my main.Storyboard I have a TableViewController which is set to a custom class with TableViewController.swift.
The swift file has all the tableview functions defined and the #IBOutlet for the UITableView connected. The classes defined are UINavigationController,UITableViewDelegate. This viewController is called from a secondViewController via the prepareForSegue function.
I also created CustomCell.swift with class UITableViewCell and all #IBOutlet for the labels in my UITableViewCell which has been set to the customCell class.
I can't paste all my code but if you need to look at any specific code let me know and I will be happy to post that.
The Build succeeds and the app runs but the tableviewcells don't show up and none of the tableview functions are called. I see 2 flash animated screens - indicating that the tableviewcell might have 2 views - but can't figure out where I should be checking?
//Below is the segue function triggering the TableViewController
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//print("In prepareswgue: ",segue, " ",sender)
if(segue.identifier == "resultSegue")
{
let nav = segue.destinationViewController as! UINavigationController
let svc = nav.topViewController as! TableViewController
svc.serialNo = self.TSSerialNoField.text
}
}
//Below is the custom TableVIewController class code
class TableViewController: UINavigationController,UITableViewDataSource, UITableViewDelegate
{
var serialNo:String!
var ashHardwareData: NSMutableArray!
#IBOutlet var ResultTableView: UITableView!
//#IBOutlet weak var LogCaseButton: UIButton!
#IBOutlet weak var TypeResultLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.ResultTableView?.allowsSelectionDuringEditing = true
self.ResultTableView?.delegate = self
ResultTableView?.dataSource = self
}
override func viewDidAppear(animated: Bool) {
self.getHardwareData(serialNo.uppercaseString)
}
/*override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
}*/
func getHardwareData(serialno:String)
{
ashHardwareData = NSMutableArray()
ashHardwareData = ModelManager.getInstance().getHardwareData(serialno)
ResultTableView?.reloadData()
}
//TableView Delegate Methods
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
print("In height func")
return 50
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(ashHardwareData.count)
return ashHardwareData.count
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:ResultsCell = tableView.dequeueReusableCellWithIdentifier("results", forIndexPath: indexPath) as! ResultsCell
let hardware:HardwareInfo = ashHardwareData.objectAtIndex(indexPath.row) as! HardwareInfo
let contract:ContractInfo = ashHardwareData.objectAtIndex(indexPath.row) as! ContractInfo
cell.SNOLabel.text = "Serial N0: \(hardware.SerialNo)"
cell.ContractIDLabel.text = "Contract ID: \(contract.ContractID)"
cell.OrgLabel.text = "Organisation: \(hardware.Organisation)"
cell.ModelLabel.text = "Model: \(hardware.Model)"
if(contract.DaystoExpiry > 0) {
cell.TypeLabel.text = "Contract Type: Active"
self.TypeResultLabel.hidden = false
self.TypeResultLabel.text = "To log a technical case for the Hardware please click on Log Technical Case button."
cell.LogCaseButton.hidden = false
cell.LogCaseButton.tag = indexPath.row
}
else {
cell.TypeLabel.text = "Contract Type: Expired"
cell.LogCaseButton.hidden = true
self.TypeResultLabel.hidden = false
self.TypeResultLabel.text = "Support Contract for the hardware expired. Please contact Sales team to renew the contract."
}
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
//here is the code for the ResultsCell custom UITableViewCell
import Foundation
import UIKit
class ResultsCell: UITableViewCell {
#IBOutlet weak var SNOLabel: UILabel!
#IBOutlet weak var LogCaseButton: UIButton!
#IBOutlet weak var ContractIDLabel: UILabel!
#IBOutlet weak var OrgLabel: UILabel!
#IBOutlet weak var TypeLabel: UILabel!
#IBOutlet weak var ModelLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
have you configured ResultsCell in story board or in view didload. check it once. if not try to add the following code into your view didload
[self.tableView registerNib:[UINib nibWithNibName:#"ResultsCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:#"ResultsCell"];
I have spent fair bit of my time trying to get this issue resolved and decided to go without the tableviewcell, instead I have created custom viewControllers and defined labels to display the results from the DB search (which is working OK) and used the perform segue function to pass values between viewcontrollers. So now my app works the way I want - maybe not ideal from a programming side but due to time constraints I had to get this working.
I have my app as a Tabbed Application with First and Second ViewControllers. The FirstViewController adds data to the SQLLite DB and the Second searches for the data and displays the results. for displaying the results I created a Custom viewController with labels for the data that I wanted displayed and passed all data from the DB results in SecondViewController functions to it and updated the labels with the data. As long as I get the results I wanted I am happy. I will re-visit this for improvement if I have to. Thanks to all who responded with solutions and suggestions. It has been a good learning experience :)
I have a Collections View that includes various cells that are also buttons. Upon clicking one of those buttons, a separate ViewController is shown (I just connected the button to the ViewController and clicked "show"). I would like to know how to switch the textView in that separate ViewController for every different button in my Collections View.
Here's the code in my CollectionsView:
class Example : UICollectionViewController {
var Array = [String]()
var ButtonArray = [String]()
#IBOutlet var menu: UIBarButtonItem!
override func viewDidLoad() {
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
Array = ["A", "B", "C, "D]
ButtonArray = ["", "", "", ""]
menu.target = self.revealViewController()
menu.action = Selector("revealToggle:")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Array.count
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! UICollectionViewCell
var Label = cell.viewWithTag(1) as! UILabel
Label.text = Array[indexPath.row]
var Button = cell.viewWithTag(2) as! UIButton
Button.setTitle(ButtonArray[indexPath.row], forState: UIControlState.Normal)
return cell
}
}
And the code in the separate ViewController:
class Example2: UIViewController {
#IBOutlet var exText: UITextView!
override func viewDidLoad() {
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Follow these steps...
1: Create a Separate file called Manager.swift and place this code in it...
//manager.swift
import Foundation
struct Manager {
static var messageText = [String]()
}
2: Clean your project by pressing Shift+Command+K.
3: In the first view controller set the messageText to the text fields text...
Manager.messageText.append(self.textField.text)
4: In the second view controller retrieve the text and set the label to the message text...
self.label.text = Manager.messageText[0]
5: Your Finished!!
That was one way, and it relies on creating a global variable that can be defined out of the scope of any available class, no new file needed here. Just define the variable above (or below) the class code.
Another good way is to use UserDefaults that are automatically saved in app settings file, this way the variable is retained, and when you restart the app again the value will be the same you left with.
Swift 3+
To store/change a value:
UserDefaults.standard.setValue(yourValue, forKey: "yourKey")
To retrieve a value
UserDefaults.standard.value(forKey: "yourKey")
I've been stuck with this problem for days, so I'd be really happy if someone could help.
I'm trying to create a dynamic UITableView, for which I created a custom UITableView subclass and I've created a custom UITableViewCell subclass as well, because I need several UILabels and a UIButton in each cell.
The cell is created, but the problem is that the value of the labels is always nil, hence the cell isn't displayed properly.
This is, how the storyboard looks like, and this is what I see while running the program.
Here's my UITableViewCell subclass:
import UIKit
class QuestionTableViewCell: UITableViewCell {
#IBOutlet var student: UILabel!
#IBOutlet var labDesk: UILabel!
#IBOutlet var topic: UILabel!
#IBOutlet var answers: UILabel!
}
and my UITableView subclass:
import UIKit
class QuestionViewController: UITableViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet var table: UITableView!
struct Question {
var student: String
var labDesk: String
var topic: String
var answered: String
}
override func viewDidLoad() {
super.viewDidLoad()
table.estimatedRowHeight = 50
table.dataSource = self
table.delegate = self
self.table.registerClass(QuestionTableViewCell.self, forCellReuseIdentifier: "cell")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as QuestionTableViewCell
cell.student.text = "random string"
cell.labDesk?.text = "25/A"
cell.topic?.text = "string"
cell.answers?.text = "3"
return cell
}
}
Try removing self.table.registerClass(QuestionTableViewCell.self, forCellReuseIdentifier: "cell")
If you're using a cell with a nib then make sure that you are registering the cell with the table view using registerNib:forCellReuseIdentifier:. If the cell just has a class then use registerClass:forCellReuseIdentifier:.
First, you don't have to register the class if it exists in Interface Builder.
Second, you should dequeueReusableCellWithIdentifier:forIndexPath instead of dequeueReusableCellWithIdentifier.
Third, UITableViewController already has a property called tableView so there is no need to make an IBOutlet to table as UITableViewController already handles this. It also conforms to the UITableViewDataSource and UITableViewDataSource so these are extraneous.
Fourth, don't set the properties for table set them for tableView.
Fifth, cell.labDesk.text = "" is sufficient, no need to make it optional.
If all your IBOutlets are hooked up, Cell Identifiers correctly set, and these revisions are made, it will work.
class QuestionTableViewCell: UITableViewCell {
#IBOutlet var student: UILabel!
#IBOutlet var labDesk: UILabel!
#IBOutlet var topic: UILabel!
#IBOutlet var answers: UILabel!
}
class QuestionViewController: UITableViewController {
struct Question {
var student: String
var labDesk: String
var topic: String
var answered: String
}
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 50
tableView.dataSource = self
tableView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as QuestionTableViewCell
cell.student.text = "random string"
cell.labDesk.text = "25/A"
cell.topic.text = "string"
cell.answers.text = "3"
return cell
}
}
The most important part is to register the xib containing the custom cell with the table view. Therefore add the following code in viewDidLoad() method.
let nib = UINib.init(nibName: "MyCustomCell", bundle: nil)
self.tblUsers.register(nib, forCellReuseIdentifier: "MyCustomCell")
I might be late here, but I just solved a similar problem.
Make sure you've set the Identifier in InterfaceBuilder on your UITableViewCell.
For those who are still trying to figure this out after trying all those possible solutions:
Disconnect/Reconnect the IBOutlets in your Storyboards should do the trick!
Don't forget to add:
tableView?.register(UINib(nibName: "xyz",
bundle: nil),
forCellReuseIdentifier: "abc")
If you are using a table cell with Xib. you need to register your cell with ..
register(_:forCellReuseIdentifier:)
If you haven't added constraints for the label then they will not be created though the custom cell is created.
Make sure you added some constraints.
Make sure that the selected cell is in the right "module" and if necessary, inherit:
If not, your IBOutlets will be nil.
Issue I was facing: TableViewCell has been created and all the IBOutlets are nil. So I can't set any values such as text or color etc. Below code worked for me.
Xcode version: 13.3
Step 1:
Remove datasource and delegate reference form storyboard.
Step 2:
In viewDidLoad add,
tableView.delegate = self
tableView.dataSource = self
Step 3:
In tableview UITableViewDataSource cellForRowAt function, add your cell the given way.
let cell = tableView.dequeueCell(ofType: YourCellName.self)
cell.yourCellFunction()
return cell
Note 1: dequeueCell(ofType...) is calling the below function internally. you don't need to use it directly.
func dequeueCell<T: UITableViewCell>(ofType type: T.Type) -> T {
}
Important: You don't need to provide any "Resporation ID" or "Reuse Identifier" for cell. It works with your cell name.