"See Less" in ExpandableLabel iOS - ios

I'm using the third party library ExpandableLabel to implement a see more feature. I am looking for swift only solutions that include the text in the label rather than in the button so this works perfectly. After adding the library and changing label type in IB I only need a few lines of code :
#IBOutlet weak var myLabel: ExpandableLabel!
myLabel = 3
myLabel = true
I can't however figure out how to implement "see less" after it has been expanded fully. I added the delegate method :
ExpandableLabelDelegate
and functions:
// MARK: ExpandableLabel Delegate
func willExpandLabel(_ label: ExpandableLabel) {
}
func didExpandLabel(_ label: ExpandableLabel) {
}
func willCollapseLabel(_ label: ExpandableLabel) {
}
func didCollapseLabel(_ label: ExpandableLabel) {
}
func shouldCollapseLabel(_ label: ExpandableLabel) -> Bool {
return true
}
to try and gain control of the process but have still struggled. Has anyone else managed to get this right? If so please can you help me out here...

Based on my personal experience, I noticed that the expandedAttributedLink works only if you set it before the actual label text.
infoLabel.setLessLinkWith(lessLink: "less", attributes: [NSFontAttributeName: boldItalicFont], position: nil)
infoLabel.text = viewModel.description
infoLabel.collapsedAttributedLink = NSAttributedString(string: "more", attributes: [NSFontAttributeName: boldItalicFont]);
infoLabel.ellipsis = NSAttributedString(string: "...")
infoLabel.collapsed = true
I was able to identify this behaviour by looking at the setter of the text property inside the source file.

For Show Less Implementation.
Use 2 Properties of expandable label.
yourLabel.collapsed = true
yourLabel.shouldCollapse = true

Related

How to set different font size for UIButton when it is pressed

I am trying to change font size of button itself after it was pressed.
class ViewController: UIViewController {
#IBOutlet weak var buttonToResize: UIButton!
#IBAction func buttonTapped(_ sender: UIButton) {
buttonToResize.titleLabel!.font = UIFont(name: "Helvetica", size: 40)
// Also tried buttonToResize.titleLabel?.font = UIFont .systemFont(ofSize: 4)
}
However the changes are not applied.
What is interesting, to me, that if I try to resize some other button (second one) after pressing on initial (first one), it works as expected.
Like this:
class ViewController: UIViewController {
#IBOutlet weak var buttonToResize: UIButton!
#IBOutlet weak var secondButtonToResize: UIButton!
#IBAction func buttonTapped(_ sender: UIButton) {
secondButtonToResize.titleLabel!.font = UIFont(name: "Helvetica", size: 40)
}
Other properties like backgroundColor seems to apply, however with font size I face problem.
First, here's the sequence of events when you tap on a UIButton.
The button sets its own isHighlighted property to true.
The button fires any Touch Down actions, possibly multiple times if you drag your finger around.
The button fires any Primary Action Triggered actions (like your buttonTapped).
The button fires any Touch Up actions.
The button sets its own isHighlighted property to false.
Every time isHighlighted changes, the button updates its styling to how it thinks it should look. So a moment after buttonTapped, the button you pressed overwrites your chosen font with its own font.
It's worth exploring this to make sure you understand it by creating a UIButton subclass. Don't use this in production. Once you start overriding parts of UIButton, you need to override all of it.
// This class is to demonstrate the sequence of events when you press a UIButton.
// Do not use in production.
// To make this work properly, you would also have to override all the other properties that effect UIButton.state, and also UIButton.state itself.
class MyOverrideHighlightedButton : UIButton {
// Define a local property to store our highlighted state.
var myHighlighted : Bool = false
override var isHighlighted: Bool {
get {
// Just return the existing property.
return myHighlighted
}
set {
print("Setting MyOverrideHighlightedButton.isHighlighted from \(myHighlighted) to \(newValue)")
myHighlighted = newValue
// Since the UIButton remains unaware of its highlighted state changing, we need to change its appearance here.
// Use garish colors so we can be sure it works during testing.
if (myHighlighted) {
titleLabel!.textColor = UIColor.red
} else {
titleLabel!.textColor = titleColor(for: .normal)
}
}
}
}
So where does it keep pulling its old font from? On loading a view it will apply UIAppearance settings, but those will get discarded when you press the button too. iOS 15+, it looks like it uses the new UIButton.Configuration struct. So you could put this in your buttonTapped:
// The Configuration struct used here was only defined in iOS 15 and
// will fail in earlier versions.
// See https://developer.apple.com/documentation/uikit/uibutton/configuration
sender.configuration!.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in
var outgoing = incoming
// We only want to change the font, but we could change other properties here too.
outgoing.font = UIFont(name: "Zapfino", size: 20)
return outgoing
}
I'd like to think there's a simpler way to do this. Whichever way you work, make sure it will also work in the event of other changes to your button, such as some other event setting isEnabled to false on it.
You probably want something like this
struct MyView: View {
#State var pressed: Bool = false
var body: some View {
Button(action: {
withAnimation {
pressed = true
}
}) {
Text("Hello")
}
Button(action: {
}) {
Text("Hello")
.font(pressed ? .system(size: 40) : .system(size: 20))
}
}
}
class ViewController: UIViewController {
#IBOutlet weak var buttonToResize: UIButton!
#IBAction func buttonTapped(_ sender: UIButton) {
sender.titleLabel?.font = .systemFont(ofSize: 30)
}
}
This should solve the problem. Use the sender tag instead of the IBOutlet.
Cowirrie analysis made me think of this solution (tested)
#IBAction func testX(_ sender: UIButton) {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
sender.titleLabel?.font = sender.titleLabel?.font.withSize(32)
}
}

