Send many things through a segue - Swift 4 - ios

Edit - not really a question anymore - please feel free to delete this post or use it as you wish. A little red box says this post is mostly code and Grammarly is kind of helpful too so just rambling on now until the little red box goes away.
Working solution:
ViewController1 ->
enum to tag many buttons - (change each button tag in interface builder)
enum Answers : Int, CustomStringConvertible {
case bag = 1
case bird = 2
enum for text
var description : String {
switch self {
case .bag: return "Beg"
case .bird: return "Birds"
}
}
enum for images - (stored in Assets)
var pics : String {
switch self {
case .bag: return "beg"
case .bird: return "birds"
}
}
}
Prepare segue - (storyboard should match identifier name)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowAnswers" {
guard
let controller = segue.destination as? proverbsViewController,
let category = sender as? Answers
else { return }
controller.answer = category.description
controller.image = UIImage(named: category.pics)!
}
}
Button to segue - all buttons are connected here (tagged in IB)
#IBAction func onButtonTap(_ sender: UIButton) {
performSegue(withIdentifier: "ShowAnswers", sender: Answers(rawValue: sender.tag))
}
}
Recieve data:
ViewController2 ->
var answer = ""
var image = UIImage()

Related

Using multiple toggles I'would like to regroup all toggles in one action

I'm trying to create an array composed of multiple tools.
The array changes depending on which tools are carried.
Expecting a long list of tools I would like to avoid having to write an action for each single element to add or remove from the array. ( I managed this part as you'll see below)
Instead I'd like to use the name of the toggle switch and apply the same action to all the toggles on the screen.
I came up with this inelegant method:
import UIKit
class mainVC: UIViewController {
var myEquippement: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func shovelSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Shovel")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Shovel")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func hammerSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Hammer")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Hammer")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func screwdriverSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Screwdriver")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Screwdriver")
myEquippement.remove(at: subjectIndex!)
}
}
#IBAction func sawSwitch(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append("Saw")
}
else {
let subjectIndex = myEquippement.firstIndex(of: "Saw")
myEquippement.remove(at: subjectIndex!)
}
}
}
could you point me please to a better way of doing it.
I thought of using something like this:
#IBAction func toggleSwitched(_ sender: UISwitch) {
if sender.isOn{
myEquippement.append(sender.title!)
}
else {
let subjectIndex = myEquippement.firstIndex(of: sender.title!)
myEquippement.remove(at: subjectIndex!)
}
}
but sender.title always returns a nil value, the force unwrapping crashes the app.
Thanks in advance for your help.
One solution would be to have a master array of tool names. When creating a switch, assign its tag property to the corresponding index within that array.
Then in your single switch handler you can use the sender's tag to get the name of the tool from the master list of tool names. That will give you the title for the switch. Then your logic for adding/removing the title from myEquipment can be used.

Swift. Passing persistent data from one View Controller to another and storing within a label array

I am using a segue to go from View Controller 1 to View Controller 2. View Controller 1 has a button that sets the persistent data when it is clicked on:
I declare a global var for user default:
let userDefault = UserDefaults()
Here is my button to set the user default to a string with text values from labels:
#IBAction func saving(_ sender: UIButton) {
let savedText = "Gallon \(gallonTextFieldOutlet.text) is equal to Litre \(litreTextFieldOutlet.text) is equal to Pint \(pintTextFieldOutlet.text)"
userDefault.setValue(savedText, forKey: "SavedConversion")
}
I then get a reference to View Controller 2 and pass this user default when the user goes from View Controller 1 to View Controller 2 via a segue:
// in view controller 2: reference to get persistent data
var volumeDataOne:String?
// in view controller 2: instantiation of my queue class to use methods
var queue = Queue<String>()
// segue action in view controller 1
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "savedVolumeData"
{
let historyVC:VolumeHistoryViewController = segue.destination as! VolumeHistoryViewController
if let value = userDefault.value(forKey: "SavedConversion") as? String {
historyVC.volumeDataOne = value
}
}
I get this in the View Controller 2 and I am trying to set this to three labels that I have in this View Controller:
func DisplayVolumeHistory() {
let labelArray = [volumeDataLabelOutlet, volumeDataLabelTwoOutlet, volumeDataLabelThreeOutlet]
if let bindingOptional = volumeDataOne
{
for index in 0..<labelArray.count
{
queue.enqueue(val: bindingOptional)
labelArray[index]?.text = queue.arr[index]
}
}
}
In my specification, I have been told that the data needs to be persistent and that only the last five data can be stored at one time. So I have a class that I have called Queue which is referenced in this function. This function gets called on the viewDidLoad of the View Controller 2.
override func viewDidLoad() {
super.viewDidLoad()
//Debug ...
volumeDataLabelOutlet.text = "na"
volumeDataLabelTwoOutlet.text = "na 2"
volumeDataLabelThreeOutlet.text = "na 3"
//...
DisplayVolumeHistory()
// Do any additional setup after loading the view.
}
I have tested my Queue class in a Playground and it works as expected. The Queue class can be seen here:
class Queue {
var arr = [T]()
func enqueue(val: T){
if(arr.count < 3) {
arr.append(val)
} else {
for i in 0..<arr.count-1 {
arr[i] = arr[i + 1]
}
arr[arr.count - 1] = val
}
}
func dequeue() -> (T?){
if (arr.isEmpty){
return nil
} else {
return arr.remove(at: 0)
}
}
}
Here is my issue that I cannot seem to figure out. In View Controller 2, all of the three labels will have persistent data, but they will all be of the same data,
For example, if I have data as follows:
DATA 1: 555
DATA 2: 700
DATA 3: 62
I would want:
LABEL 1 --> 555
LABEL 2 --> 700
LABEL 3 --> 62
However, currently it will be:
LABEL 1 --> 62
LABEL 2 --> 62
LABEL 3 --> 62
I am unsure as to why debugging. I believe it is because my persistent data in my View Controller 1 is only taking a string, which the Dictionary is overriding as I use the same key.
However, I looked at the documentation and trying to use a user default array did not solve my issue and I am unsure as to what is causing this problem.
I appreciate any guidance and help to try to solve this issue.
Thanks
You are right on your comment,
which the Dictionary is overriding as I use the same key
Each time you tap that button, you are overriding the value with the new one. So you should be seeing always on your v2, the last one.
So probably you should store an array instead of a String.
//on v1
var values = [String]()
#IBAction func saving(_ sender: UIButton) {
let savedText = "Gallon \(gallonTextFieldOutlet.text) is equal to Litre \(litreTextFieldOutlet.text) is equal to Pint \(pintTextFieldOutlet.text)"
values.append(savedText)
UserDefaults.standard.setValue(values, forKey: "SavedConversion")
}
You will pass it to v2 as you are doing now
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "savedVolumeData"
{
let historyVC:VolumeHistoryViewController = segue.destination as! VolumeHistoryViewController
if let values = userDefault.value(forKey: "SavedConversion") as? [String] {
historyVC.volumeDataOne = values //volumeDataOne now needs to be an [String]
}
}
Then on V2, go through your array and labels.

