Swift - Execute performSegue under conditions - ios

My intention is to run performSegue solely if all textfields are filled. If not, the button is not supposed to work - to be exact, performSegue shall not be executed.
My approach was to put performSegue inside of an if statement, but somehow it's being ignored and performSegue is being executed anyways, even though both fields are empty. Is there another more successful approach?
#IBAction func buttonAdd(_ sender: Any) {
if (addKmInput.text! != "" && addPriceInput.text != "") {
...
performSegue(withIdentifier: "goBackToSecond", sender: self)
}
}
#IBOutlet weak var addKmInput: UITextField!
#IBOutlet weak var addPriceInput: UITextField!
New version:
#IBAction func buttonAdd(_ sender: Any) {
performSegue(withIdentifier: "goBackToSecond", sender: self)
}
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
switch identifier {
case "goBackToSecond":
return shouldGoBackToSecond()
default:
return true
}
}
func shouldGoBackToSecond() -> Bool {
guard let kmInput = addKmInput.text, let priceInput = addPriceInput.text else { return false }
return !kmInput.isEmpty && !priceInput.isEmpty
}

Try following solution:
#IBAction func buttonAdd(_ sender: Any) {
if shouldGoBackToSecond() {
performSegue(withIdentifier: "goBackToSecond", sender: self)
}
}
func shouldGoBackToSecond() -> Bool {
guard let kmInput = addKmInput.text, let priceInput = addPriceInput.text else { return false }
return !kmInput.isEmpty && !priceInput.isEmpty
}

use str.isEmpty to check if the string int he textField is empty :
if let t1 = addKmInput?.text, let t2 = addPriceInput?.text, !t1.isEmpty, !t2.isEmpty {
...
performSegue(withIdentifier: "goBackToSecond", sender: self)
}
and you should ideally use this delegate to deny segue performance:
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
return true //return true/false conditionally. returning false will prevent segue performance.
}

Related

Swift - Accessing implicitly unwrapped variable gives a nil error

I'm following a tutorial on CoreData and I've been following it exactly, yet when they run the app, everything works and saves correctly, yet I get a nil error. The tutorial is a few years old, so I'm not sure if something has been udpated in the way CoreData works. It's an app to save goals.
Here's the first view controller where you enter the text of the goal and if it is short or long term:
import UIKit
class CreateGoalViewController: UIViewController, UITextViewDelegate {
#IBOutlet weak var goalTextView: UITextView!
#IBOutlet weak var shortTermButton: UIButton!
#IBOutlet weak var longTermButton: UIButton!
#IBOutlet weak var nextButton: UIButton!
var userGoalType: GoalType = .shortTerm
override func viewDidLoad() {
super.viewDidLoad()
nextButton.bindToKeyboard()
shortTermButton.setSelectedColor()
longTermButton.setDeselectedColor()
print("\(userGoalType)")
goalTextView.delegate = self
}
#IBAction func nextButtonPressed(_ sender: Any) {
if goalTextView.text != "" && goalTextView.text != "What is your goal?" {
guard let finishVC = storyboard?.instantiateViewController(withIdentifier: "FinishVC") as? FinishGoalViewController else {return}
finishVC.initData(description: goalTextView.text!, type: userGoalType)
print("\(finishVC.goalType.rawValue) after next button pressed")
performSegue(withIdentifier: "goToFinish", sender: self)
}
}
#IBAction func longTermButtonPressed(_ sender: Any) {
userGoalType = .longTerm
longTermButton.setSelectedColor()
shortTermButton.setDeselectedColor()
print("\(userGoalType)")
}
#IBAction func shortTermButtonPressed(_ sender: Any) {
userGoalType = .shortTerm
shortTermButton.setSelectedColor()
longTermButton.setDeselectedColor()
print("\(userGoalType)")
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true)
}
func textViewDidBeginEditing(_ textView: UITextView) {
goalTextView.text = ""
goalTextView.textColor = UIColor(ciColor: .black)
}
}
And here's the following view controller where you set the number of times you want to do that goal where the CoreData functions are:
import UIKit
import CoreData
class FinishGoalViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var createButton: UIButton!
#IBOutlet weak var pointsTextField: UITextField!
var goalDescription: String!
var goalType: GoalType!
func initData(description: String, type: GoalType) {
self.goalDescription = description
self.goalType = type
}
override func viewDidLoad() {
super.viewDidLoad()
createButton.bindToKeyboard()
pointsTextField.delegate = self
}
#IBAction func createGoalPressed(_ sender: Any) {
if pointsTextField.text != ""{
self.save { finished in
if finished {
dismiss(animated: true)
}
}
}
}
#IBAction func backButtonPressed(_ sender: Any) {
dismiss(animated: true)
}
func save(completion: (_ finished: Bool) -> ()) {
guard let managedContext = appDelegate?.persistentContainer.viewContext else {return}
let goal = Goal(context: managedContext)
goal.goalDescription = goalDescription
goal.goalType = goalType.rawValue
goal.goalCompletionValue = Int32(pointsTextField.text!)!
goal.goalProgress = Int32(0)
do{
try managedContext.save()
print("successfully saved data")
completion(true)
}catch{
debugPrint("Could not save: \(error.localizedDescription)")
completion(false)
}
}
}
I'm getting a nil error in the save function with the goalType.rawValue turning up nil. The goal type is set up in an enum file:
import Foundation
enum GoalType: String {
case longTerm = "Long Term"
case shortTerm = "Short Term"
}
I'm not sure why there's an error. Because in the CreateGoalViewController, I print the goalType.rawValue from the following view controller and it comes up with the correct string, either short or long-term. But when FinishGoalViewController loads, it is all of a sudden nil.
You are initiating and configuring your FinishGoalViewController in nextButtonPressed but you never use it. performSegue(withIdentifier: "goToFinish", sender: self) will create and push a new instance of FinishGoalViewController.
The most simple aproach would be to push your allready configured controller from your curent Controller. Remove performSegue(... and use.
self.navigationController?.pushViewController(finishVC, animated: true)
If you still want to use the segue, remove everything from the nextButtonPressed function, leaving just the performSegue(... line. After that add this function to your CreateGoalViewController controller.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToFinish" {
if let finishVC = segue.destination as? FinishGoalViewController {
// configure finshVC here
}
}
}