how to create tappable text in uitextview in swift

I want to create tappable text when I tap on those text then fire some action like call any function do some operation of that text I catnap on any text not on whole uitextview
Try this -
override func viewDidLoad() {
super.viewDidLoad()
// You must set the formatting of the link manually
let linkAttributes: [NSAttributedStringKey: Any] = [
.link: NSURL(string: "https://www.apple.com")!,
.foregroundColor: UIColor.blue
]
let attributedString = NSMutableAttributedString(string: "Just click here to register")
// Set the 'click here' substring to be the link
attributedString.setAttributes(linkAttributes, range: NSMakeRange(5, 10))
self.textView.delegate = self
self.textView.attributedText = attributedString
self.textView.isUserInteractionEnabled = true
self.textView.isEditable = false
}
If I understand your question correctly, you want to click on a text view and call a function. You can use UITapGestureRecognizer.
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.isUserInteractionEnabled = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(labelTapped))
textView.addGestureRecognizer(tapGestureRecognizer)
}
#objc func labelTapped(){
print("Do something")
}
I've faced with problem same like your and I've tried many things to resolve. The best approach will be use Atributika (or similar) library. You will not regret this decision. It's easy to use and have a lot features.
https://github.com/psharanda/Atributika
If I understand your question properly then you could just add a button then change the button text to the text you want. Then like normal text you can mess with the colour, border or fill. After you have added this to your app link it up as an action with your view controller.swift file
Here is an example from my app 'Health Dash':
Image 1
Image 2
Then the swift 4 code:
import UIKit
class FriendsViewController: UIViewController {
#IBAction func exampleText(_ sender: Any) {
print("When you click this button something happens")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Here is an image of what it should look like:
Image 3

Access TextField from another class or method without storyboard

Okay, this might be one of the most basic questions ever, but all answers I find use storyboard to declare an outlet for a label, textfield or whatever element that needs to be changed. I, however, don't use storyboards and write everything in code. Now I have a function setupViews, where I define a textfield:
let usernameInput = UITextField()
Now, I can perfectly set the text or placeholder or whatever inside this setupViews() class, but how can I access it outside? For example, if I have a function logIn(), I want to call usernameInput.text and use it in this function.
Someone who can point me in the right direction? Do I need to declare this textfield globally, in another file, or something else?
When I create my views in code I always associate a property with the view that has all those various display values.
I have not tested this code to see but hopefully the following will give you an idea.
import UIKit
struct {
var name: String
}
class CustomViewController : UIViewController {
// some struct which contains data for view
var customViewData : ViewDataInfo? {
didSet {
labelOnScreen.text = customViewData.name
}
}
var labelOnScreen: UILabel = {
let label = UILabel()
label.text = "Placeholder information..."
// stuff auto layout
label.translateAutoresizingMaskIntoConstraints = false
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private func setupView() {
view.addSubview(label)
// set your constraints here
}
}

How to go back when the UITextfield is empty in Swift code?

My question is: When the UITextField is empty, how do I click the "Backspace" button to go to the previous UITextField? I have been struggling trying to do this in my code below?
Second Question: How do I only allow 1 character to get entered in the UITextField?
I am new at Swift code and trying to learn. Any help would be great.
What I am trying to do is have the user be able to type in a code in the 6 UITextFields and be able to click the "Backspace" button on any one of the UITextFields with only allowing the user to enter one number in each UITextField.
Code Below:
#objc func textFieldDidChange(textfield: UITextField) {
let text = textfield.text!
if text.utf16.count == 0 {
switch textfield {
case textField2:
textField1.becomeFirstResponder()
textField1.backgroundColor = UIColor.blue
textField1.tintColor = .clear
case textField3:
textField2.becomeFirstResponder()
textField2.backgroundColor = UIColor.blue
textField2.tintColor = .clear
case textField4:
textField3.becomeFirstResponder()
textField3.backgroundColor = UIColor.blue
textField3.tintColor = .clear
case textField5:
textField4.becomeFirstResponder()
textField4.backgroundColor = UIColor.blue
textField4.tintColor = .clear
case textField6:
textField5.becomeFirstResponder()
textField5.backgroundColor = UIColor.blue
textField5.tintColor = .clear
textField6.resignFirstResponder()
textField6.backgroundColor = UIColor.blue
textField6.tintColor = .clear
default:
break
}
}
else if text.utf16.count == 1 {
switch textfield {
case textField1:
textField1.backgroundColor = UIColor.black
textField1.textColor = .white
textField1.tintColor = .clear
textField2.becomeFirstResponder()
textField2.backgroundColor = UIColor.black
textField2.textColor = .white
textField2.tintColor = .clear
case textField2:
textField3.becomeFirstResponder()
textField3.backgroundColor = UIColor.black
textField3.textColor = .white
textField3.tintColor = .clear
case textField3:
textField4.becomeFirstResponder()
textField4.backgroundColor = UIColor.black
textField4.textColor = .white
textField4.tintColor = .clear
case textField4:
textField5.becomeFirstResponder()
textField5.backgroundColor = UIColor.black
textField5.textColor = .white
textField5.tintColor = .clear
case textField5:
textField6.becomeFirstResponder()
textField6.backgroundColor = UIColor.black
textField6.textColor = .white
textField6.tintColor = .clear
case textField6:
textField6.resignFirstResponder()
default:
break
}
}
}
I'd just like to point out that I'm still relatively new to iOS and Swift in general, but even with just a few minutes of searching, I was able to find some seeds of ideas which provided me with the suggested solution.
Based on your (improved) question, I believe a different approach is required. What you really don't want to use a text component. "Why"?
I here you ask. Because they don't actually provide you with the functionality that you want and come with a considerable overhead.
For this, what you really want is more control. You want to know when a key is pressed and you want to respond to it (I know, sounds like a text component, but) and be notified when more extended functionality occurs, like the delete key is pressed.
After a few minutes of research, some trial and error, I found that the UIKeyInput is more along the lines of what you want.
It will tell you when text is inserted and, more importantly, will tell you when Delete is pressed
The added benefit is, you can filter the input directly. You can take the first character from the String and ignore the rest or auto fill the following elements with the remaining text. You can perform validation (for numerical only content) and what ever else you might want to do
So, I started a really new project, added a UILabel to the UIViewController in the storyboard, bound it to the source and implemented the UIKeyInput protocol as such...
class ViewController: UIViewController {
override var canBecomeFirstResponder: Bool {
return true
}
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
becomeFirstResponder()
}
}
extension ViewController: UIKeyInput {
var hasText: Bool {
return true
}
func insertText(_ text: String) {
print(text)
label.text = text
}
func deleteBackward() {
print("Delete backward")
}
}
I ran the project and when a key was typed, the label was updated with the new key and when delete was pressed, the Delete backward text was printed to console.
Now. You have some choices to make. To use a single UIViewController and (maybe) a series of UILabels and manage interactions within it, so when a key is typed, you present the next label as the input focus (and when delete is pressed, you move back) or do you create a series of UIControls which represent each digit and manage via some delegate call back process.
You may also need to implement the UITextInputTraits protocol, which will allow you to control the keyboard presented
You might also like to have a read through Responding to Keyboard Events on iOS, CustomTextInputView.swift and Showing the iOS keyboard without a text input which were just some of the resources I used to hobble this basic example together with.
you can use this extension for your second question:
import UIKit
private var maxLengths = [UITextField: Int]()
extension UITextField {
#IBInspectable var maxLength: Int {
get {
guard let length = maxLengths[self] else {
return Int.max
}
return length
}
set {
maxLengths[self] = newValue
addTarget(
self,
action: #selector(limitLength),
for: UIControlEvents.editingChanged
)
}
}
#objc func limitLength(textField: UITextField) {
guard let prospectiveText = textField.text,
prospectiveText.count > maxLength
else {
return
}
let selection = selectedTextRange
let maxCharIndex = prospectiveText.index(prospectiveText.startIndex, offsetBy: maxLength)
text = prospectiveText.substring(to: maxCharIndex)
selectedTextRange = selection
}
}
when you add this extension to your project you can see an extra attribute in "Attribute Inspector" tab and you can set the max length of UITextField.

