Thread 1: Fatal error: Index out of range on swift 4 - ios

I have a issue on Thread 1 fatal error on my code. I try to create 2 pickers in one view controller. But the error keep coming. When I tried to run on my phone, everything is okay till this page (picker cannot move). It shows an error on (return subject[row]) on 6 line from bottom)
enter image description here
Heres my code:
Import UIKit
class ThirdViewController: UIViewController, UIPickerViewDelegate,
UIPickerViewDataSource {
#IBOutlet weak var classPickerView: UIPickerView!
#IBOutlet weak var subjectPickerView: UIPickerView!
#IBOutlet weak var nextButton: UIButton!
let subclass = ["AA234", "ASA231", "AA9292", "AAA839", "AA5682", "AAA789"]
let subject = ["Introduction to Database","Introduction to Programming","Mathematics","Multimedia"]
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func buttonPressed(_ sender: Any) {
let picker = UIImagePickerController()
picker.delegate = (self as! UIImagePickerControllerDelegate & UINavigationControllerDelegate)
//select row first
let selectedRow1 = classPickerView.selectedRow(inComponent: 0)
let selectedRow2 = subjectPickerView.selectedRow(inComponent: 0)
let selectedClass = subclass [selectedRow1]
let selectedSubClass = subject [selectedRow2]
let messageToShow = "Class \(selectedClass) with the subject \(selectedSubClass)"
// insert messageToShow in message to appear what u selected
let actionSheet = UIAlertController (title: "Please Confirm Before Scan", message: messageToShow, preferredStyle: .alert)
let okAction = UIAlertAction (title: "Proceed to Scan", style: .default, handler: nil) //{action in
//picker.sourceType = .camera})
let cancelAction = UIAlertAction (title: "Cancel", style: .cancel, handler: nil)
actionSheet.addAction(okAction)
actionSheet.addAction(cancelAction)
present(actionSheet, animated: true, completion: nil)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 2
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if (pickerView.tag == 1) {
return subject.count
}
else {
return subclass.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
if component == 0 {
return subject[row]
}
else {
return subclass[row]
}
}
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if (component == 0) {
return subject.count
}
else {
return subclass.count
}
}
Change numberOfRowsInComponent code as above

Related

Swift 5: How can get the selected PickerView value inside a UIAlert Textfield?

I'm struggling to implement a UIPickerView inside the UIAlert as an input for the textfield. Here's what I've got (think of a simple TODO app);
import UIKit
class ViewController: UIViewController {
let todoItemCategory = ["Work", "Home", "Friends", "Buy stuff"]
let pickerView = UIPickerView()
override func viewDidLoad() {
super.viewDidLoad()
pickerView.dataSource = self
pickerView.delegate = self
}
#IBAction func addItemButtonPressed(_ sender: Any) {
let alert = UIAlertController(title: "Add a new todo item", message: "Provide the title and the category of the new item.", preferredStyle: .alert)
alert.addTextField { field in
field.placeholder = "Type the item title"
field.returnKeyType = .next
}
alert.addTextField { field in
field.placeholder = "Select category"
field.inputView = self.pickerView
}
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Save", style: .default, handler: { _ in
guard let fields = alert.textFields, fields.count == 2 else {
return
}
let itemTitleField = fields[0]
let categoryField = fields[1]
guard let itemTitle = itemTitleField.text, !itemTitle.isEmpty, let category = categoryField.text, !category.isEmpty else {
print("Invalid entries")
return
}
print(itemTitle)
print(category)
}))
present(alert, animated: true)
}
}
//MARK: - Pickerview datasource & delegate methods
extension ViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return todoItemCategory.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return todoItemCategory[row]
}
}
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Update the textfield inside the UIAlert
}
}
What I've done is;
A UIAlert view pops up when the user clicks on the button (addItemButtonPressed).
In the "Select category" textfield, I linked to the PickerView as an input view. The Pickerview with possible categories will then appear as selectable options.
The picker view appears nicely, but whenever the user selects one of the values, nothing happens. I want the textfield to show the selected category, and I know I need to do something inside
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Update the textfield inside the UIAlert
}
I was going to assign the selected value to a global variable, let's say currentSelectedCategory, but I do not know how to make the UIAlert textfield to read it up again whenever the value gets changed.
Appreciate your help in advance to this poor swift newbie!
Set a UIAlertController object reference outside the button action and access it.
class ViewController: UIViewController {
// Other code
var alert: UIAlertController = UIAlertController()
// Other code
#IBAction func addItemButtonPressed(_ sender: Any) {
alert = UIAlertController(title: "Add a new todo item", message: "Provide the title and the category of the new item.", preferredStyle: .alert)
// Other code
}
}
extension ViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
// Update the textfield inside the UIAlert
alert.textFields?.last?.text = todoItemCategory[row]
}
}

