I used UITableViewCell like this.
class WeatherListTableViewCell: UITableViewCell {
#IBOutlet weak var weatherImageView: UIImageView!
#IBOutlet weak var city: UILabel!
#IBOutlet weak var degree: UILabel!
#IBOutlet weak var rainPercent: UILabel!
var weather: String?
}
I thought the cell is kind of View so it can't have not UI stuff.
But sometimes I added a value like weather for using hidden info and the cell worked like ViewController.
Can I use cell like this?
UITableViewCell is a class like any other in iOS. If you derive from it you can extend its capabilities and therefore add properties and functions.
Like you have already done you can add #IBOutlets that represent parts of the cell's UI but you can also add non-visible fields that you just use within the class to hold information that you need.
Think about functions that you add, they are also not part of the UI but they are within the declaration of your WeatherListTableViewCell class.
There could be a function which uses the weather string you already added and extracts information from in, populating your UI elements with their corresponding values.
Related
I'm developing an app for my school that helps students better understand their grades by getting an analysis on their portfolio of assignments for each class. Right now I am at the point of letting the user create the classes they are in and customize the information within it that is displayed in a tableview with custom cells. The user gives the majority of the information in a child view where they input information such as class name, teacher, grade weighting, etc. I wanted to give the user the ability to change the color of the cell once they are viewing the TableView with all the cells - classes - they made. I decided to go about this by having the cells have a UIButton that they can click on for their determined cell to then pull up a UIColorPickerViewController.
What I wanted to happen was...
User taps button in cell
UIPickerViewController is pulled up
User selects their desired color
User exits UIPickerViewController
Cell is changed to the color
What is actually happening is this
User taps button in cell
Cell background becomes black right as UIPickerViewController is presented
User selects their desired color
User exits UIPickerViewController
Cell remains black
I used a delegate to send the information from the cells and then I used the "colorPickerViewControllerDidFinish()" function and it's still not working out. When I did some debugging I found that the value of the UIColorPickerViewController is actually being stored in the variable I am using, but only after I have already assigned it's value to the cell background so I'm unsure what to do. As you can probably tell, I'm new to swift so apologies for any stupid mistakes in my code.
Custom Cell File
// Protocol for VC's to conform to so they can distinguish which cell has a button being tapped
protocol newlyCreatedCellDelegate: AnyObject
{
func didTapButton(title: String, cellView: UIView)
}
class newlyCreatedClass: UITableViewCell {
// Telling the delegate what to do once they are assigned
weak var delegate: newlyCreatedCellDelegate?
#IBOutlet weak var classContentView: UIView!
#IBOutlet weak var classUIView: UIView!
#IBOutlet weak var classNameLabel: UILabel!
#IBOutlet weak var classTeacherNameLabel: UILabel!
#IBOutlet weak var pointType1NameLabel: UILabel!
#IBOutlet weak var pointType2NameLabel: UILabel!
#IBOutlet weak var pointType3NameLabel: UILabel!
#IBOutlet weak var percent1Label: UILabel!
#IBOutlet weak var percent2Label: UILabel!
#IBOutlet weak var percent3Label: UILabel!
#IBOutlet weak var colorButton: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
colorButton.layer.cornerRadius = 21
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
// Essentially creating the prep list for the delegate. If they are called - whoever it is - they will go through this 'checklist'
#IBAction func colorButtonTapped(_ sender: Any)
{
delegate?.didTapButton(title: classNameLabel.text!, cellView: classUIView)
}
}
ViewController Extensions
extension ClassSelection: newlyCreatedCellDelegate
{
func didTapButton(title: String, cellView: UIView)
{
let colorPickerVC = UIColorPickerViewController()
colorPickerVC.delegate = self
present(colorPickerVC, animated: true, completion: nil)
colorPickerViewControllerDidFinish(colorPickerVC)
// 'cellBackgroundColor' is a variable declared in the VC to transfer the UIColor value
cellView.backgroundColor = cellBackgroundColor
}
}
extension ClassSelection: UIColorPickerViewControllerDelegate
{
func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
cellBackgroundColor = viewController.selectedColor
}
}
You should implement one more UIColorPickerViewControllerDelegate method:
func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) {
cellBackgroundColor = viewController.selectedColor
}
It's a great start! As a really direct answer to your original question:
The reason for your problem is that in your didTapButton function you are presenting the color picker, but then immediately telling the app that the user is done with the picker, and then immediately setting the background color to cellBackgroundColor, which I assume has a default value of UIColor.black.
Instead you should delete those last 2 lines in your didTapButton function - just initialize the picker, set the delegate, and present the picker. Then the delegate method you chose - colorPickerViewControllerDidFinish isn't really the correct one for your purpose. Instead consider using the didSelect delegate method (see docs). When that method is called it will pass you the color the user selected, which you can simply use to set your background color and refresh your tableView cell if needed.
Since you mention you are a new Swift dev I will also mention that UITableView reuses its cells, so simply setting the background color of a cell once will not have the result you are expecting. You will see that as you scroll the cells up and down the colors will change in the various cells, so ultimately you'll need to store the color selections in another way so that each time a cell is being dequeued you can set the correct color based on user input. That part is outside of the scope of the original question, but just letting you know.
This question already has answers here:
Fatal error: unexpectedly found nil while unwrapping an Optional values [duplicate]
(13 answers)
Closed 2 years ago.
I've been wanting to code for a while, and just started using my free time to try to learn so I know very little but am eager to learn! Also first time poster.
I'm making a simple dice game app, and I've been horribly stuck trying to unwrap strings and variables. I keep getting the error "Unexpectedly found nil while implicitly unwrapping an Optional value" when trying to assign a user input name to a label on the next view controller.
Let me know if this is too convoluted off an app design, but this is what I'm trying to design:
first view controller where 2-4 players enter their names. They hit the next button and it takes them to the next viewController where there are dice, a roll button, and the names they just entered.
I have the UITextField and UILabel for the names correctly connected to IBOutlets.
I forced the UITextField to unwrap by making them optional to prevent the app from crashing, but the names are still not updated.
Here's my code:
class ViewController: UIViewController, UITextViewDelegate, UITextFieldDelegate {
#IBOutlet weak var name1: UITextField!
#IBOutlet weak var name2: UITextField!
#IBOutlet weak var name3: UITextField!
#IBOutlet weak var name4: UITextField!
//connects name strings
#IBOutlet weak var dice1: UIImageView!
#IBOutlet weak var dice2: UIImageView!
#IBOutlet weak var dice3: UIImageView!
#IBOutlet weak var dice4: UIImageView!
//links dice images
#IBOutlet weak var name1Label: UILabel!
#IBOutlet weak var name3Label: UILabel!
#IBOutlet weak var name4Label: UILabel!
#IBOutlet weak var name2Label: UILabel!
//links name labels on second viewController
var diceArray = ["dice1", "dice2", "dice3", "dice4", "dice5", "dice6"]
//creates an array to set dice images
var randomArray = [0,0,0]
var randomDice1 : Int = 0
var randomDice2 : Int = 0
var randomDice3 : Int = 0
//creates variables to hold random number
func storeNames () {
name1Label?.text = name1.text. THIS IS WHERE I KEEP GETTING FATAL ERRORS
name2Label?.text = name2.text
name3Label?.text = name3.text
name4Label?.text = name4.text
//stores the user inputed names into an array
}
//stores the names from textfield into name strings
override func viewDidLoad() {
super.viewDidLoad()
Thread.sleep(forTimeInterval:0.25)
//extends loading screen
name1?.placeholder = "Player 1 Name"
name2?.placeholder = "Player 2 Name"
name3?.placeholder = "Player 3 Name"
name4?.placeholder = "Player 4 Name"
//Creates placeholder text
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing))
view.addGestureRecognizer(tap)
//removes keyboard when you tap outside keyboard
}
#IBAction func nextButton(_ sender: UIButton) {
storeNames()
}
You want to
assign a user input name to a label on the next view controller.
But all your labels, and text fields seem to be declared as outlets on the same view controller.
You cannot directly use next / previous view controller's outlets like this.
What you need to do is collect all naems in the first view controller, pass them to the next view controller and set them as label text in the next view controller. If you are using storyboards and segues, you can leverage prepareForSegue:sender:, method to do this.
Since you mention you have just started, and are learning things Here are some online tutorials on passing data between views:
https://www.hackingwithswift.com/example-code/system/how-to-pass-data-between-two-view-controllers
https://learnappmaking.com/pass-data-between-view-controllers-swift-how-to/
https://matteomanferdini.com/how-ios-view-controllers-communicate-with-each-other/
P.S. This is a very basic approach, but not a recommended one. View transitions and data should not be mixed together in the same code. If you are planning to use this for building a robust app it will be a good idea to also start learning SOLID principles, and design patterns.
I am trying to build a single view app that could simply calculate what you would need on a final depending on numerical values in two text fields (Current grade and percentage weight of the final) and a segmented controller with A, B, C, and D. I'm lost though on how to make a function that will change a label whenever it detects a change in either the segmented controller or the text fields.
On the most recent version of Xcode
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var gradeCurrent: UITextField!
#IBOutlet weak var finalWeight: UITextField!
#IBOutlet weak var gradeWanted: UISegmentedControl!
#IBOutlet weak var gradeNeeded: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
}
I want the ending label to have added on a rounded up number of what is required to achieve the grade
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Xcode won't let me name my IBOutlet "switch", because it's a reserved keyword:
#IBOutlet weak var switch: UISwitch!
If you want to name a constant or variable with same name as a reserved keyword.
Just surround the keyword within (`) backticks
for eg.
#IBOutlet weak var `switch`: UISwitch!
when using it as a name, However, we should avoid using keywords as names anyway.
As always, you should name variables by their meaning not just repeat the class name in lowercase. For example, the following variables have bad naming:
let double: Double = 2
let array: [String] = []
better names are
let scale: Double = 2
let names: [String] = []
The same applies to outlets. It's not nice to call UILabel just label. Better name is titleLabel or usernameLabel. It's not nice to call UIImageView just imageView. Better names are backgroundImageView or avatarImageView.
A good name for UISwitch depends on the meaning of the variable. It could be enabledSwitch or something similar.
Variable naming, while seemingly trivial, can be one of those things that causes endless friction (confusion, tech-debt manifesting as refactoring) not to mention interpersonal conflict.
A simple rule that is helpful to minimise this, is to name UI elements as minimally generically as possible, but no less.
For example, for a UI that contains a UIImageView and a UILabel, the following would be suitable:
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var label: UILabel!
This would avoid specifying the content of an imageView in code as much as possible (as opposed to, say, "avatarImageView" which might need to be refactored when the design changes).
However, should there be multiple elements of the same type, it would become important to disambiguate them:
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var detailLabel: UILabel!
With regards to the case of a UISwitch, where naming the variable "switch" is not possible, following the same logic would be to disambiguate it with a prefix:
#IBOutlet weak var settingSwitch: UISwitch!
#IBOutlet weak var preferenceSwitch: UISwitch!
Update: Thanks to #rmaddy for pointing this out!
I never use generic names for controls, mainly because if requirements change for the app, you might need to add another UISwitch to the same view (or view controller). I'll typically use a naming convention that gives a fairly clear understanding of the control:
#IBOutlet weak var emailTxt: UITextField!
#IBOutlet weak var passwordTxt: UITextField!
#IBOutlet weak var showPasswordTgl: UISwitch!
#IBOutlet weak var loginBtn: UIButton!
#IBOUtlet weak var loginSectionPnl: UIView!
I abbreviate the word "toggle" for switch because I think switch was a bad choice for Apple to name that control. There are many switches in the real world that have more than 2 states, so toggle is more straightforward for a control that's either in the "on" or "off" state. For a basic view, I use an abbreviation for "Panel" because that's how I'll typically use an empty view. Since most visual controls in UIKit derive from a UIView (i.e. UIControl->UIView), to me, it would be too generic a term to abbreviate.
I am building one framework. I need to get the IBOutels list from UIViewcontroller through code. I have written InterfaceOutletsReadable protocol. If the framework user conforms this protocol I have to read the list of IBOutlets from ViewController.
protocol InterfaceOutletsReadable {
///Read the outlets objects
func readOutlets()
}
extension InterfaceOutletsReadable {
//TODO:- Stuck at this stage. Here I have to read the viewcontroller IBOutlets
}
class HomeViewController: InterfaceOutletsReadable {
#IBOutlet weak var userNameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var errorLabel: UILabel!
}
Edit: I don't want to get the list from IBOutlet Collection. Is there any way to get all outlets programmatically?
you can take IBOutletCollection of one textField and connect to all others.It maintain Array of Outlet and You Can access through index.
#IBOutlets exist only at design time, there are no ones even at compile time. So it’s imposible to get all outlets in the way you want. Outlets have no difference with other variables.
If you need list of the outlets you may implement readOutlets() in your HomeViewController and return Array consist any variables you want.