Avoid copying code for a lot of buttons - swift

Edit on my question:
Grimxn, I made a subclass and can see it works, because of the borderWidth and color. But I still have a couple of questions on how to add my function:
Should I code "func textField(textField: UITextField" or "func textField(textField: MyCustomTextField" ?
What should I do with "if textField == numberField01 {" ?
How do I 'call this' from the ViewController code ?
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var numberField01: UITextField!
#IBOutlet weak var numberField02: MyCustomTextField!
override func viewDidLoad() {
super.viewDidLoad()
numberField01.delegate = self
numberField01.keyboardType = UIKeyboardType.NumberPad
numberField02.delegate = self
numberField02.keyboardType = UIKeyboardType.NumberPad
}
class MyCustomTextField: UITextField {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.borderColor = UIColor.redColor().CGColor
self.layer.borderWidth = 1.5
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String)
-> Bool {
var result = true
var prospectiveText = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
prospectiveText = prospectiveText.stringByReplacingOccurrencesOfString(".", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
if textField == numberField01 {
if count(string)>0 {
let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
let resultingStringLengthIsLegal = count(prospectiveText) <= 4
let scanner = NSScanner(string: prospectiveText)
let resultingTextIsNumeric = scanner.scanDecimal(nil) && scanner.atEnd
result = replacementStringIsLegal && resultingStringLengthIsLegal && resultingTextIsNumeric
}
}
return result
}
}
}
Original question:
The following code is working fine for one textfield (numberField01). It makes sure the input is decimal only, places a decimal point, and prevents a user to paste in a non decimal string. But I have a lot more buttons... (numberField02 and up). How can I handle more buttons, without just copying my code for each button?
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var numberField01: UITextField!
#IBOutlet weak var numberField02: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
numberField01.delegate = self
numberField01.keyboardType = UIKeyboardType.NumberPad
numberField02.delegate = self
numberField02.keyboardType = UIKeyboardType.NumberPad
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// Tap background to add decimal point and defocus keyboard
#IBAction func userTappedBackground(sender: AnyObject) {
for view in self.view.subviews as! [UIView] {
if let textField = view as? UITextField {
if count(numberField01.text) > 0 {
var numberString = numberField01.text
numberString = numberString.stringByReplacingOccurrencesOfString(".", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
var numberFromString = Double(numberString.toInt()!) / 100
numberField01.text = String(format:"%.2f", numberFromString)
}
textField.resignFirstResponder()
}
}
}
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String)
-> Bool {
var result = true
var prospectiveText = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
prospectiveText = prospectiveText.stringByReplacingOccurrencesOfString(".", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
if textField == numberField01 {
if count(string)>0 {
let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
let resultingStringLengthIsLegal = count(prospectiveText) <= 4
let scanner = NSScanner(string: prospectiveText)
let resultingTextIsNumeric = scanner.scanDecimal(nil) && scanner.atEnd
result = replacementStringIsLegal && resultingStringLengthIsLegal && resultingTextIsNumeric
}
}
return result
}
}

So something similar to the following:
Button GetButtonCommonFeatures(Button myButton)
{
Write common code here....
e.g. myButton.delegate = self;...
return myButton;
}
Then call your method for each button. Lets take numberField01 for example. You will include the code in the method that applies to every button.
numberField01 = GetButtonCommonFeatures(numberField01);
Hope this helps

Related

NSAttributedString not changing colour of certain text of NSTextview