UITextfield as float passed through segue to an UILabel

Im fairly new to swift and would like for a user to enter a number using the decimal pad for UITextfield to enter a float then pass that float to another view controller using a UILabel. Everything works except passing the float to the UILabel on the other view controller. I get this error
"Cannot assign value of type 'UITextField?' to type 'UILabel?'"
import UIKit
class SelectAttributesViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate {
#IBOutlet weak var kitPickerView: UIPickerView!
#IBOutlet weak var reactionVolumeLabel: UILabel!
#IBOutlet weak var numberOfSampleTextField: UITextField!
#IBOutlet weak var volumeOfTemplateDNATextField: UITextField!
#IBOutlet weak var slider: UISlider!
#IBOutlet weak var calculateButton: UIButton!
let kits = ["kit 1", "Kit 2"]
let pArray = [10,20,50]
override func viewDidLoad() {
super.viewDidLoad()
self.numberOfSampleTextField.delegate = self
self.volumeOfTemplateDNATextField.delegate = self
kitPickerView.dataSource = self
kitPickerView.delegate = self
let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:)))
view.addGestureRecognizer(tap)
}
func pSliderInterval() {
slider.minimumValue = 0
slider.maximumValue = Float(pArray.count)
slider.isContinuous = false
}
#IBAction func reactionVolumeSlider(_ sender: UISlider) {
print(Int(sender.value))
let currentValue = pArray[Int(sender.value)]
reactionVolumeLabel.text = "\(currentValue) "
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return kits.count
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if row == 0{ print(kits[row])
}else if row == 1{
createAlert(title: "Kit not available", message: "More kits will be added soon!")
calculateButton.isEnabled = false
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return kits[row]
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
guard let oldText = textField.text, let r = Range(range, in: oldText) else {
return true
}
let newText = oldText.replacingCharacters(in: r, with: string)
let isNumeric = newText.isEmpty || (Double(newText) != nil)
let numberOfDots = newText.components(separatedBy: ".").count - 1
let numberOfDecimalDigits: Int
if let dotIndex = newText.firstIndex(of: ".") {
numberOfDecimalDigits = newText.distance(from: dotIndex, to: newText.endIndex) - 1
} else {
numberOfDecimalDigits = 0
}
return isNumeric && numberOfDots <= 1 && numberOfDecimalDigits <= 2
}
func createAlert (title: String, message: String){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: { (Action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
// This is the Segue.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
var masterMix = segue.destination as! MasterMixTableViewController
masterMix.waterVolumeLabel = numberOfSampleTextField
}
#IBAction func calculatePressed(_ sender: Any) {
if numberOfSampleTextField.text != ""{
performSegue(withIdentifier: "masterMixSegue", sender: self)
}
}
}
I have browsed heavily for the solution but haven't found anything.
You need
let masterMix = segue.destination as! MasterMixTableViewController
masterMix.loadViewIfNeeded()
masterMix.waterVolumeLabel!.text = numberOfSampleTextField!.text
The error says you are assigning UITextField to UILabel. But you must assign textFeild.text to label.text.
let textField = UITextField()
let label = UILabel()
label.text = textField.text

How to compare selected UIPickerView row element to Array element?

