UIButton inside UISegmentedControl action - ios

I am continuing my calculation program to improve my skills in Swift code. Right now I have a problem: I want that an UISegmentedControl select the operator of my operations (+, - ecc..) and when one is selected a UIButton func calculate the values based on the decision of the UISegmentedControl.
Here is the code but is not working.
Thanks,
Matteo
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var firstNumber: UITextField!
#IBOutlet weak var secondNumber: UITextField!
#IBOutlet weak var resultButton: UIButton!
#IBOutlet weak var resultNumber: UILabel!
#IBOutlet weak var operatorSelector: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func operatorSelectorChanged(_ sender: UISegmentedControl) {
switch operatorSelector.selectedSegmentIndex {
case 0:
#IBAction func resultFunction(_ sender: AnyObject) {
if let firstNumberConv :Int = Int(firstNumber.text!), let secondNumberConv :Int = Int(secondNumber.text!) {
let result = firstNumberConv + secondNumberConv
resultNumber.text = "\(result)"
} else { resultNumber.text = "Inserire solo valori numerici"}
}
case 1:
#IBAction func resultFunction(_ sender: AnyObject) {
if let firstNumberConv :Int = Int(firstNumber.text!), let secondNumberConv :Int = Int(secondNumber.text!) {
let result = firstNumberConv - secondNumberConv
resultNumber.text = "\(result)"
} else { resultNumber.text = "Inserire solo valori numerici"}
}
default:
break;
}
}
}

Try removing the lines with #IBAction func ... inside your switch cases.
I think your #IBAction func operatorSelectorChanged should look something like this:
#IBAction func operatorSelectorChanged(_ sender: UISegmentedControl) {
switch operatorSelector.selectedSegmentIndex {
case 0:
if let firstNumberConv = Int(firstNumber.text!), let secondNumberConv = Int(secondNumber.text!) {
let result = firstNumberConv + secondNumberConv
resultNumber.text = "\(result)"
} else { resultNumber.text = "Inserire solo valori numerici"}
case 1:
if let firstNumberConv = Int(firstNumber.text!), let secondNumberConv = Int(secondNumber.text!) {
let result = firstNumberConv - secondNumberConv
resultNumber.text = "\(result)"
} else { resultNumber.text = "Inserire solo valori numerici"}
default:
break;
}
}

A possible solution for your problem is to define an IBAction that calculates the result of adding/subtracting two numbers
#IBAction func calculate(_ sender: AnyObject) {
if let a = Int(firstNumber.text!), let b = IntsecondNumber.text!) {
switch operatorSelector.selectedSegmentIndex {
case 0:
resultNumber.text = "\(a+b)"
case 1:
resultNumber.text = "\(a-b)"
default:
print("❗️unknown operator selected")
}
}
}
You could then bind this action to the sent events from your user interface: for example the Editing Changed event of your text fields (firstNumber and secondNumber) and the Value Changed of your segmented control (operatorSelector)
I created an example project that illustrates this: https://github.com/dev1an/calculator
How to bind multiple events to one IBAction
Write down the IBAction in code (like the calculate action I wrote above). Go to your storyboard and open the assistent editor (AltCmdEnter). Select a UI element (like a text field) in the interface builder. Open the Connections Inspector (AltCmd6) and drag from the circle next to a sent event (for example Editing Changed) to your IBAction code.

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
}
}
}

How to connect multiple action to one button Xcode?