Unexpectedly found nil while unwrapping an Optional value from XIB file

Error: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Hello everyone, I'm making an app that functions like a "To-Do List" app. However I've made two XIBs for my two custom cells that I want displayed on my TableView.
Keep in mind this is my first time using XIBs and building something that's not been a follow along. I've watched many "To-Do List" projects to get an idea of how this should work so I feel as if I'm on the right path. Anyways, to the code.
My prepare for segue func that is giving the error after putting text into the box :
var card: Card?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
guard segue.identifier == "saveUnwind" else { return }
let taskText = card!.taskCell.taskText.text
let taskBtn = card!.taskCell.taskBtn.isSelected
let gratefulText = card!.gratefulCell.gratefulText.text
let gratefulNum = card!.gratefulCell.gratefulNum.text
card = Card(taskText: taskText!, taskBtn: taskBtn, gratefulText: gratefulText!, gratefulNum: gratefulNum!)
}
Here is my "Card" struct:
struct Card: Equatable {
let gratefulCell = GratefulCell()
let taskCell = TaskCell()
var id = UUID()
var taskText: String
var taskBtn: Bool
var gratefulText: String
var gratefulNum: String
static let dueDateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .short
return formatter
}()
static func ==(lhs: Card, rhs: Card) -> Bool {
return lhs.id == rhs.id
}
static func loadTasks() -> [Card]? {
return nil
}
static func loadSampleTasks() -> [Card]? {
let task1 = Card(taskText: "Test", taskBtn: false, gratefulText: "Test", gratefulNum: "1")
let task2 = Card(taskText: "Test2", taskBtn: false, gratefulText: "Test2", gratefulNum: "2")
return [task1, task2]
}
}
Also here is the XIB file in which I'm trying to access the .text on:
class TaskCell: UITableViewCell {
#IBOutlet weak var taskBtn: UIButton!
#IBOutlet weak var taskText: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
// updateSaveButtonState()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
#IBAction func textEditingChanged(_ sender: UITextField) {
// updateSaveButtonState()
}
#IBAction func isCompleteButtonTapped(_ sender: UIButton) {
taskBtn.isSelected.toggle()
}
}
If there's any other information I can provide please let me know! Any advice is greatly appreciated.

Button.titleLabel error in swift3