import UIKit
class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
#IBOutlet weak var categoryFactsPincker: UIPickerView!
#IBOutlet weak var randomFactLabel: UILabel!
This is my array of my UIPickerView
let categoryFactsArray = ["Interesting Facts", "Fun Facts", "Nutrition Facts", "Funny Facts", "Unbelievable Facts", "Did You Know", "Animal Facts", "Mind Blowing Facts", "Weird Facts"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
categoryFactsPincker.delegate = self
categoryFactsPincker.dataSource = self
}
I would like to use this button to compare array element and UIPickerView selected for print different categories
#IBAction func buttonPressed(_ sender: UIButton) {
if categoryFactsArray[row] == 0 {
updateInterestingFacts()
}else if categoryFactsPincker.selectedRow(inComponent: 1){
}
}
func updateInterestingFacts(){
let random = interestingFactsArray[Int(arc4random_uniform(UInt32(interestingFactsArray.count)))]
// randomFactLabel.text = random
let alert = UIAlertController(title: "Random Fact", message: "\(random)", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true)
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return categoryFactsArray.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return categoryFactsArray[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
categoryFactsArray[row]
}
}
try this
#IBAction func buttonPressed(_ sender: UIButton) {
if categoryFactsPincker.selectedRow(inComponent: 0) == 0 {
updateInterestingFacts()
}else{
let categorySelected = categoryFactsArray[categoryFactsPincker.selectedRow(inComponent: 0)]
print(categorySelected)
}
}
this print should show the selected value, so you can use to compare, or whatever you need
At the end I fixed it with this code:
#IBAction func buttonPressed(_ sender: UIButton) {
if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 0 {
updateInterestingFacts()
}else if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 1 {
updateFunFacts()
}else if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 2{
updateNutritionFacts()
}else if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 3{
updateFunnyFacts()
}else if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 4{
updateUnbelievableFacts()
}else if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 5{
updateDidYouKnowFacts()
}else if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 6{
updateAnimalFacts()
}else if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 7{
updateMindBlowingFacts()
}else if Int(categoryFactsPincker.selectedRow(inComponent: 0)) == 8{
updateWeirdFacts()
}
}

Get text from UIPickerview and move Textfield inside UIAlert

Here's an example. I have other alerts with textfields and others with picker views but doing one with both is a pain.
let gradeTypeTextField = gradeTypeTextField (Doesn't work)
var gradeTypeTextField: UITextField!
#IBAction func newGrade(_ sender: AnyObject) {
When button is tapped, show alert.
alertForGrade()
}
var gradeType = ["Attendance", "Assignment", "Homework Assignment", "Quiz", "Test", "Mid-Term", "Exam", "Other"]
override func viewDidLoad() {
super.viewDidLoad()
Hide extra cells
tableView.tableFooterView = UIView()
}
func alertForGrade() {
Create the alert controller.
alert = UIAlertController(title: "New Grade", message: nil, preferredStyle: .alert);
Add the text field. You can configure it however you need.
alert?.addTextField(configurationHandler: { (gradeTypeTextField) -> Void in
gradeTypeTextField.placeholder = "Type of Grade."
let pickerView = UIPickerView()
pickerView.delegate = self
pickerView.dataSource = self
pickerView.backgroundColor = UIColor.white
pickerView.showsSelectionIndicator = true
gradeTypeTextField.inputView = pickerView
self.view.endEditing(true)
})
alert?.addTextField(configurationHandler: { (assignmentTextField) -> Void in
assignmentTextField.placeholder = "Name of the assignment."
})
alert?.addTextField(configurationHandler: { (gradeTextField) -> Void in gradeTextField.placeholder = "Grade for the assignment."
Grab the value from the text field, and print it when the user clicks OK.
let ok = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
let assignmenttextField = (self.alert?.textFields![0])! as UITextField
let gradeTextField = (self.alert?.textFields![1])! as UITextField
let assignment = assignmenttextField.text!
let gradeString = gradeTextField.text!
let grade = Int(gradeString)
self.dictionaryForGradebook.updateValue(grade!, forKey: assignment)
print(self.dictionaryForGradebook)
self.tableView.reloadData()
})
self.alert?.addAction(ok)
self.alert?.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) -> Void in
}))
// 4. Present the alert.
self.present(self.alert!, animated: true, completion: nil)
})
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return gradeType.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return gradeType[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
This prints but if I try " yearTextField.text = years[row]" it crashes.
print("Testinggggg")
self.view.endEditing(true)
}
Where is the isuue?
You need to be able to reference your created UITextField later when you attempt to add text to it when you select a row in the UIPicker. You seem to be forgetting to place that link:
alert?.addTextField(configurationHandler: { (gradeTypeTextField) -> Void in
// Append this:
self.gradeTypeTextField = gradeTypeTextField
gradeTypeTextField.placeholder = "Type of Grade."
let pickerView = UIPickerView()
pickerView.delegate = self
pickerView.dataSource = self
pickerView.backgroundColor = UIColor.white
pickerView.showsSelectionIndicator = true
gradeTypeTextField.inputView = pickerView
self.view.endEditing(true)
})
This will however create a retain-cycle so it is recommend to create an unowned/weak self link by adding [weak self] to the start of your closure.

