I am new to Swift language. I'm trying to use SwiftValidator library from #jpotts18. I got to use rules and validate UITextFields but I can't get them to normal state if they're correct.
This is my view controller
class SignUpViewController: UIViewController, ValidationDelegate {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var rePasswordTextField: UITextField!
let validator = Validator()
override func viewDidLoad() {
super.viewDidLoad()
validator.registerField(nameTextField, rules: [RequiredRule(), FullNameRule()])
}
func validationSuccessful() {
print("success")
}
func validationFailed(errors: [(Validatable, ValidationError)]) {
for (field, _) in errors {
if let field = field as? UITextField {
field.layer.borderColor = UIColor.redColor().CGColor
field.layer.borderWidth = 1.0
}
}
}
#IBAction func onRegisterClick(sender: UIButton) {
validator.validate(self)
}
}
My view turns red if validation fails but if it doesn't I don't know what code should I use to return that specific UITextField to normal state.
Any suggestions?
Thanks.
After some trial and error I managed to do what I want with this code.
validator.styleTransformers(success: {
(validationRule) -> Void in
print("Field Validated")
if let textField = validationRule.field as? UITextField {
textField.textColor = UIColor.blackColor()
}
}, error: {
(validationError) -> Void in
print("Field Incorrect")
if let textField = validationError.field as? UITextField {
textField.textColor = UIColor.redColor()
}
})
Related
I've been working through a project and have gotten most of the functionality straight, however there's one part that continues to stump me. I am using MVC format, and I have a two view controllers (one profile and one game). When progress is made on the game, the proper changes to game view controller show (i.e. button disappears and UI background color changes colors). However, when I segue to profile view controller using 'back' on navigation bar and return to the game view controller, the game view controller returns to the default without reflecting game progress.
I've tried unwinding segue, making a delegate going from view controller to model responsible for game play, adjusting my game logic, and trying viewdidload and viewwillappear. I'm running out of ideas.
Here are samples of pertinent code:
Game View Controller
import UIKit
import Firebase
class GameController: UIViewController, BrainDelegate, UITextFieldDelegate {
#IBOutlet weak var dateUsername: UILabel!
#IBOutlet weak var questionText: UILabel!
#IBOutlet weak var scoreText: UILabel!
#IBOutlet weak var answerOne: UITextField!
var userListOne: [String] = []
#IBOutlet weak var answerTwo: UITextField!
#IBOutlet weak var answerThree: UITextField!
#IBOutlet weak var answerFour: UITextField!
#IBOutlet weak var enterOne: UIButton!
#IBOutlet weak var enterTwo: UIButton!
#IBOutlet weak var enterThree: UIButton!
#IBOutlet weak var enterFour: UIButton!
#IBOutlet weak var feedbackText: UILabel!
#IBAction func logoutPressed(_ sender: UIBarButtonItem) {
do {
try Auth.auth().signOut()
navigationController?.popToRootViewController(animated: true)
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
}
}
let db = Firestore.firestore()
var questions = [Question]()
var Brain = Brain()
var delegate: brainDelegate?
//variables for delegate with json data
var questioner : String?
var dater : String?
var a1 : String?
var a2 : String?
var a3 : String?
var a4 : String?
//For game play
var answer1: String?
var answer2: String?
var answer3: String?
var answer4: String?
var puzzlesCompleted = "0"
func didUpdateBrain(narratives: [Question]) {
self.questions = narratives
}
func didFailWithError(error: Error) {
print(error)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
minketBrain.delegate = self
answerOne.delegate = self
answerTwo.delegate = self
answerThree.delegate = self
answerFour.delegate = self
if let localData = minketBrain.readLocalJSONFile(forName: K.minketClues) {
minketBrain.parseJSON(jsonData: localData)
}
//Transfer data from Profile Controller
self.questionText.text = questioner
self.dateUsername.text = dater
self.answer1 = a1
self.answer2 = a2
self.answer3 = a3
self.answer4 = a4
}
override func viewWillAppear(_ animated: Bool) {
view.reloadInputViews()
answerOne.text = userListOne.last
}
//MARK: - Button Logic
#IBAction func enterPressed(_ sender: UIButton) {
enterPressedOne()
allRight()
}
//Other buttons replicate the same code above
//MARK: - Correct Answer Logic
func allRight(){
if answerOne.backgroundColor == UIColor.green && answerTwo.backgroundColor == UIColor.green && answerThree.backgroundColor == UIColor.green && answerFour.backgroundColor == UIColor.green {
feedbackText.text = K.congratsText
puzzlesCompleted = "1"
if let allAnswer = scoreText.text, let userName = Auth.auth().currentUser?.email{
db.collection(K.FStore.collectionName).addDocument(data: [K.FStore.userName:userName, K.FStore.clueDate:dateUsername.text!, K.FStore.totalPoints:allAnswer, K.FStore.completionField:puzzlesCompleted]) { (error) in
if let e = error{
print("There was an error in data collection to Firestore \(e)")
}else{
print("Successfully saved data")
}
}
}
}
}
//MARK: - Update UI-Each Question
func enterPressedOne(){
if let item = answerOne.text, item.isEmpty == true { // need to make sure we have something here
userListOne.append(item) // store it in our data holder
print(userListOne)
}
if answerOne.text == self.answer1!.lowercased(){
answerOne.backgroundColor = UIColor.green
enterOne.isHidden = true
answerOne.isEnabled = false
}else{
answerOne.backgroundColor = UIColor.red
}
}
}
Unwind segue function in my profile controller
#IBAction func unwindToDestinationViewController (sender: UIStoryboardSegue){
print("Unwind")
}
I want to make error handling pages to all of my view controller through, so when error fetching data error, it's not only showing dialog, but showing a XIB files that contain error message and button to refresh. Like this:
Here's the XIB Class code:
import UIKit
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
#IBAction func didTapTryAgain(_ sender: Any) {
Dialog.showProgressDialog(info: "")
}
}
Here's my base controller code, who handling all the problem.
import Foundation
class BaseViewController: UIViewController {
var uiView = UIView();
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
self.view.addSubview(viewErrorMessage)
}
}
And here's how I call it in another class which I extend BaseViewController, so it can show the error problem globally, without I redeclared again the class:
func onFailedDeleteCart(errorMessage: String) {
getErrorMessage(message: errorMessage)
}
Right now I can pass the error message.
The problem is, I want the Refresh button refreshing current View Controller when I click it. Maybe calling current View Controller's viewDidLoad when I click it will be the nice logic but I don't know how to implement it in XIB class. Anyone can solve out this? Thank you!
Approach: 1
Step:1 Create closure for callback
typealias RefreshBlock = (()->())?
Step:2 Define closure in your UIView class
Step:3 Call closure if user tap refresh button
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
var refreshBlock:RefreshBlock!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
// Step : 3
#IBAction func didTapTryAgain(_ sender: UIButton) {
refreshBlock!()
}
}
Step:4 Assign value in closure when addSubview called
class BaseViewController: UIViewController {
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
viewErrorMessage.refreshBlock = {()
self.viewDidLoad()
print("Refresh Contents")
}
self.view.addSubview(viewErrorMessage)
}
}
Approach: 2
Pass your current UIViewController Reference into UIView class. Refer below code.
class ErrorMessage: UIView {
#IBOutlet weak var imageViewError: UIImageView!
#IBOutlet weak var labelError: UILabel!
#IBOutlet weak var buttonTryAgain: UIButton!
var currentVC:UIViewController!
static var message: String?
override func awakeFromNib() {
labelError.text = ErrorMessage.message
}
#IBAction func didTapTryAgain(_ sender: UIButton) {
currentVC.viewDidLoad()
}
}
class BaseViewController: UIViewController {
override func viewDidLoad() {
}
func getErrorMessage(message:String) {
super.viewDidLoad()
ErrorMessage.message = message
guard let viewErrorMessage = Bundle.main.loadNibNamed("ErrorMessage", owner: self, options: nil)?.first as? ErrorMessage else { return}
viewErrorMessage.currentVC = self
self.view.addSubview(viewErrorMessage)
}
}
I'm new in Swift 3, and I just notice that we need to scroll a view since the keyboard can go over the some widgets as we type in text fields, like in this video. Fixing UITextField-Keyboard Problems (Swift in Xcode).
However, I am having an unfortunate behaviour even before programmatically scrolling the view. My view keeps being reset to its initial position. I made a video showing this behaviour.
I can't find mentions for this problem. Would someone have a clue?
[EDITED]
Here is the widgets are structured
Here is my View Controller code:
import UIKit
import AVFoundation
class SignUpController: UIViewController, UITextFieldDelegate
{
#IBOutlet var scrollView: UIScrollView!
#IBOutlet var stackView: UIStackView!
#IBOutlet var usernameTextField: UITextField!
#IBOutlet var passwordTextField: UITextField!
#IBOutlet var confirmationTextField: UITextField!
#IBOutlet var emailTextField: UITextField!
#IBOutlet var phoneTextField: UITextField!
#IBOutlet var firstNameTextField: UITextField!
#IBOutlet var lastNameTextField: UITextField!
#IBOutlet var signUpButton: UIButton!
var keyboardHeigh:CGFloat!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.scrollView.contentSize = self.stackView.frame.size
self.scrollView.contentSize.height += self.stackView.frame.minY + 20
}
override func viewDidLoad() {
super.viewDidLoad()
let textFields :[UITextField] =
[
usernameTextField,
passwordTextField,
confirmationTextField,
emailTextField,
phoneTextField,
firstNameTextField,
lastNameTextField,
]
for i in 0..<textFields.count
{
textFields[i].delegate = self
textFields[i].tag = i
}
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow),
name: .UIKeyboardWillShow, object: nil)
}
func keyboardWillShow(notification: NSNotification)
{
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
{
keyboardHeigh = keyboardSize.height
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
// Try to find next responder
if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField
{
nextField.becomeFirstResponder()
}
else
{
// Not found, so remove keyboard.
textField.resignFirstResponder()
}
return false
}
}
1.You can take UItableViewController with static cell,
2.Put textField inside cell.
3.Make selection for cell none.
4.Make separator none.
It will work fine.
Just learning so I am sure there are much better ways to code what I have so far. Anyways, I have a simple task list that is saved to core data. You can segue to another VC where you can edit or create new task. I wanted to be able to put a uiswitch in the custom cell that would allow you to set the task as complete and then reload the tableview. I have searched all over and found this Select UITableView's row when tapping on its UISwitch in Swift but can't seem to translate a solution. Any help would be great.
enter image description here
I am not sure if I am supposed to code the save within the custom cell or just or pass delegate.
Here is my custom cell
protocol SettingCellDelegate : class {
func didChangeSwitchState(sender: TaskCell, isOn: Bool)
}
class TaskCell: UITableViewCell {
var delegate: SettingCellDelegate!
#IBOutlet weak var taskImage: UIImageView!
#IBOutlet weak var arrowImage: UIImageView!
#IBOutlet weak var dueDateLabel: UILabel!
#IBOutlet weak var dueDateValue: UILabel!
#IBOutlet weak var dueTimeLabel: UILabel!
#IBOutlet weak var dueTimeValue: UILabel!
#IBOutlet var taskCellStatus: UITextField!
#IBOutlet var taskCellSwitchDisplay: UISwitch!
#IBAction func taskCellSwitchAction(sender: AnyObject) {
if (taskCellSwitchDisplay.on) {
taskCellStatus.text = "Completed"
} else {
taskCellStatus.text = "Active"
}
}
#IBOutlet weak var taskNameValue: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
delegate = self
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
taskCellSwitchDisplay.addTarget(self, action: "switchChanged:", forControlEvents: UIControlEvents.ValueChanged)
}
func configureCell(task: Tasks) {
taskImage.image = UIImage(named: "Icon.png")
arrowImage.image = UIImage(named: "arrow_filled.png")
dueDateLabel.text = "Due Date:"
dueDateValue.text = task.taskDate
dueTimeLabel.text = "Due Time:"
dueTimeValue.text = task.taskTime
taskNameValue.text = task.taskName
taskCellStatus.text = task.status
//taskCellStatus.hidden = true
if (taskCellStatus.text == "Completed") {
taskCellSwitchDisplay.on = true
} else {
taskCellSwitchDisplay.on = false
}
}
func switchChanged(taskCellSwitchDisplay: UISwitch) {
_ = taskCellSwitchDisplay.on
if taskCellSwitchDisplay.on == true {
let switchValue = "Completed"
print("Switch Variable: \(switchValue)")
delegate.didChangeSwitchState(self, isOn: true)
} else {
let switchValue = "Active"
print("Switch Variable: \(switchValue)")
delegate.didChangeSwitchState(self, isOn: false)
}
}
Here is where I am getting the delegate in my tableview
extension TaskCell: SettingCellDelegate {
func didChangeSwitchState(sender: TaskCell, isOn: Bool) {
if isOn == true {
print("true")
} else {
print("false")
}
}
I've done plenty of searching but am not finding the answer to my question.
My two UITextFields fields are resetting using the clear function. The UILabel retains the original value from the printWatts function, doesn't clear. Would appreciate any advice to resolve this small issue as I learn Swift. Thanks!
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var inputFeet: UITextField!
#IBOutlet weak var inputWatts: UITextField!
#IBOutlet weak var resultsLabel: UILabel!
var stripFeet = ""
var wattValue = ""
var totalWatts : Float = 0.0
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func submitButton(sender: AnyObject) {
calculateWatts()
}
#IBAction func clearButton(sender: AnyObject) {
clear()
}
func calculateWatts() {
if let stripFeet = inputFeet.text,
wattValue = inputWatts.text,
fstripFeet = Float(stripFeet),
fwattValue = Float(wattValue){
totalWatts = fstripFeet * fwattValue
}
printWatts()
}
func printWatts() {
let formatWatts = String(format: "%0.2f", totalWatts)
resultsLabel.text = "Total watts: \(formatWatts)"
}
func clear(){
inputFeet.text = ""
inputWatts.text = ""
self.resultsLabel.text = ""
}
}
Thanks to #Eendje for suggesting that I check my connections. I had the submit and clear actions both connected to my submit button. Option drag is too convenient. All good now.