I’m using NSAttributedString to change the text color of my NSTextview. When I want to change the color of a word, the color will not show. Here is my code for more detail
let main_string = “Hello World"
let string_to_color = “World"
let range = (main_string as NSString).range(of: string_to_color)
textView.textStorage?.addAttribute(NSAttributedString.Key.foregroundColor, value: NSColor.orange, range: range);
When I want the change the text Colour of another string,
let main_string = "Hello World"
let string_to_color = "World"
let range = (main_string as NSString).range(of: string_to_color)
textView.textStorage?.addAttribute(NSAttributedString.Key.foregroundColor, value: NSColor.orange, range: range)
let jhgfds = "Lorem Ipsum"
let tdftgdg = "ipsum"
let raresrgsdnge = (jhgfds as NSString).range(of: tdftgdg)
textView.textStorage?.addAttribute(NSAttributedString.Key.foregroundColor, value: NSColor.orange, range: range)
This is what happens
Can you please help me with this
Here is my full code…
import Cocoa
import WebKit
class ViewController: NSViewController, NSTextViewDelegate {
struct Keys {
static let noteBook = "noteTaking"
static let title = "appTitle"
}
#IBOutlet weak var livePreviewOn: NSButton!
var isPaused = true
var timer = Timer()
#IBOutlet var textView: NSTextView!
let defaults = UserDefaults.standard
#IBOutlet weak var webView: WKWebView!
#IBOutlet weak var scrollView: NSScrollView!
/// - Tag: setRepresentedObjectExample
override var representedObject: Any? {
didSet {
// Pass down the represented object to all of the child view controllers.
for child in children {
child.representedObject = representedObject
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
scrollView.magnification = 1.5
textView.isAutomaticQuoteSubstitutionEnabled = false
checkSavedText()
Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(self.updater), userInfo: nil, repeats: true)
}
#IBAction func pauseResume(sender: AnyObject) {
if isPaused{
timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
isPaused = false
} else {
timer.invalidate()
isPaused = true
}
}
#IBAction func saveBTN(_ sender: Any) {
saveNoteBookText()
}
func saveNoteBookText() {
defaults.set(textView.string, forKey: Keys.noteBook)
}
func checkSavedText() {
let name = defaults.value(forKey: Keys.noteBook) as? String ?? ""
textView.string = name
}
#objc func update() {
saveBTN(self)
webView.loadHTMLString(textView.string, baseURL: nil)
}
#objc func updater() {
saveBTN(self)
let main_string = "Hello World"
let string_to_color = "World"
let range = (textView.textStorage!.string as NSString).range(of: string_to_color)
textView.textStorage?.addAttribute(NSAttributedString.Key.foregroundColor, value: NSColor.orange, range: range)
}
}

Add or subtract numbers from different viewports in Swift4