I am trying to compare the title that I have in a button so that if it is compared to another it does a certain thing or not. I'm using the .setTitle function but I think in swift3 it gives error and I can not find the why. Then I leave a catch of the error to make it easier to see it and also the code itself. Thanks for the help
#IBAction func _login_button(_ sender: Any)
{
if(_login_button.titleLabel?.text == "OKEY")
{
let preferences = UserDefaults.standard
preferences.removeObject(forKey: "session")
LoginToDo()
return
}
let username = _username.text
let password = _password.text
if(username == "" || password == "")
{
return
}
DoLogin(username!, password!)
}
change your #IBAction func _login_button(_ sender: Any) to you need to type cast from Any to UIButton #IBAction func _login_button(_ sender: UIButton)
#IBAction func _login_button(_ sender: UIButton)
{
if(sender.titleLabel?.text == "OKEY")
{
}
}
_login_button is #IBAction you have to create the button for #IBOutlet of button
you need to get button as below , now you access function name instead of sender
#IBAction func _login_button(_ sender: Any)
let button: UIButton = sender as! UIButton
if(button.titleLabel?.text == "OKEY")
{
}
}
If you get this error as per screen shot, you need to type cast from Any to UIButton and then use titleLabel property.

How can I call in Swift 3 the method prepare after the user's input has four characters?

I have the following methods:
#IBAction func postcodeTextfieldDidChanged(_ sender: UITextField) {
if (postcodeTextField.text?.characters.count == 4) {
self.postcode = postcodeTextField.text
postcodeHas4Chars = true
} else {
postcodeHas4Chars = false
}
}
And the prepare-method:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "CoffeeShopListWithPostcode" {
if (postcodeHas4Chars == true) {
if let list = segue.destination as? CoffeeShopListVC {
list.postcode = postcode
} else {
print("Data NOT Passed!")
}
}
} else { print("Id doesnt match with Storyboard segue Id")
}
}
The code works and I can send the postcode to the next ViewController, but how can I change the code to automatically call the method after the fourth input?
At the moment, the user must manually exit the input so that it can be executed.
Each time the text field's text change, check if the text is 4 characters. If it is, call performSegue(withIdentifier, sender:). Simple:
#IBAction func postcodeTextfieldDidChanged(_ sender: UITextField) {
if (postcodeTextField.text?.characters.count == 4) {
self.postcode = postcodeTextField.text
postcodeHas4Chars = true
performSegue(withIdentifier: "CoffeeShopListWithPostcode", sender: self)
} else {
postcodeHas4Chars = false
}
}
Also, remember that you connect to the editingChanged event instead of the valueChanged event.

Cannot receive event with custom DelegateProxy and Protocol

I try to migrate delegate of DifficultyViewDelegate to observable. This is my DifficultyViewDelegate :
#objc protocol DifficultyViewDelegate: class {
func levelDidIncrease()
func levelDidDecrease()
}
And my DifficultyView :
weak var delegate: DifficultyViewDelegate?
#IBAction func decreaseLevel(_ sender: Any) {
delegate?.levelDidDecrease()
}
#IBAction func increaseLevel(_ sender: Any) {
delegate?.levelDidIncrease()
}
And this is my RxDifficultyViewDelegateProxy
class RxDifficultyViewDelegateProxy: DelegateProxy, DelegateProxyType {
static func currentDelegateFor(_ object: AnyObject) -> AnyObject? {
let difficultyView: DifficultyView = object as! DifficultyView
return difficultyView.delegate
}
static func setCurrentDelegate(_ delegate: AnyObject?, toObject object: AnyObject) {
let difficultyView: DifficultyView = object as! DifficultyView
difficultyView.delegate = delegate as? DifficultyViewDelegate
}
}
I also added an extension on my DifficultyView :
extension DifficultyView {
public var rx_delegate: RxDifficultyViewDelegateProxy {
return RxDifficultyViewDelegateProxy.proxyForObject(RxDifficultyViewDelegateProxy.self)
}
public var rx_levelDidIncrease: Observable<Void> {
return rx_delegate.methodInvoked(#selector(DifficultyViewDelegate.levelDidIncrease)).map { _ in return }
}
}
But it seems that when I do :
difficultyView.rx_levelDidIncrease.asObservable().subscribe(onNext: {
print("did increase")
}).addDisposableTo(disposeBag)
It's never called. Someone has any pointers ?
Try use PublishSubject:
DifficultyView:
class DifficultyView: UIView {
var levelDidIncrease = PublishSubject<Void>()
var levelDidDecrease = PublishSubject<Void>()
#IBAction func decreaseLevel(_ sender: Any) {
levelDidDecrease.onNext()
}
#IBAction func increaseLevel(_ sender: Any) {
levelDidIncrease.onNext()
}
}
And then:
var difficultyView = DifficultyView()
difficultyView.levelDidDecrease.asObservable()
.subscribe(onNext: {
print("did decrease")
})
.addDisposableTo(disposeBag)
difficultyView.decreaseLevel(theSender) // <- THIS line performs the side effect

Resources