Make Clickable UILabel Using Swift - ios

I want to Set Particular Word clickable in UILabel text using Swift.
Is it possible?
If more than one label is here how can I detect which word is pressed?

You can not do with the simple label.
There is library available in the github.
https://github.com/TTTAttributedLabel/TTTAttributedLabel
From this you can use the method called yourLabel.addLinkToURL()
class ViewController: UIViewController , TTTAttributedLabelDelegate{
#IBOutlet var lbl: TTTAttributedLabel!
override func viewDidLoad() {
super.viewDidLoad()
var str : NSString = "Hello this is link"
lbl.delegate = self
lbl.text = str as String
var range : NSRange = str.rangeOfString("link")
lbl.addLinkToURL(NSURL(string: "http://github.com/mattt/")!, withRange: range)
}
func attributedLabel(label: TTTAttributedLabel!, didSelectLinkWithURL url: NSURL!) {
UIApplication.sharedApplication().openURL(url)
}
}

SWIFT 3.0
privacyLabel.delegate = self
let strPolicy : NSString = "Agree to the Terms & Conditions"
privacyLabel.text = strPolicy as String
let range1 : NSRange = strPolicy.range(of: "Terms & Conditions")
privacyLabel.addLink(to: URL(string: "http://Terms.com")!, with: range1)
func attributedLabel(_ label: TTTAttributedLabel!, didSelectLinkWith url: URL!) {
print("url \(url)")
// UIApplication.sharedApplication().openURL(url)
}

I'd like to share my library https://github.com/psharanda/Atributika
It contains modern replacement of TTTAtributedLabel + powerful set of methods to detect and style different stuff like tags, hashtags, mentions etc (everything of that can be clickable)
Some code to show how it works:
let link = Style
.font(.boldSystemFont(ofSize: 14))
.foregroundColor(.black)
.foregroundColor(.red, .highlighted)
let tos = link.named("tos")
let pp = link.named("pp")
let all = Style
.font(.systemFont(ofSize: 14))
.foregroundColor(.gray)
let text = "<tos>Terms of Service</tos> and <pp>Privacy Policy</pp>"
.style(tags: tos, pp)
.styleAll(all)
let tosLabel = AttributedLabel()
tosLabel.textAlignment = .center
tosLabel.attributedText = text
tosLabel.onClick = { label, detection in
switch detection.type {
case .tag(let tag):
switch tag.name {
case "pp":
print("Privacy Policy clicked")
case "tos":
print("Terms of Service clicked")
default:
break
}
default:
break
}
}
view.addSubview(tosLabel)

Related

Hangman Game letters reset each time the button is pressed [duplicate]

