When I added an extension to UIAlertController to modify actions fonts, it didn't seem to work. What should I do to modify the Action font。
Here is my code
extension UIAlertController {
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if self.preferredStyle == .actionSheet {
for i in self.actions {
let attributedText = NSAttributedString(string: i.title ?? "", attributes: [NSAttributedString.Key.font : UIFont(name: "SFProText-Semibold", size: 15.0)!])
guard let label = (i.value(forKey: "__representer") as AnyObject).value(forKey: "label") as? UILabel else { return }
label.attributedText = attributedText
}
}
}
}
Create an extension of UIAlertController
extension UIAlertController {
func setBackgroudColor(color: UIColor) {
if let bgView = self.view.subviews.first,
let groupView = bgView.subviews.first,
let contentView = groupView.subviews.first {
contentView.backgroundColor = color
}
}
func setTitle(font: UIFont?, color: UIColor?) {
guard let title = self.title else { return }
let attributeString = NSMutableAttributedString(string: title)
if let titleFont = font {
attributeString.addAttributes([NSAttributedString.Key.font : titleFont],
range: NSMakeRange(0, title.utf8.count))
}
if let titleColor = color {
attributeString.addAttributes([NSAttributedString.Key.foregroundColor : titleColor],
range: NSMakeRange(0, title.utf8.count))
}
self.setValue(attributeString, forKey: "attributedTitle")
}
func setMessage(font: UIFont?, color: UIColor?) {
guard let title = self.message else {
return
}
let attributedString = NSMutableAttributedString(string: title)
if let titleFont = font {
attributedString.addAttributes([NSAttributedString.Key.font : titleFont], range: NSMakeRange(0, title.utf8.count))
}
if let titleColor = color {
attributedString.addAttributes([NSAttributedString.Key.foregroundColor : titleColor], range: NSMakeRange(0, title.utf8.count))
}
self.setValue(attributedString, forKey: "attributedMessage")
}
func setTint(color: UIColor) {
self.view.tintColor = color
}
}
Call it from a UIViewController may be in a button action like below.
func tapShowAlert() {
let alertController = UIAlertController(title: "Alert!!", message: "This is custom alert message", preferredStyle: .alert)
alertController.setTitle(font: UIFont.boldSystemFont(ofSize: 24), color: UIColor.yellow)
alertController.setMessage(font: UIFont(name: "Verdana", size: 16), color: UIColor.red)
let actnOk = UIAlertAction(title: "Ok", style: .default, handler: nil)
let actnCancel = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alertController.addAction(actnOk)
alertController.addAction(actnCancel)
self.present(alertController, animated: true, completion: nil)
}
Related
I want to create a hyperlink with selected text in UITextView in swift.eg. When I type "Please click here" in UITextView and select "here" from the sentence I will open a UIAlertController with textView where I can enter the url and while tapping "Save" button on UIAlertController I want the word "here" to add the hyperlink. How do I achieve this?
This is my code for UITextView:
var textView: TextView?
override public func viewDidLoad() {
super.viewDidLoad()
textView = TextView(
editorConfig: EditorConfig(
theme: getTheme()
)
)
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
view.backgroundColor = .white
layoutTextView()
}
private func layoutTextView() {
textView?.frame = CGRect(
x: view.safeAreaInsets.left,
y: view.safeAreaInsets.top,
width: view.bounds.width - view.safeAreaInsets.left - view.safeAreaInsets.right,
height: view.frame.size.height / 3
)
textView?.layer.borderWidth = 1
textView?.layer.borderColor = UIColor.gray.cgColor
textView?.isEditable = true
textView?.isUserInteractionEnabled = true
textView?.textColor = .black
if let textView = textView {
view.addSubview(textView)
}
}
I have a button insertLink which displays the UIAlertController. Here's the code for the Alert:
func showAlert(url: String?) {
guard let url = url else {
return
}
let attributedString = NSMutableAttributedString(string: url)
attributedString.SetAsLink(textToFind: url + alertTextView.text, linkURL: url + alertTextView.text)
let alertController = UIAlertController(title: "Link", message: nil, preferredStyle: .alert)
alertTextView.textContainerInset = UIEdgeInsets.init(top: 15, left: 5, bottom: 8, right: 35)
let saveAction = UIAlertAction(title: "Save", style: .default) { (action) in
alertController.view.removeObserver(self, forKeyPath: "bounds")
}
saveAction.isEnabled = false
let cancelAction = UIAlertAction.init(title: "Cancel", style: .default) { (action) in
alertController.view.removeObserver(self, forKeyPath: "bounds")
}
alertController.view.addObserver(self, forKeyPath: "bounds", options: NSKeyValueObservingOptions.new, context: nil)
NotificationCenter.default.addObserver(forName: UITextView.textDidChangeNotification, object: alertTextView, queue: OperationQueue.main) { (notification) in
saveAction.isEnabled = self.alertTextView.text != ""
}
alertTextView.backgroundColor = UIColor.white
alertTextView.isEditable = true
alertTextView.attributedText = attributedString
alertTextView.dataDetectorTypes = UIDataDetectorTypes.link
alertTextView.layer.cornerRadius = 20
alertController.view.addSubview(self.alertTextView)
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "bounds"{
if let rect = (change?[NSKeyValueChangeKey.newKey] as? NSValue)?.cgRectValue {
let margin:CGFloat = 8.0
alertTextView.frame = CGRect.init(x: rect.origin.x + margin, y: rect.origin.y + margin, width: rect.width - 2*margin, height: rect.height / 2)
alertTextView.bounds = CGRect.init(x: rect.origin.x + margin, y: rect.origin.y + margin, width: rect.width - 2*margin, height: rect.height / 2 - 10)
}
}
}
So in the UIAlertController's textView(alertTextView) when I type "https://www.google.com" how do I send this link to the main UITextView and onClick of it it should open browser?
I have created an alert Where a mybutton have to be hidden while the activity indicator has to be shown. but it didn't work.
Here is the code:
private func showAlert() {
let alert = UIAlertController(title: "aaaaaa", message: ccccccccc, preferredStyle: .alert)
let attributedString = NSAttributedString(string: "aaaaa", attributes: [.font: UIFont.boldSystemFont(ofSize: 18), //your font here
.foregroundColor : Theme.bleu!])
alert.setValue(attributedString, forKey: "attributedTitle")
alert.addAction(UIAlertAction(title: annuler, style: .cancel))
alert.addAction(UIAlertAction(title: optimiserLesMicrosUniquement, style: .default, handler: { (action) in
self.myButton.isHidden = true // Here begins the problem
self.activityIndicator.isHidden = false
let menuOptimiser = functionNumberX()
self.activityIndicator.isHidden = true // Here is the problem
self.myButton.isHidden = false
let reponse = menuOptimiser.0
if reponse == false {
self.showAlerteMenuOptimal()
} else {
if menuOptimiser.1 > 0 {
self.presentAlerteDuGain(gain: menuOptimiser.1, menu: menuOptimiser.2)
} else {
self.presentAlerteOptimisationSansGainDeCalories(calorie: abs((round(menuOptimiser.1))), menu: menuOptimiser.2)
}
}
self.gestionDeLaNotationDuMenuEnCours()
}))
present(alert, animated: true)
}
When i execute it, mybutton never disappears, and activity indicator never appears.
i don't know how to do that.
try removing, these two lines , since you are opposite at almost the same time .
self.activityIndicator.isHidden = true // Here is the problem
self.myButton.isHidden = false
I just found a solution. Here is the code
var menuOptimiser : (Bool, Float, [[AlimentObject]])!
private func showAlert() {
let alert = UIAlertController(title: "aaaaaa", message: ccccccccc, preferredStyle: .alert)
let attributedString = NSAttributedString(string: "aaaaa", attributes: [.font: UIFont.boldSystemFont(ofSize: 18), //your font here
.foregroundColor : Theme.bleu!])
alert.setValue(attributedString, forKey: "attributedTitle")
alert.addAction(UIAlertAction(title: annuler, style: .cancel))
alert.addAction(UIAlertAction(title: optimiserLesMicrosUniquement, style: .default, handler: { (action) in
self.myButton.isHidden = true // Here begins the problem
self.activityIndicator.isHidden = false
DispatchQueue.main.asyncAfter(deadline: .now()) {
let menuOptimiser = functionNumberX()
self.activityIndicator.isHidden = true // Here is the problem
self.myButton.isHidden = false
let reponse = menuOptimiser.0
if reponse == false {
self.showAlerteMenuOptimal()
} else {
if menuOptimiser.1 > 0 {
self.presentAlerteDuGain(gain: menuOptimiser.1, menu: menuOptimiser.2)
} else {
self.presentAlerteOptimisationSansGainDeCalories(calorie: abs((round(menuOptimiser.1))), menu: menuOptimiser.2)
}
}
}
self.gestionDeLaNotationDuMenuEnCours()
}))
present(alert, animated: true)
}
when i choose an option from action sheet, it set a title label to button. But when i click another button with another action sheet, it cancel title label of first button. How can i set each button without cancel any title label?
This is how it works:
This is my code of two action sheets:
#IBAction func paymentMethodActionSheet(_ sender: Any) {
PaymentMethodTitle.titleLabel?.text = "Seleziona"
let optionMenu = UIAlertController(title: nil, message: "Scegli il metodo di pagamento", preferredStyle: .actionSheet)
let cardAction = UIAlertAction(title: "Paga con carta", style: .default, handler: { action in
self.PaymentMethodTitle.titleLabel?.text = "Paga con carta"
self.PaymentMethodTitle.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
})
let contantiAction = UIAlertAction(title: "Contanti", style: .default, handler: { action in
self.PaymentMethodTitle.titleLabel?.text = "Contanti"
self.PaymentMethodTitle.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
})
let cancelAction = UIAlertAction(title: "Cancella", style: .cancel)
optionMenu.addAction(cardAction)
optionMenu.addAction(contantiAction)
optionMenu.addAction(cancelAction)
if let popoverController = optionMenu.popoverPresentationController {
popoverController.barButtonItem = sender as? UIBarButtonItem
popoverController.sourceView = self.view
popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
popoverController.permittedArrowDirections = []
}
self.present(optionMenu, animated: true, completion: nil)
}
#IBAction func shippingMethodActionSheet(_ sender: Any) {
shippingMethodTitle.titleLabel?.text = "Seleziona"
let option2Menu = UIAlertController(title: nil, message: "Scegli l'opzione d'acquisto", preferredStyle: .actionSheet)
let houseAction = UIAlertAction(title: "Consegna a domicilio", style: .default, handler: { action in
self.shippingMethodTitle.titleLabel?.text = "Consegna a domicilio"
self.shippingMethodTitle.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
})
let businessAction = UIAlertAction(title: "Ritiro presso attività", style: .default, handler: { action in
self.shippingMethodTitle.titleLabel?.text = "Ritiro presso attività"
self.shippingMethodTitle.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
})
let cancel2Action = UIAlertAction(title: "Cancella", style: .cancel)
option2Menu.addAction(houseAction)
option2Menu.addAction(businessAction)
option2Menu.addAction(cancel2Action)
if let popoverController2 = option2Menu.popoverPresentationController {
popoverController2.barButtonItem = sender as? UIBarButtonItem
popoverController2.sourceView = self.view
popoverController2.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
popoverController2.permittedArrowDirections = []
}
self.present(option2Menu, animated: true, completion: nil)
}
Instead of using self.PaymenMethodTitle?.titleLabel.text = "Contanti"to change the button title inside closure, just Use self.PaymentMethodTitle.setTitle("Contanti", for: .normal).
I've tried it myself and Its working Fine.
Code:
class ViewController: UIViewController {
#IBOutlet weak var btn1: UIButton!
#IBOutlet weak var btn2: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btn1Pressed(_ sender: Any) {
let optionMenu = UIAlertController(title: nil, message: "Scegli il metodo di pagamento", preferredStyle: .actionSheet)
let cardAction = UIAlertAction(title: "Paga con carta", style: .default, handler: { action in
self.btn1.setTitle("Paga con carta", for: .normal)
self.btn1.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
})
let contantiAction = UIAlertAction(title: "Contanti", style: .default, handler: { action in
self.btn1.setTitle("Conati", for: .normal)
self.btn1.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
})
let cancelAction = UIAlertAction(title: "Cancella", style: .cancel)
optionMenu.addAction(cardAction)
optionMenu.addAction(contantiAction)
optionMenu.addAction(cancelAction)
if let popoverController = optionMenu.popoverPresentationController {
popoverController.barButtonItem = sender as? UIBarButtonItem
popoverController.sourceView = self.view
popoverController.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
popoverController.permittedArrowDirections = []
}
self.present(optionMenu, animated: true, completion: nil)
}
#IBAction func btn2Pressed(_ sender: Any) {
let option2Menu = UIAlertController(title: nil, message: "Scegli l'opzione d'acquisto", preferredStyle: .actionSheet)
let houseAction = UIAlertAction(title: "Consegna a domicilio", style: .default, handler: { action in
self.btn2.setTitle("Consegna a domicilio", for: .normal)
self.btn2.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
})
let businessAction = UIAlertAction(title: "Ritiro presso attività", style: .default, handler: { action in
self.btn2.setTitle("Ritiro presso attività", for: .normal)
self.btn2.contentHorizontalAlignment = UIControl.ContentHorizontalAlignment.right
})
let cancel2Action = UIAlertAction(title: "Cancella", style: .cancel)
option2Menu.addAction(houseAction)
option2Menu.addAction(businessAction)
option2Menu.addAction(cancel2Action)
if let popoverController2 = option2Menu.popoverPresentationController {
popoverController2.barButtonItem = sender as? UIBarButtonItem
popoverController2.sourceView = self.view
popoverController2.sourceRect = CGRect(x: self.view.bounds.midX, y: self.view.bounds.midY, width: 0, height: 0)
popoverController2.permittedArrowDirections = []
}
self.present(option2Menu, animated: true, completion: nil)
}
}
Simulator ScreenShot:
I want to display in an attributed string 2 links, each link with a different color. I do not understand how to do that. It will always set just one color. I've been struggling with this for days and still can't figure out how to make it work. Does anybody know? I can set two colors but not for links! All links are the same color.
This is my whole implementation: (UPDATE)
var checkIn = ""
var friends = ""
//MARK: Change Name Color / Font / Add a second LABEL into the same label
func setColorAndFontAttributesToNameAndCheckIn() {
let nameSurname = "\(postAddSetup.nameSurname.text!)"
checkIn = ""
friends = ""
if selectedFriends.count == 0 {
print("we have no friends...")
friends = ""
} else if selectedFriends.count == 1 {
print("we have only one friend...")
friends = ""
friends = " is with \(self.firstFriendToShow)"
} else if selectedFriends.count > 1 {
print("we have more than one friend...")
friends = ""
friends = " is with \(self.firstFriendToShow) and \(self.numberOfFriendsCount) more"
}
if checkIn == "" {
checkIn = ""
}
var string = postAddSetup.nameSurname.text
string = "\(nameSurname)\(friends)\(checkIn) "
let attributedString = NSMutableAttributedString(string: string!)
attributedString.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFont(ofSize: 14), range: (string! as NSString).range(of: nameSurname))
attributedString.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 13), range: (string! as NSString).range(of: checkIn))
attributedString.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 13), range: (string! as NSString).range(of: friends))
attributedString.addLink("checkIn", linkColor: UIColor.darkGray, text: checkIn)
attributedString.addLink("tagFriends", linkColor: UIColor.red, text: friends)
//attributedString.addAttribute(NSLinkAttributeName, value: "checkIn", range: (string! as NSString).range(of: checkIn))
//attributedString.addAttribute(NSLinkAttributeName, value: "tagFriends", range: (string! as NSString).range(of: friends))
//postAddSetup.nameSurname.linkTextAttributes = [NSForegroundColorAttributeName:UIColor.redIWorkOut(), NSFontAttributeName: UIFont.systemFont(ofSize: 13)]
//attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.darkGray, range: (string! as NSString).range(of: checkIn))
postAddSetup.nameSurname.attributedText = attributedString
print("atribute: \(attributedString)")
}
func string1Action() {
print("action for string 1...")
}
func string2Action() {
print("action for string 2...")
}
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
if URL.absoluteString == "string1" {
string1Action()
} else if URL.absoluteString == "string2" {
string2Action()
}
return false
}
extension NSMutableAttributedString {
func addLink(_ link: String, linkColor: UIColor, text: String) {
let pattern = "(\(text))"
let regex = try! NSRegularExpression(pattern: pattern,
options: NSRegularExpression.Options(rawValue: 0))
let matchResults = regex.matches(in: self.string,
options: NSRegularExpression.MatchingOptions(rawValue: 0),
range: NSRange(location: 0, length: self.string.characters.count))
for result in matchResults {
self.addAttribute(NSLinkAttributeName, value: link, range: result.rangeAt(0))
self.addAttribute(NSForegroundColorAttributeName, value: linkColor, range: result.rangeAt(0))
}
}
}
I have used in a project this NSMutableAttributedString extension adapted from this Article.
Using NSRegularExpression you can assign your respective color matching the range of your link text:
The extension:
extension NSMutableAttributedString {
func addLink(_ link: String, linkColor: UIColor, text: String) {
let pattern = "(\(text))"
let regex = try! NSRegularExpression(pattern: pattern,
options: NSRegularExpression.Options(rawValue: 0))
let matchResults = regex.matches(in: self.string,
options: NSRegularExpression.MatchingOptions(rawValue: 0),
range: NSRange(location: 0, length: self.string.characters.count))
for result in matchResults {
self.addAttribute(NSLinkAttributeName, value: link, range: result.rangeAt(0))
self.addAttribute(NSForegroundColorAttributeName, value: linkColor, range: result.rangeAt(0))
}
}
}
Edit:
Set a custom UITextView class to use this extension and using the delegate function shouldInteractWith url it’s possible to simulate the hyperlink logic of UITextView:
class CustomTextView: UITextView {
private let linksAttributes = [NSLinkAttributeName]
override func awakeFromNib() {
super.awakeFromNib()
let tapGest = UITapGestureRecognizer(target: self, action: #selector(self.onTapAction))
self.addGestureRecognizer(tapGest)
}
#objc private func onTapAction(_ tapGest: UITapGestureRecognizer) {
let location = tapGest.location(in: self)
let charIndex = self.layoutManager.characterIndex(for: location, in: self.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
if charIndex < self.textStorage.length {
var range = NSMakeRange(0, 0)
for linkAttribute in linksAttributes {
if let link = self.attributedText.attribute(linkAttribute, at: charIndex, effectiveRange: &range) as? String {
guard let url = URL(string: link) else { return }
_ = self.delegate?.textView?(self, shouldInteractWith: url, in: range, interaction: .invokeDefaultAction)
}
}
}
}
}
How to use:
attributedString.addLink(yourLinkUrl, linkColor: yourLinkColor, text: yourLinkText)
let textView = CustomTextView()
textView.attributedText = attributedString
I have a UIAlertController with two textFields :
class ModifiedSearchViewController: UISearchController, UISearchBarDelegate {
var nameOfOwnPlace = ""
var adressOfOwnPlace = ""
func insertYourPLace() {
let alertController = UIAlertController(title: "Insert New Place", message: "", preferredStyle: UIAlertControllerStyle.Alert)
let saveAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: {
alert -> Void in
let nameTextField = alertController.textFields![0] as UITextField
let adressTextField = alertController.textFields![1] as UITextField
self.nameOfOwnPlace = nameTextField.text!
self.adressOfOwnPlace = adressTextField.text!
print(self.nameOfOwnPlace)
})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Default, handler: {
(action : UIAlertAction!) -> Void in
})
alertController.addTextFieldWithConfigurationHandler { (textField : UITextField!) -> Void in
textField.placeholder = "Enter Place's Name..."
}
alertController.addTextFieldWithConfigurationHandler { (textField : UITextField!) -> Void in
textField.placeholder = "Enter Adress's Name..."
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
...
}
I have an other view using my ModifiedSearchViewController class
but I dont succeed to send my strings (nameOfOwnPlace, adressOfOwnPlace) to the other view
searchController = ModifiedSearchViewController(searchResultsController: resultsViewController)
print(searchController.adressOfOwnPlace) // nil
Thanks for any help! :)
EDIT::
var resultsViewController = GMSAutocompleteResultsViewController()
var searchController = ModifiedSearchViewController()
func setSearchViewController() {
resultsViewController.delegate = self
searchController = ModifiedSearchViewController(searchResultsController: resultsViewController)
searchController.searchResultsUpdater = resultsViewController
print(searchController.adressOfOwnPlace) // nil
}