I am new here and would like to ask a question that has been working for me for days. I'm just learning Swift 4 and I've come quite a long way. I really do not know what to do any more, and my books on swift do not help me either.
I have created a small testapp, in which should simply be charged.
There are 5 view controllers. The first one has 4 buttons to get to one of the other 4 and to enter a number there in a text box. This number is then output in the first viewcontroller in a label. The numbers are displayed and even the last entered number is displayed again after a restart of the app.
But now I want to charge off the numbers in the first viewcontroller. How can I fix the code?
My Viewports:
my viewports
code from main viewport:
import UIKit
class ViewController: UIViewController, sendValue1, sendValue2, sendValue3, sendValue4 {
#IBOutlet weak var value1: UILabel!
#IBOutlet weak var value2: UILabel!
#IBOutlet weak var value3: UILabel!
#IBOutlet weak var value4: UILabel!
#IBOutlet weak var calculatedValue1: UILabel! // here i want to see the calculated value like from the label 1-4...value1 + value2 + value3 + value4 = ???
#IBOutlet weak var calculatedValue2: UILabel! // here the same like in claculatedValue1 value but with "-" or "*" or something else...
func value1Data(data: String) {
value1.text = data
UserDefaults.standard.set(value1.text, forKey: "value1")
}
func value2Data(data: String) {
value2.text = data
UserDefaults.standard.set(value2.text, forKey: "value2")
}
func value3Data(data: String) {
value3.text = data
UserDefaults.standard.set(value3.text, forKey: "value3")
}
func value4Data(data: String) {
value4.text = data
UserDefaults.standard.set(value4.text, forKey: "value4")
}
override func viewDidAppear(_ animated: Bool) {
if let lastValue1Data = UserDefaults.standard.object(forKey: "value1") as? String {
value1.text = lastValue1Data
}
if let lastValue2Data = UserDefaults.standard.object(forKey: "value2") as? String {
value2.text = lastValue2Data
}
if let lastValue3Data = UserDefaults.standard.object(forKey: "value3") as? String {
value3.text = lastValue3Data
}
if let LastValue4Data = UserDefaults.standard.object(forKey: "value4") as? String {
value4.text = LastValue4Data
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "VC1" {
let SendingVC1: Value1ViewController = segue.destination as! Value1ViewController
SendingVC1.delegate = self
}
if segue.identifier == "VC2" {
let SendingVC2: Value2ViewController = segue.destination as! Value2ViewController
SendingVC2.delegate = self
}
if segue.identifier == "VC3" {
let SendingVC3: Value3ViewController = segue.destination as! Value3ViewController
SendingVC3.delegate = self
}
if segue.identifier == "VC4" {
let SendingVC4: Value4ViewController = segue.destination as! Value4ViewController
SendingVC4.delegate = self
}
}
#IBAction func unwindToView1(_ segue: UIStoryboardSegue) {
}
and the code from one of the other four:
import UIKit
protocol sendValue1 {
func value1Data(data: String)
}
class Value1ViewController: UIViewController {
var delegate: sendValue1? = nil
#IBOutlet weak var textValue1: UITextField!
#IBAction func done(_ sender: Any) {
if delegate != nil {
if textValue1.text != nil {
let data = textValue1.text
delegate?.value1Data(data: data!)
dismiss(animated: true, completion: nil)
}
}
}
why is the result always nil here?
let a = Float(value3.text!) ?? 0
let b = Float(value4.text!) ?? 0
let SUM = a + b
calculatedValue1.text = "\(SUM)" + "m"
No matter what I do, the numbers are not processed ...

How can I get a number input from a TextField?

I will get right to the question.
var a = 0
var b = 20
I want a user to input number into TextField and I could save that number into variable A. Then I want to do an if statement where
if a == b {
//code
}
What I am having trouble is getting that number input from the textfield.
You can check the input in the textfield for get the number
Swift4
let text = textField.text ?? ""
guard let number = Int(text) else {
print("Must be input the number")
return
}
// Do your task with number
a = number
Try this class Functions
class SourceVC: UIViewController
{
#IBOutlet weak var sampleTF: UITextField!{
didSet{
sampleTF.delegate = self
}
}
/// TF value
var a : Int = 0
var b : Int = 20
override func viewDidLoad() {
super.viewDidLoad()
}
/// Assuming this button action to get Value from TF
#IBAction func naviagteToDestination(_ sender: Any)
{
a = Int((sampleTF.text?.trimmingCharacters(in: .whitespaces))!)!
if a == b {
print("Execute")
}
else{
print("Dont Execute")
}
}
}
extension SourceVC : UITextFieldDelegate
{
/// This will Limit TF to accept only Numbers
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
}

why do interactive parts of UITextview require a long press?