This question already exists:
I am making a HangMan game and I'm having problems with letters being doubled each time it loops [closed]
Closed last month.
So I've been creating a hangman game and been having problems with each time a user guesses a correct letter again, the previous correct letter/letters gets removed.??
I tried a lot of if elses to hold each index of the letter and append if its correct but that did not work. So in general let's say a user got their first letter correct which was A. This is what happens..
XXXAXXAX
But the Second letter they get correct, lets say B this happens...
XBXXXXXX
they are supposed to bring A along to the next iteration and so onn.
can anyone see what's the problem? Here's my code below..
class ViewController: UIViewController {
var allWords = [String]()
var usedLetters = [String]()
var startWords = [String]()
var promptWord = String()
var randomWord = ""
override func viewDidLoad() {
super.viewDidLoad()
EnterGuess()
fileWork()
print(randomWord)
title = "GUESS A Letter: ?????????)"
}
func fileWork() {
if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt") {
if let startWords = try? String(contentsOf: startWordsURL) {
allWords = startWords.components(separatedBy: "\n")
let wordssss = startWords.randomElement()
randomWord = allWords.randomElement()!
}
}
}
func EnterGuess() {
let ac = UIAlertController(title: "Guess a letter", message: nil, preferredStyle: .alert)
ac.addTextField()
var submitGuess = UIAlertAction(title: "Submit", style: .default) {_ in
guard var answer = ac.textFields?[0].text else {return }
answer
self.submit(answer)
self.EnterGuess()
}
ac.addAction(submitGuess)
present(ac, animated: true)
}
func submit(_ answer: String) {
let guessedLetter = answer
let wordArray = randomWord.map(String.init)
var hidden = Array(repeating: "x", count: randomWord.count)
for index in hidden.indices {
if wordArray[index] == answer {
hidden[index] = wordArray[index]
}
}
print(hidden.joined())
// print(wordArray)
title = String(describing: hidden.joined())
}
}
The problem is that you are "re-setting" the hidden string with each letter guess.
Instead, you want to create a class-level var:
var hidden = [String]()
and "re-set" it to "xxxxxx..." when you start a new game.
Then, with each guess, you'll be keeping the old letters and replacing new letters.
Suppose the game word is EASY:
hidden is "xxxx"
guessed letter is "A"
hidden goes from "xxxx" to "xAxx"
guessed letter is "Y"
hidden goes from "xAxx" to "xAxY"
guessed letter is "E"
hidden goes from "xAxY" to "EAxY"
and so on.
Here's a quick example you can run:
class HangmanViewController: UIViewController, UITextFieldDelegate {
var allWords: [String] = [
"easy",
"birthday",
"laboratory",
"hangman",
"iphone",
"requirements",
"gaming",
]
var usedLetters = [String]()
var startWords = [String]()
var promptWord = String()
var randomWord = ""
var hidden = [String]()
let tf = UITextField()
let progressLabel = UILabel()
let usedLabel = UILabel()
let newGameButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
tf.textAlignment = .center
tf.borderStyle = .roundedRect
tf.textAlignment = .center
tf.font = .monospacedSystemFont(ofSize: 24.0, weight: .light)
tf.delegate = self
progressLabel.textAlignment = .center
progressLabel.font = .monospacedSystemFont(ofSize: 24.0, weight: .light)
progressLabel.backgroundColor = .yellow
usedLabel.textAlignment = .center
usedLabel.font = .monospacedSystemFont(ofSize: 16.0, weight: .light)
usedLabel.backgroundColor = .cyan
newGameButton.setTitle("New Game", for: [])
newGameButton.setTitleColor(.white, for: .normal)
newGameButton.setTitleColor(.lightGray, for: .highlighted)
newGameButton.backgroundColor = .systemBlue
newGameButton.layer.cornerRadius = 8
newGameButton.layer.borderColor = UIColor.blue.cgColor
newGameButton.layer.borderWidth = 1
[tf, progressLabel, usedLabel, newGameButton].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tf.widthAnchor.constraint(equalToConstant: 80.0),
tf.topAnchor.constraint(equalTo: g.topAnchor, constant: 80.0),
tf.centerXAnchor.constraint(equalTo: g.centerXAnchor),
progressLabel.widthAnchor.constraint(equalToConstant: 300.0),
progressLabel.topAnchor.constraint(equalTo: tf.bottomAnchor, constant: 40.0),
progressLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
usedLabel.widthAnchor.constraint(equalToConstant: 300.0),
usedLabel.topAnchor.constraint(equalTo: progressLabel.bottomAnchor, constant: 40.0),
usedLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
newGameButton.widthAnchor.constraint(equalToConstant: 200.0),
newGameButton.topAnchor.constraint(equalTo: usedLabel.bottomAnchor, constant: 40.0),
newGameButton.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
newGameButton.addTarget(self, action: #selector(newGameTapped(_:)), for: .touchUpInside)
newGame()
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// upper-case the entered letter
let s = string.uppercased()
// we don't want to process if a string was pasted into the field
if s.count != 1 {
return false
}
// only allow A - Z
if s.rangeOfCharacter(from: .uppercaseLetters) == nil {
return false
}
// replace the current text
textField.text = s
// process the entered letter
submit(s)
// don't let the textfield process the string
return false
}
func newGame() {
// cycle the array of game words
allWords.append(allWords.removeFirst())
// safely unwrap
guard let w = allWords.first else {
fatalError("Bad setup")
}
// upper-case the word
randomWord = w.uppercased()
// set hidden string to "####..."
hidden = Array(repeating: "#", count: randomWord.count)
// clear used letters
usedLetters = []
// update the game progress label
progressLabel.text = String(hidden.joined())
progressLabel.textColor = .black
// update used letters label
usedLabel.text = " "
// hide the new game button
newGameButton.isHidden = true
// re-enable text field
tf.isUserInteractionEnabled = true
// for development
print("New Game Word is:", randomWord)
}
func submit(_ answer: String) {
if usedLetters.contains(answer) {
return
}
usedLetters.append(answer)
usedLabel.text = usedLetters.joined()
let wordArray = randomWord.map(String.init)
for index in hidden.indices {
if wordArray[index] == answer {
hidden[index] = wordArray[index]
}
}
progressLabel.text = hidden.joined()
if hidden == wordArray {
progressLabel.textColor = .red
// clear and disable text field
tf.text = ""
tf.isUserInteractionEnabled = false
// dismiss keyboard
view.endEditing(true)
// show the new game button
newGameButton.isHidden = false
}
}
#objc func newGameTapped(_ sender: UIButton) {
newGame()
}
}
It will look about like this (the cyan label will show the used letters). I'll type T, R, Y, S, M, E, A:

How to add two numbers in Swift only when both of the text fields are filled in

I'm trying to add two numbers in Swift 5, and I want to add some error checks. I don't want to make it possible for a user to click on the plus button if both of the text fields are not filled in. I tried with the if state below but it did not work.
the whole function:
#IBAction func sum(_ sender: Any) {
let one = input1.text
let oneInt = Int(one!)
let two = input2.text
let twoInt = Int(two!)
let total = oneInt! + twoInt!
label.text = "\(total)"
if(input2.text == nil){
addBtn.isEnabled = false
}
if(input1.text == nil){
addBtn.isEnabled = false
}
}
Try to use guard like this. If your input field does not contain any value that field return blank string and when you try to get integer value from that string it will return nil and your add button will be disable.
#IBAction func sum(_ sender: Any) {
guard let text1 = input1.text, let intValue1 = Int(text1) else {
addBtn.isEnabled = false
return
}
guard let text2 = input2.text, let intValue2 = Int(text2) else {
addBtn.isEnabled = false
return
}
label.text = "\(intValue1 + intValue2)"
}
A nice and simple way is to addTarget to your textFiels. This will enable you to handle the events on the text field. In this scenario we'll use .editingChanged and use a single selector to achieve our goal:
What we'll do : We will listen for when someone types something in the textfield. Whenever a text changed was made, we'll check to see if all the textfields was populated and then if it was we enable the sum button.
A small controller sample :: Make sure to read the comments to understand the code faster
import UIKit
class ViewController: UIViewController {
#IBOutlet var textfield1: UITextField!
#IBOutlet var textfield2: UITextField!
#IBOutlet var sumButton: UIButton!
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
sumButton.isEnabled = false /// Disable the button first thing
[textfield1, textfield2].forEach {
$0.addTarget(self, action: #selector(editingChanged(_:)), for: .editingChanged) /// add targets to handle the events (in your case it listens for the 'editingChanged' event )
}
}
#objc func editingChanged(_ textField: UITextField) {
/// Here we just loop through all our textfields
for each in [textfield1, textfield2] {
if let text = each?.text { /// Just make sure the textfields text is not nil
if text.count < 1 {
// If the textfiels text has no value in, then we keep the button disabled and return
sumButton.isEnabled = false
return
}
} else {
/// Else if the text field's text is nill, then return and keep the button disabled
sumButton.isEnabled = false
return
}
}
sumButton.isEnabled = true /// If the code reaches this point, it means the textfields passed all out checks and the button can be enabled
}
#IBAction func sum(_ sender: Any) {
let one = textfield1.text!
let two = textfield2.text!
guard let oneInt = Int(one), let twoInt = Int(two) else {
print("Whatever was in that text fields, couldn't be converted to an Int")
label.text = "Be sure to add numbers."
return
}
let total = oneInt + twoInt
label.text = "\(total)"
}
}
textfields are not nil but empty strings. so make your comparison like :
if input1.text == "" {
// do your check here
}
Seems like you want to start with addBtn.isEnabled = false then update it whenever the user enters two valid integers into the text fields, i.e. Int(input1.text ?? "") != nil && Int(input2.text ?? "") != nil. You can do this by adding a target to your textfields (input1 and input2) for .editingChanged events. For example, if you're doing this in a UIViewController, you can do this in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
addBtn.isEnabled = false
input1.addTarget(self, action: #selector(textFieldDidEdit(_:)), for: .editingChanged)
input2.addTarget(self, action: #selector(textFieldDidEdit(_:)), for: .editingChanged)
}
Where textFieldDidEdit(_:) action looks like:
#objc func textFieldDidEdit(_ sender: UITextField) {
addBtn.isEnabled = Int(input1.text ?? "") != nil && Int(input2.text ?? "") != nil
}
Finally your sum function becomes:
#IBAction func sum(_ sender: UIButton) {
guard let oneInt = Int(input1.text ?? ""), let twoInt = Int(input2.text ?? "") else {
return
}
let total = oneInt + twoInt
label.text = "\(total)"
}
As all of the number validation has moved to the textFieldDidEdit(_:) function.

Pictures in the label (Swift)