I'm a noob in ios development and i have a simple problem which i still cannot solve. The thing is i making a reverse words app and when the user tap the button at first time it will reverse the sample text but then when user tup second time same button it will clear the text inside sample text and result label. So the main question is how to connect the "clear action" to the same button ?
#IBOutlet var actReverseStyle: UIButton!
#IBOutlet weak var sampletext: UITextField!
var sample: String {return sampletext.text ?? ""
}
#IBOutlet weak var resultscreen: UILabel!
#IBAction func actreverse(_ sender: UIButton!) {
let sampleSentence = sample
func reverseWolrdsInSentance(sentanse:String) -> String{
let allWords = sampleSentence.components(separatedBy: " ")
var newSentence = ""
for word in allWords{
if newSentence != ""{
newSentence += " " }
let reverseWord = String(word.reversed())
newSentence += reverseWord}
return newSentence}
resultscreen.text = reverseWolrdsInSentance(sentanse: sampleSentence)
actReverseStyle.setTitle("Clear", for: .normal)
}
}
This may be more convenient.
#IBAction func actreverse(_ sender: UIButton!) {
sender.isSelected.toggle();
if sender.isSelected {
// do reverse
} else {
// do clear
}
}
Just use a boolean flag which toggles every time the action is fired
private var clearAction = false
#IBAction func actreverse(_ sender: UIButton!) {
if clearAction {
// do clear stuff
clearAction = false
} else {
// do reversing stuff
clearAction = true
}
}

Hide a website button in my tableViewCell

This is code for my custom tableViewCell
#IBOutlet weak var webButton: UIButton!
func update(place:EClass) {
self.place = place
myLabel.text = place.name
myImage.image = nil
myLabel2.text = place.getDescription()
// IF (didClickWebsite) THE SITE DOESN'T EXIST, MAKE INVISIBLE THE BUTTON webButton
if let url = place.photos?.first?.getPhotoURL(maxWidth: maxWidht) {
myImage.af_setImage(withURL: url)
}
}
#IBAction func goToWebSite(_ sender: Any) {
if let place = place, let delegate = delegate {
delegate.didClickWebsite(place: place)
}
}
where I recently added this button #IBOutlet weak var webButton: UIButton!
and this extension is in the VC of my tableView
extension CourseClass2: PlaceCellDelegate {
func didClickWebsite(place: EClass) {
NearbyZone.getZoneDetails(place: place) { (place) in
if let website = place.details?["website"] as? String, let url = URL(string: website) {
let svc = SFSafariViewController.init(url: url)
self.navigationController?.pushViewController(svc, animated: true)
}
}
}
}
what I would like to do is to tell the func update in my custom tableViewCell that if the function didClickWebsite does not produce any results (in this case when the site does not exist) it has to hide the website button, how caI i do this?

Add elements to search history?

I have a model - Movies.
and two controllers - first for search movie by title, second - for display result with poster, title and year.
Now i need to create some history search on my third controller
(searchHistoryController - TableView) where displayed all movies, and when i tapped on cell with movie's title show movie info.
How I can build it?
I tried create array in my model. And write resutl in it, but each time when i use search it rewrite array, not add new element.
Maybe use realm
Need some help:)
Movie.swift
import Foundation
import UIKit
import Alamofire
import AlamofireImage
protocol MovieDelegate {
func updateMovieInfo()
}
class Movie {
private let omdbUrl = "http://www.omdbapi.com/?"
var title: String?
var filmYear: String?
var poster: String?
var delegete: MovieDelegate!
var historyMovie = [Movie]()
func getMovieInfo(title: String, completion: #escaping ()->()){
let params = ["t": title]
Alamofire.request(omdbUrl, method: .get, parameters: params).validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON { (response) in
switch response.result {
case .success(let JSON):
let response = JSON as! NSDictionary
let status = response["Response"] as! String
if status == "True" {
self.title = (response["Title"] as! String)
self.filmYear = (response["Year"] as! String)
self.poster = (response["Year"] as! String)
// self.delegete.updateMovieInfo()
completion()
} else {
self.title = (response["Error"] as! String)
completion()
}
case .failure(let error):
print (error)
}
}
}
}
SearchVC
import UIKit
class SearchViewController: UIViewController {
var movie = Movie()
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var searchTextField: UITextField!
#IBOutlet weak var searchButton: UIButton!
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
self.performSegue(withIdentifier: "movieInfo", sender: self)
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
}
}
DetailVC
class DetailInfoViewController: UIViewController, MovieDelegate {
#IBAction func showHistory(_ sender: UIButton) {
performSegue(withIdentifier: "showHistory", sender: self)
}
#IBOutlet weak var posterImageView: UIImageView!
#IBOutlet weak var filmNameLabel: UILabel!
#IBOutlet weak var filmYearLabel: UILabel!
var movie = Movie()
var movieTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
self.movie.getMovieInfo(title: movieTitle ) {
self.updateMovieInfo()
}
self.movie.delegete = self
}
func updateMovieInfo() {
getPoster(link: movie.poster)
filmNameLabel.text = movie.title
filmYearLabel.text = movie.filmYear
}
func getPoster(link: String?) {
if link != nil {
guard let url = URL(string: link!) else { return }
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
self.posterImageView.image = UIImage(data: data)
}
}
} } else {
self.posterImageView.image = #imageLiteral(resourceName: "Image")
}
}
}
First of all, movieHistory should not be part of your Movie class, but part of your SearchViewController class.
Second of all, unless you want to persist your data, you don't need Realm for this.
Just save the movies in SearchViewController into an array once the search button has been tapped and send it to your other view controller in the segue. Like so
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
movieHistory.append(movie)
self.performSegue(withIdentifier: "movieInfo", sender: movieHistory)
})
}
}
Also, modify prepare(for segue:...) like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
secondVC.movieHistory = movieHistory
}
In detailVC override prepare(for segue:...) as well and send movieHistory to searchHistoryController the same way it is done in the previous VC.

