Reputation: 67
I want to terminate the operation and hide the keyboard so that the user can get same result when they press calculate button multiple times. My codes are following
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var result: UILabel!
var myArray = [Int]() // empty array of int
func convert(a:Character) -> Int {
if (a == "1") {
return 1;
}
else {
return 0;
}
}
func power(a:Int , b:Int) -> Int {
var result = 1
for var i = 0; i < b; i++ {
result *= a
}
return result
}
@IBAction func binaryNumber(sender: AnyObject) {
var text = textField.text.toInt() // for checking whether or not user left the textfield empty
if text != nil {
for i in textField.text {
myArray += [convert(i)]
}
var exponent = myArray.count - 1;
//Multiplying binary number with power of 2
for var i = 0; i < myArray.count; i++ {
var base = 2;
myArray[i] = myArray[i] * power(base, b: exponent)
exponent--
}
// add up
var sum = 0
for var i = 0; i < myArray.count; i++ {
sum += myArray[i]
}
result.text = "\(sum)"
}
else {
result.text = "Please enter binary number"
}
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
self.view.endEditing(true)
}
}
Upvotes: 1
Views: 542
Reputation: 437917
In my original answer (at the end of this question), I answered a far more complicated question of how to terminate a time-consuming calculation.
But, as Sultan pointed out, you were probably asking a far simpler question, namely how to reset the calculation, so that when you calculate it a second time, you get the same result.
The root of your problem is that you have defined myArray
to be a property (which persists between calls to binaryNumber
), whereas it should be a local variable inside binaryNumber
.
For example:
// do not define `myArray` here:
//
// var myArray = [Int]()
@IBAction func binaryNumber(sender: AnyObject) {
// instead, define it here:
var myArray = [Int]()
// now the rest of your method continues as before
var text = textField.text.toInt() // for checking whether or not user left the textfield empty
if text != nil {
for i in textField.text {
myArray += [convert(i)]
}
var exponent = myArray.count - 1;
//Multiplying binary number with power of 2
for var i = 0; i < myArray.count; i++ {
var base = 2;
myArray[i] = myArray[i] * power(base, b: exponent)
exponent--
}
// add up
var sum = 0
for var i = 0; i < myArray.count; i++ {
sum += myArray[i]
}
result.text = "\(sum)"
}
else {
result.text = "Please enter binary number"
}
}
You, alternatively, could leave myArray
as a property, but then alter binaryNumber
to manually reset it:
@IBAction func binaryNumber(sender: AnyObject) {
myArray = [Int]()
// the rest of your method here
}
Personally, I prefer the former approach (making myArray
a local variable instead; it solves the problem and avoids unintended consequences), but either approach will address your problem.
BTW, my original answer, discussing how to cancel a long running operation, is below.
--
A couple of thoughts:
If you want to cancel an operation, add a boolean property that indicates whether it's canceled or not, and check for the state of that variable in the calculation loop in order to determine whether to abort the calculation.
For this to work, though, you really have to do that calculation in a background thread. So the background thread will do calculations and the main thread will handle the UI as well as initiate/cancel calculation operations.
An elegant pattern is to wrap the calculation in an NSOperation
subclass that has an existing cancelled
property that you can check and determine whether you want the operation cancelled.
So, wrap the calculation in an NSOperation
:
class CalculationOperation : NSOperation {
var inputString: String
var completionHandler: (Int)->()
init(string: String, completion: (Int)->()) {
inputString = string
self.completionHandler = completion
super.init()
}
func convert(character: Character) -> Int {
if character == "1" {
return 1
} else {
return 0
}
}
override func main() {
// get the array of numeric values for the characters
let digitValues = inputString.characters.map { convert($0) }
// convert to binary
var sum = 0
var exponent = digitValues.count - 1;
for var i = 0; i < digitValues.count; i++ {
if cancelled { return } // check to see if the user canceled the operation, and if so, quit
let digit = digitValues[i]
let value = digit * 1 << exponent // using bitwise shift is more efficient than `power`
sum += value
exponent--
}
dispatch_async(dispatch_get_main_queue()) {
self.completionHandler(sum)
}
}
}
Then your IBAction
can add this operation to a NSOperationQueue
in order to perform the operation in the background, first canceling any other operations on that queue:
lazy var queue = NSOperationQueue()
@IBAction func binaryNumber(sender: AnyObject) {
textField.endEditing(true)
queue.cancelAllOperations()
let operation = CalculationOperation(string: textField.text!) { result in
self.resultLabel.text = "\(result)"
}
queue.addOperation(operation)
}
By the way, the binary conversion routine in the above NSOperation
subclass can be made even more efficient using functional programming:
override func main() {
let digitValues = inputString.characters.map { convert($0) }
let binaryValue = digitValues.reduce(0) { $0 << 1 + $1 }
dispatch_async(dispatch_get_main_queue()) {
self.completionHandler(binaryValue)
}
}
Note, though, that these functional methods of map
and reduce
don't have an "cancel" feature, but they're so efficient that I don't think you'd ever need to. Anyway, if you implemented main
this way, you'd eliminate the need for the cancellation logic when you initiate the operation.
Rather than checking to see if the user entered a valid binary number, I'd make sure that they can only enter digits 0
and 1
in the text field to start with. Just specify the view controller as the delegate of the UITextField
, and then implement a shouldChangeCharactersInRange
that only accepts binary digits.
let nonBinaryCharacterSet = NSCharacterSet(charactersInString: "01").invertedSet
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.rangeOfCharacterFromSet(nonBinaryCharacterSet) != nil {
return false
} else {
return true
}
}
As you'll see above, you don't need to write a power
method. There is a built in function, pow
that does that already. Or you can use a bitwise shift operator, <<
, which is an efficient way to do binary exponents. E.g. 2 raised to the fourth power can be represented as 1 << 4
.
Upvotes: 2