I need to make part of a text interactive, this is a screenshot of what I made so far, which is good, but the problems are
1- it requires a long tap
2- and force me to have the text selectable
any idea about how to solve this?
import UIKit
class ViewController: UIViewController, UITextViewDelegate
{
#IBOutlet weak var textView: UITextView!
override func viewDidLoad()
{
super.viewDidLoad()
let attributedString = NSMutableAttributedString(string: "text with a link", attributes: nil)
attributedString.setSubstringAsLink(substring: "link", linkURL: "CUSTOM://WHATEVER")
let linkAttributes: [String : AnyObject] = [NSForegroundColorAttributeName : UIColor.redColor(), NSUnderlineColorAttributeName : UIColor.redColor(), NSUnderlineStyleAttributeName : NSUnderlineStyle.StyleSingle.rawValue]
textView.linkTextAttributes = linkAttributes
textView.attributedText = attributedString
textView.selectable = true
textView.editable = false
textView.userInteractionEnabled = true
textView.delegate = self
}
func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool
{
if URL == "CUSTOM://WHATEVER"
{
print("????")
}
else
{
print("!!!!")
}
return true
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension NSMutableAttributedString
{
public func setSubstringAsLink(substring substring: String, linkURL: String) -> Bool
{
let range = self.mutableString.rangeOfString(substring)
if range.location != NSNotFound
{
self.addAttribute(NSLinkAttributeName, value: linkURL, range: range)
return true
}
return false
}
}
I don't know how to solve your specific issues but I guess you are better of subclassing UILabel instead of UITextView. Also, try to not re-invent the wheel, use TTAttributedLabel or similar libraries to do what you want.
I resolved this problem like below,
let attributedString = NSMutableAttributedString(string: "Press this link text")
attributedString.addAttribute(NSLinkAttributeName, value: "http://www.google.com", range: NSRange(location: 6, length: 14))
let linkAttributes = [
NSForegroundColorAttributeName: UIColor.greyishBrownColor(),
NSUnderlineColorAttributeName: UIColor.greyishBrownColor(),
NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue
]
agreementsTextView.delegate = self
agreementsTextView.attributedText = attributedString
agreementsTextView.linkTextAttributes = linkAttributes
// Delegate Method
func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
if URL.absoluteString == "http://www.google.com" || URL.relativePath == "http://www.google.com" {
print("Tapped")
}
return false
}
Range might be wrong, this should fix your problem

Digit input in a text field

Additional question:
Still need some help with my code. The textfield is 'measuredValue' and I plan to have 30 different texfields (measuredValue1...30). When I type '923' the text will be set to '9.23' right away. Then I want to add '4'... for '92.34' but that doesn't work. Thanks for helping out.
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String)
-> Bool {
if count(string) == 0 { return true }
var measuredValue = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
switch textField {
case digitsOnlyTextField:
if count(measuredValue) == 3 || count(measuredValue) == 4 {
let stringNumeric = Double(measuredValue.toInt()!) / 100
measuredValue = String(format:"%.2f", stringNumeric)
digitsOnlyTextField.text = measuredValue
}
return measuredValue.containsOnlyCharactersIn("0123456789") && count(measuredValue) <= 4
default:
return true
}
}
Original question:
I would like to validate my text fields to get the right input for my app. Input needs to be formatted like '9.90' or '15.34'. So always 3 or 4 digits, and always 2 decimals.
I would like to use 'numberpad keyboard' (just 0...9, no point) and add the decimal point after the user exits the field. So the user input is 990 or 1534, and then in the text field it will become 9.90 or 15.34 automatically.
I tried searching for examples first, but didn't find what I was looking for.
Any help appreciated.
Jan
You have to implement the UITextField delegate method
func textFieldDidEndEditing(textField: UITextField) {
//Logic goes here....
var textString = textField.text as! String
let textLength = countElements(textString)
if textLength >= 3 && textLength <= 4 {
let stringNumeric = Double(textString.toInt()!) / 100
let texts = String(format:"%.2f", stringNumeric)
textField.text = texts
}
}
Your class should confirm to the protocol UITextFieldDelegate
this is the 'final' code I used (but I'm open for improvements!). A few remarks:
when a user clicks the background (and leaves the textfield) the
decimal point gets set.
when a user copies & pastes text from another app, this code handles that.
when a user goes back in the textfield for editing, the decimal point gets set again.
Deleting a value (to an empty textfield) is handled correctly.
So I'm pleased. Thanks for the help.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var numberField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
numberField.delegate = self
numberField.keyboardType = UIKeyboardType.NumberPad
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// Tap background to add decimal point and defocus keyboard
#IBAction func userTappedBackground(sender: AnyObject) {
for view in self.view.subviews as! [UIView] {
if let textField = view as? UITextField {
if count(numberField.text) > 0 {
var numberString = numberField.text
numberString = numberString.stringByReplacingOccurrencesOfString(".", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
var numberFromString = Double(numberString.toInt()!) / 100
numberField.text = String(format:"%.2f", numberFromString)
}
textField.resignFirstResponder()
}
}
}
func textField(textField: UITextField,
shouldChangeCharactersInRange range: NSRange,
replacementString string: String)
-> Bool {
var result = true
var prospectiveText = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
prospectiveText = prospectiveText.stringByReplacingOccurrencesOfString(".", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
if textField == numberField {
if count(string)>0 {
let disallowedCharacterSet = NSCharacterSet(charactersInString: "0123456789").invertedSet
let replacementStringIsLegal = string.rangeOfCharacterFromSet(disallowedCharacterSet) == nil
let resultingStringLengthIsLegal = count(prospectiveText) <= 4
let scanner = NSScanner(string: prospectiveText)
let resultingTextIsNumeric = scanner.scanDecimal(nil) && scanner.atEnd
result = replacementStringIsLegal && resultingStringLengthIsLegal && resultingTextIsNumeric
}
}
return result
}