Save uipickerview color in core data

I know what I want, but I'm just struggling to get it the right way. I'm trying to implement a small app, which let the user add a category by providing a category name, description and then select colour from the uipickerview. I've successfully saved the name, description in the core data. I'm just struggling in how to get the selected colour string and save it in the core data. Any help will be appreciated. This is what I've so far as code:
import UIKit
import CoreData
class NewCategory: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate {
let categoryColor = ["Red","Yellow","Black","White", "Green", "Blue"]
// MARK: - Properties
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
// MARK: Outlets
#IBOutlet weak var name: UITextField!
#IBOutlet weak var descriptionField: UITextView!
#IBOutlet weak var pickerView: UIPickerView!
#IBOutlet weak var colorLabel: UILabel!
// MARK: Actions
#IBAction func saveBtn(sender: AnyObject) {
if name.text == "" || name.text.isEmpty || name.text.isEmpty{
var alert = UIAlertController(title: "WARNING !!!", message: "Couldn't add category. Fill all fields.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} else {
createCategory() }
}
// MARK: View settings
override func viewDidLoad() {
super.viewDidLoad()
self.pickerView.dataSource = self
self.pickerView.delegate = self
}
// Custom functions
func createCategory() {
let entity = NSEntityDescription.entityForName("Category", inManagedObjectContext: context!)
let category = Category(entity: entity!, insertIntoManagedObjectContext: context)
category.name = name.text
category.descript = descriptionField.text
category.color = colorLabel
println(category.name)
context?.save(nil)
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return categoryColor.count
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
return categoryColor[row]
}
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if (row == 0) {
colorLabel.backgroundColor = UIColor.redColor()
}
else if(row == 1) {
colorLabel.backgroundColor = UIColor.yellowColor()
}
else if(row == 2) {
colorLabel.backgroundColor = UIColor.blackColor()
}
else if(row == 3) {
colorLabel.backgroundColor = UIColor.whiteColor()
}
else if(row == 4) {
colorLabel.backgroundColor = UIColor.greenColor()
}
else {
colorLabel.backgroundColor = UIColor.blueColor()
}
}
}
You can get the color name this way:
let index = pickerView.selectedRowInComponent(0)
let color = categoryColor[index]
For example, you could use it in your function like this:
func createCategory() {
let entity = NSEntityDescription.entityForName("Category", inManagedObjectContext: context!)
let category = Category(entity: entity!, insertIntoManagedObjectContext: context)
category.name = name.text
category.descript = descriptionField.text
let index = pickerView.selectedRowInComponent(0)
let color = categoryColor[index]
category.color = color // (assuming color is a String)
println(category.name)
context?.save(nil)
}
In didSelectRow get the value from categoryColor
func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let colorString = categoryColor[row] as! String
// Save the colorString into Core data
}

Resources