How can I create a "hyperlink" with Swift?

I'm trying to make separate pieces of text UILabels clickable. What I'm looking for is commonly known as a hyperlink in web development.
Link 1
Link 2
Link 3
Each a tag is its own UILabel, and it would ideally open Safari to the specified href when the text between the tags is clicked.
I've found a bevy of resources on how to do this sort of thing in Objective-C, but they all seem unnecessarily complicated and don't translate well to Swift (they fit an Objective-C organizational structure that doesn't work well in Swift and goes against the recommended way of using the language).
Here are a few:
How to add hyperlink in iPhone app?
How to make a clickable link inside a NSTextField and Cocoa
Text as Hyperlink in Objective-C
If I had a 3 UILabels,
Item 1
Item 2
Item 3
then what would be the best "Swift-y" way to make each item open to a different URL in Safari?
I could create separate buttons for each, but the UILabels are programmatically populated, so I was thinking that making the text respond to taps might be a better option.
Swift 3
I created a LinkUILabel class in github:
https://github.com/jorgecsan/LinkUILabel
With this you only need add the url inspectable as the shows the image:
or assign the url variable programmatically:
linkUILabel.url = "www.example.com"
If you want to implement by your self also I found that solution!:)
using:
// This is the label
#IBOutlet weak var label: UILabel!
override func loadView() {
super.loadView()
// This is the key
let tap = UITapGestureRecognizer(target: self, action: #selector(self.onClicLabel(sender:)))
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap)
}
// And that's the function :)
func onClicLabel(sender:UITapGestureRecognizer) {
openUrl("http://www.google.com")
}
func openUrl(urlString:String!) {
let url = URL(string: urlString)!
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
}
}
Hope it helps!:)
The One approach would be something like the following.
The assumptions are:
self.urls is a string array containing the urls associated with each UILabel.
Each UILabel tag has been set to the corresponding index in the array
labelTapped: is set as the touchUpInside handler for the labels.
import Foundation
import UIKit
class urltest {
var urls:[String]
init() {
self.urls=[String]() // Load URLs into here
}
#IBAction func labelTapped(sender:UILabel!) {
let urlIndex=sender.tag;
if (urlIndex >= 0 && urlIndex < self.urls.count) {
self.openUrl(self.urls[urlIndex]);
}
}
func openUrl(url:String!) {
let targetURL=NSURL.URLWithString(url)
let application=UIApplication.sharedApplication()
application.openURL(targetURL);
}
}
Hyperlink via UITextView
var termsConditionsTextView: UITextView = {
let view = UITextView()
view.backgroundColor = .clear
view.textAlignment = .left
let firstTitleString = "By registering for THIS_APP I agree with the "
let secondTitleString = "Terms & Conditions"
let finishTitleString = firstTitleString + secondTitleString
let attributedString = NSMutableAttributedString(string: finishTitleString)
attributedString.addAttribute(.link, value: "https://stackoverflow.com", range: NSRange(location: firstTitleString.count, length: secondTitleString.count))
view.attributedText = attributedString
view.textContainerInset = .zero
view.linkTextAttributes = [
.foregroundColor: UIColor.blue,
.underlineStyle: NSUnderlineStyle.single.isEmpty
]
view.font = view.font = UIFont(name: "YOUR_FONT_NAME", size: 16)
view.textColor = UIColor.black
return view }()

Resources