I have code to display the history in the calculator but the signs (+, -, ×, ÷) are taken from the "case" (Photo 1)
How can I make it so that in the history the signs (+, -, ×, ÷) are displayed by the pictures I have set (Photo 2)
#IBAction func equalitySignPressed(sender: UIButton) {
if stillTyping {
secondOperand = currentInput
}
dotIsPlaced = false
addHistory(text: operationSign + displayResultLabel.text!)
switch operationSign {
case "+":
operateWithTwoOperands{$0 + $1}
case "-":
operateWithTwoOperands{$0 - $1}
case "×":
operateWithTwoOperands{$0 * $1}
case "÷":
operateWithTwoOperands{$0 / $1}
default: break
}
}
History:
func addHistory(text: String){
//Add text
resultLabelText.text = resultLabelText.text! + "" + text
}
You can make your symbols images and use NSTextAttachment to construct a NSAttributedAtring that replaces the text in your string with the corresponding NSTextAttachment with your symbol image. Here is an example playground that does it with one image, but you can easily add more images to the dictionary to replace all of the other symbols with images:
import PlaygroundSupport
import UIKit
class V: UIViewController {
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(label)
label.textColor = .red
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let expression = "5 + 5"
let plusAttachment = NSTextAttachment()
plusAttachment.image = UIImage(named: "star.png")
let plusString = NSAttributedString(attachment: plusAttachment)
let substitutions: [Character: NSAttributedString] = ["+": plusString]
let attributedExpression = NSMutableAttributedString()
for character in expression {
if let substitution = substitutions[character] {
attributedExpression.append(substitution)
} else {
attributedExpression.append(NSAttributedString(string: String(character)))
}
}
label.attributedText = attributedExpression
label.sizeToFit()
}
}
PlaygroundPage.current.liveView = V()
I suggest you get an emoji font that displays mathematical signs with a border around them. Use that font to create NSAttributedStrings and set it as the label's attributedText.
As to how to use custom fonts, you can refer to here or just search on SO. There are lots of questions about this topic.
I also see that you want the text to be bold, that can be done with attributed strings as well.
Alternatively, you can add those cool math signs as attachments to NSAttributedStrings, but I doubt it's easy to get the sizes correct.

iOS: Set setImageInputs of image slideshow to array of images