How to add an alert to switch toggle for multiple answers?

I created a Quiz app. There is one question with five answers, where the user can choose one of them. However at the moment it's possible to choose more than one answer. How can I add an alert (UIAlertController) and a restriction so if the user has already chosen one answer and tries to choose a second one ?
var questionIndex = 0
var answersChosen: [Answer] = []
func updateUI() {
let currentQuestion = questions[questionIndex]
let currentAnswers = currentQuestion.answers
let totalProgress = Float(questionIndex) /
Float(questions.count)
numberOfQuestion.text = "\(questionIndex+1)/5"
// navigationItem.title = "Вопрос - \(questionIndex+1)"
mainQuestion.text = currentQuestion.text
progressBar.setProgress(totalProgress, animated:
true)
updateMultipleStack(using: currentAnswers)
}
#IBAction func multipleAnswerButtonPressed(_ sender: Any) {
let currentAnswers = questions[questionIndex].answers
if firstSwitch.isOn {
answersChosen.append(currentAnswers[0])
}
if secondSwitch.isOn {
answersChosen.append(currentAnswers[1])
}
if thirdSwitch.isOn {
answersChosen.append(currentAnswers[2])
}
if fourthSwitch.isOn {
answersChosen.append(currentAnswers[3])
}
if fifthSwitch.isOn {
answersChosen.append(currentAnswers[4])
}
nextQuestion()
}
func updateMultipleStack(using answers: [Answer]) {
firstSwitch.isOn = false
secondSwitch.isOn = false
thirdSwitch.isOn = false
fourthSwitch.isOn = false
fifthSwitch.isOn = false
firstQuestionLbl.text = answers[0].text
secondQuestionLbl.text = answers[1].text
thirdQuestionLbl.text = answers[2].text
fourthQuestionLbl.text = answers[3].text
fifthQuestionLbl.text = answers[4].text
}
override func prepare(for segue: UIStoryboardSegue, sender:
Any?) {
if segue.identifier == "ResultSegue" {
let resultsViewController = segue.destination as! ResultViewController
resultsViewController.responses = answersChosen
}
}
}
The approach I'd take would be to separate the user's selection of an answer (UISwitch collection) from processing the selected answer(Submit Answer button). This would allow the user to change their answer. The previous selection is automatically cleared. The answer is final when pressing a "Submit Answer" button (not shown).
In this code example, the switches are set as an IBOutlet collection. The tag on each switch is set from 0 to 4. All the switches are connected to an IBAction called switchPressed. A variable called selectedAnswer is used to identify the user's final picked answer when the "Submit Answer" is pressed.
#IBOutlet var switches: [UISwitch]!
private let noAnswer = -1
private var selectedAnswer = -1
#IBAction func switchPressed(_ sender: UISwitch) {
// Check if user turned off switch
guard sender.isOn == true else {
selectedAnswer = noAnswer
return
}
// Get user's answer and set all switches
selectedAnswer = sender.tag
switches.forEach { $0.isOn = false }
sender.isOn = true
}
#IBAction func submitAnswer(_ sender: UIButton) {
guard selectedAnswer > noAnswer else { return }
// use selectedAnswer to process answer and go to the next question
answersChosen.append(currentAnswers[selectedAnswer])
}
You could store all the switches in an array named Switches for example and then run a for loop to check if more than one is selected when checking answer.
var count = 0
for switchToggle in switches {
if switchToggle.isOn {
count += 1
if count > 1 {
//Print Warning "only select one"
}
} else {
//append answers
}
}
you could also use this to have multiple answers for certain quiz question
For the warning, you have different options you could use. You could use UIAlertController to display a warning if more than one button is selected but this could become annoying for the user after a while.
Another option could be to use a UILabel which warns the user to only pick one answer. You could animate the label to fade away after a few seconds.