How to make calculator calculate in decimal form

Here's my code for my calculator and it works fine!
import UIKit
class ViewController: UIViewController {
var number1 = 0
var number2 = 0
var operation = 0
#IBOutlet var screen: UILabel!
#IBAction func Onebutton(sender: AnyObject) {
addNumber(1)
}
#IBAction func Twobutton(sender: AnyObject) {
addNumber(2)
}
#IBAction func Threebutton(sender: AnyObject) {
addNumber(3)
}
#IBAction func Fourbutton(sender: AnyObject) {
addNumber(4)
}
#IBAction func Fivebutton(sender: AnyObject) {
addNumber(5)
}
#IBAction func Sixbutton(sender: AnyObject) {
addNumber(6)
}
#IBAction func Sevenbutton(sender: AnyObject) {
addNumber(7)
}
#IBAction func Eightbutton(sender: AnyObject) {
addNumber(8)
}
#IBAction func Ninebutton(sender: AnyObject) {
addNumber(9)
}
#IBAction func Zerobutton(sender: AnyObject) {
addNumber(0)
}
#IBAction func Multiplybutton(sender: AnyObject) {
operation = 1
}
#IBAction func Dividebutton(sender: AnyObject) {
operation = 2
}
#IBAction func Addbutton(sender: AnyObject) {
operation = 3
}
#IBAction func Subtractbutton(sender: AnyObject) {
operation = 4
}
#IBAction func Equalsbutton(sender: AnyObject) {
switch operation{
case 1: screen.text = String (number1*number2)
case 2: screen.text = String (number1/number2)
case 3: screen.text = String (number1+number2)
case 4: screen.text = String (number1-number2)
default: println()
}
}
#IBAction func clearbutton(sender: AnyObject) {
number1 = 0
number2 = 0
operation = 0
screen.text = "0"
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func addNumber (pushButton : Int){
if operation == 0 {
number1 = number1*10+pushButton
screen.text = String (number1)
} else {
number2 = number2*10+pushButton
screen.text = String (number2)
}
}
}
But I want to make the calculator calculate in decimal form and not round off to the nearest number.
Use floats.
var number: Float = 0
number = number + 0.1
println(number)
//0.1 should be printed.
You are currently basically saying:
var number: Int = 0
This is pretty much how the computer reads it; number is an integer. To make it so that number can be a decimal (float), you have to say:
var number: Float = 0

Resources