I'm using an image slideshow from here:
iconArr = [UIImage(named: "home-min")!,UIImage(named: "category-
min")!,UIImage(named: "settings-min")!,UIImage(named: "contact us-min")!,UIImage(named: "about us-min")!,UIImage(named: "logout")!]
I need to make this array as an image source.
for image in self.iconArr {
let img = image
self.SlideShow.setImageInputs([ImageSource(image: img)])
}
But that is not working, how can I do that?
you should try this way for sure, because you reset inputs in your for-loop
var imageSource: [ImageSource] = []
for image in self.iconArr {
let img = image
imageSource.append(ImageSource(image: img))
}
self.SlideShow.setImageInputs(imageSource)
As sooper stated, can be done this way
let imageSources = self.iconArr.map { ImageSource(image: $0) }
I found a solution from this url [https://stackoverflow.com/a/50461970/5628693][1]
Below is my code working fine :
var imageSDWebImageSrc = [SDWebImageSource]()
#IBOutlet weak var slideshow: ImageSlideshow!
Add below viewDidLoad()
slideshow.backgroundColor = UIColor.white
slideshow.slideshowInterval = 5.0
slideshow.pageControlPosition = PageControlPosition.underScrollView
slideshow.pageControl.currentPageIndicatorTintColor = UIColor.lightGray
slideshow.pageControl.pageIndicatorTintColor = UIColor.black
slideshow.contentScaleMode = UIViewContentMode.scaleAspectFill
// optional way to show activity indicator during image load (skipping the line will show no activity indicator)
slideshow.activityIndicator = DefaultActivityIndicator()
slideshow.currentPageChanged = {
page in
print("current page:", page)
}
let recognizer = UITapGestureRecognizer(target: self, action: #selector(Dashboard.didTap))
slideshow.addGestureRecognizer(recognizer)
} // now add below func
#objc func didTap() {
let fullScreenController = slideshow.presentFullScreenController(from: self)
// set the activity indicator for full screen controller (skipping the line will show no activity indicator)
fullScreenController.slideshow.activityIndicator = DefaultActivityIndicator(style: .white, color: nil)
}
And last step i was getting json data from below alamofire request
Alamofire.request(url, method: .post, parameters: data, encoding: JSONEncoding.default).responseJSON { response in
if(response.value == nil){
}
else {
let json2 = JSON(response.value!)
switch response.result {
case .success:
self.indicator.stopAnimating()
if let details = json2["imgs"].array {
for dItem in details {
let img = dItem["img"].stringValue
let image = SDWebImageSource(urlString: self.imgurl+img)
self.imageSDWebImageSrc.append(image!)
}
self.slideshow.setImageInputs(self.imageSDWebImageSrc)
}
break
case .failure( _):
break
}
}
}
Thanks dude :) happy coding

why does Bundle.main.path(forResource: fileName, ofType: "txt") always return nil?

I want to read a text file line by line and display it on an iOS screen using the example shown here.
Making textView.text optional was the only way I could get readDataFromFile to run. When I click load the function runs but always returns nil. I assume this means the file is not found.
For testing purposes I created the text file in Xcode. I also tried saving it on the desktop as well as in the project folder. Either way it was readable from the project navigator. I also tried creating the file using TextEdit because the app ultimately needs to read text files created outside Xcode.
I’d be grateful if someone can explain why the text file is never found, whether there is something else I need to do in order for the project to find it or if the read function returns nil for some other reason due to the way I have implemented it. Thanks.
EDIT (2)
Thanks for the feedback. In response, I’ve made four minor code changes that allow the text file contents to be written to textView. Changes include: removing the file extension from the filename, adding an array of file names, returning String instead of String? from readDataFromFile and rewriting UITextView in code. This has solved problems I am aware of.
Here's the revised code
import UIKit
class ViewController: UIViewController {
var textView = UITextView()
var arrayOfStrings: [String]?
var fileNameWithExtension = "textFile.txt"
let arrayOfFileNames = ["textFile1.txt", "textFile2.txt", "textFile3.txt", "textFile4.txt", "textFile5.txt"]
var fileName = String()
override func viewDidLoad() {
super.viewDidLoad()
// remove comment in the next statement to test files named in ArrayOfFileNames
// fileNameWithExtension = arrayOfFileNames[4]
fileName = fileNameWithExtension.replacingOccurrences(of: ".txt", with: "")
createTextView()
createButton()
}
func readDataFromFile(fileName: String) -> String {
if let path = Bundle.main.path(forResource: fileName, ofType: nil) {
print(fileName)
do {
let data = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
arrayOfStrings = data.components(separatedBy: .newlines)
textView.text = arrayOfStrings?.joined(separator: "\n")
} catch {
textView.text = "file contents could not be loaded"
}
} else {
print(Bundle.main.path(forResource: fileName, ofType: "txt") as Any)
textView.text = "\(fileName) could not be found"
}
return textView.text
}
func createButton () {
let button = UIButton();
button.setTitle(String("Load"), for: .normal)
button.setTitleColor(UIColor.blue, for: .normal)
button.frame = CGRect(x: 100, y: 10, width: 200, height: 100)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
}
func buttonAction(myButton: UIButton) {
textView.text = readDataFromFile(fileName: fileName)
print(textView.text as Any)
}
func createTextView () {
textView = UITextView(frame: CGRect(x: 20.0, y: 75.0, width: 340.0, height: 400.0))
textView.textAlignment = NSTextAlignment.left
textView.textColor = UIColor.blue
textView.backgroundColor = UIColor.white
self.view.addSubview(textView)
}
}
EDIT (1)
The file is visible in the project navigator. I will assume that means it is in the bundle.
Here is my original code
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textView: UITextView?
var arrayOfStrings: [String]?
var fileName = "textFile.txt"
override func viewDidLoad() {
super.viewDidLoad()
createButton()
}
func readDataFromFile(fileName: String) -> String? {
if let path = Bundle.main.path(forResource: fileName, ofType: "txt") {
print(fileName)
do {
let data = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
arrayOfStrings = data.components(separatedBy: .newlines)
print(arrayOfStrings as Any)
textView?.text = arrayOfStrings?.joined(separator: "/n")
return textView?.text
} catch {
textView?.text = "file contents could not be loaded"
return textView?.text
}
} else {
print(Bundle.main.path(forResource: fileName, ofType: "txt") as Any)
textView?.text = "\(fileName) could not be found"
return nil
}
}
func createButton () {
let button = UIButton();
button.setTitle(String("Load"), for: .normal)
button.setTitleColor(UIColor.blue, for: .normal)
button.frame = CGRect(x: 100, y: 15, width: 200, height: 100)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
self.view.addSubview(button)
}
func buttonAction(myButton: UIButton) {
print("works")
textView?.text = readDataFromFile(fileName: fileName)
print(textView?.text as Any)
}
textFile.txt
Line 1
Line 2
Line 3
Line 4
Line 5
1) You have mistake in this line:
var fileName = "textFile.txt"
should be:
var fileName = "textFile"
2) Check is your file connected to target:
You should consider adding class bundle owner like this:
Bundle(for: ViewController.self).path(forResource: "fileName", ofType: "txt")
This was implemented from swift 2.0 if I was right.

Resources