inserting data into array of type swift class

How can i insert a value of textfield texts into an array of type class?
Basically, i have two view controllers one shows the list of items and code created using the following class
class Courses {
var courseName : String
var courseCode : Int
init(courseName : String, courseCode : Int){
self.courseName = courseName
self.courseCode = courseCode
}
}
//created array from that class
var courses : [Courses] = [Courses(courseName: "Unix Linux", courseCode: 101),
Courses(courseName: "ASP.Net", courseCode: 202),
Courses(courseName: "CISCO", courseCode: 203),
Courses(courseName: "Photoshop Editing", courseCode: 306)
]
Second View Controller has two text field which will allow someone to add new courses to the existing array. How can i achieve that and reload the table view with new items from a different view controller
i added var courses : [Courses] = [] on the second view controller.swift file. not sure what else to do.
I tried something like this
#IBAction func addCourseButton(_ sender: Any) {
if courseName.text != "" {
let courseCodeString = Int(courseCode.text!)
let item = Courses(courseName: "\(courseName.text)", courseCode: courseCodeString!)
courses.append(item)
for i in courses {
print(i.courseName)
}
}
}
There are two possible solutions to your problem.
Using Protocols
Using Notification Observers
In 1st case,
Add protocol in Second ViewController.swift, just above the class definition
protocol UpdateCourseAfterAddProtocol:class {
func classListUpdated(with course: Courses)
}
class SecondViewController:UIViewController {
weak var dele:UpdateCourseAfterAddProtocol? = nil
#IBAction func addCourseButton(_ sender: Any) {
if courseName.text != "" {
let courseCodeString = Int(courseCode.text!)
let item = Courses(courseName: "\(courseName.text)", courseCode: courseCodeString!)
dele?.classListUpdated(with: item)
}
}
}
Add Segue name in Main.storyboard to "firstToSecond" or any name
In first View Controller where your table View Exists (Suppose name is FirstViewController)
class FirstViewController {
// Bar button Action
#IBAction func addCoursePressed(_ send:UIBarButton) {
self.performsegue(with:"firstToSecond")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "firstToSecond" {
let toViewController = segue.destination as! SecondViewController
toViewController.dele = self
}
}
}
extension FirstViewController: UpdateCourseAfterAddProtocol {
func classListUpdated(with course: Courses) {
self.courses.append(course)
self.tableView.reloadData()
}
}

How to send Parse object field from one class to another?

I want to make such thing:
On one ViewControleer I'm making a query to Parse.com, where I'm sending objects fields to Label.Text. By clicking one button objects randomly changes, by clicking another one- next ViewController is opening. Just imagine Tinder - on the first VC I swiping girls, on the new one chat is opening, with the girl's name in the head of the NavigatorItem
So I want to send Object Field "Name" that I'm using in that view to another without other query.
I don't know, whether I can do it via segue, or protocol. Can U somehow help me with implementation?
here is the code of my random function
func retriveJobData() {
var query: PFQuery = PFQuery(className: "Jobs")
query.getObjectInBackgroundWithId("AUeuvj0zk2") {
(newJobObject: PFObject?, error: NSError?) -> Void in
if error == nil && newJobObject != nil {
println(newJobObject)
if let newJobObject = newJobObject {
self.PrcieTextField.text = newJobObject["jobPrice"] as? String
self.DateTextField.text = newJobObject["jobDate"] as? String
self.DescriptionTextField.text = newJobObject["jobDescription"] as? String
}
} else {
println(error)
}
}
}
I want to send newJobObject["jobName"] to NavigatorItemName of another ViewController
you can override prepareForSegue for this purpose:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "yourSegueIdentifier") {
// pass data to next view
}
}
Assuming you have some method that triggers a push to the new viewController and that you're using the storyboard, call performSegue using the identifier you set up in the storyboard
#IBAction func buttonPressed(sender: UIButton!) {
performSegueWithIdentifier("identifier", sender: nil)
}
Then override prepareForSegue and pass in the string
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "identifier" {
let controller = segue.destinationViewController as! ViewController
controller.jobName = someWayThatYouRetrieveNewJobObjectName
}
Then in ViewController ensure you have a property for jobName
var jobName:String! //declare this as an optional instead if needed
And set the navigation title
navigationItem.title = jobName

Resources