How to change font of buttons in UIAlertController - swift

I need to change font of buttons in UIAlertController, I've used the code below, but I doesn't work properly, because when the user touches the buttons of UIAlertController, the font will be change to it's default.
extension UIAlertController {
private func changeFont(view:UIView,font:UIFont) {
for item in view.subviews {
if let col = item as? UICollectionView {
for row in col.subviews{
changeFont(view: row, font: font)
}
}
if let label = item as? UILabel {
label.font = font
} else {
changeFont(view: item, font: font)
}
}
}
//To set font for any UILabels in action sheet
open override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let font = MyCustomFont
changeFont(view: self.view, font: font! )
}
}
and here is the usage of this extension:
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let action = UIAlertAction(title: STRING_OF_ACTION, style: .default) { _ in
//some action code
}
optionMenu.addAction(action)
//in UIViewController
self.present(optionMenu, animated: true, completion: nil)

It's hard to change it because the iOS system doesn't provide the API for us to do it. So we'd better use third party libraries like: SCLAlertView-Swift or PopupDialog if we need some customization.

Related

Changing the back button of UINavigaitonBar with MVVM+C

I am using MVVM+C pattern to build my app. Currently I am facing a problem with changing the native back button title and image of navigation bar to the custom image without the title. I've tried a lots of solutions what I was able to find, but nothing set the different title or even an image. I've ended up with this code in AppDelegate.swift:
let navigationController: UINavigationController = .init()
if #available(iOS 13.0, *) {
let appearence = UINavigationBarAppearance()
appearence.configureWithOpaqueBackground()
appearence.backgroundColor = .backgroundColor
appearence.shadowColor = nil
appearence.shadowImage = nil
navigationController.navigationBar.standardAppearance = appearence
navigationController.navigationBar.scrollEdgeAppearance = navigationController.navigationBar.standardAppearance
} else {
navigationController.navigationBar.isTranslucent = false
navigationController.navigationBar.barTintColor = .backgroundColor
navigationController.navigationBar.shadowImage = nil
navigationController.navigationBar.shadowColor = nil
}
// This code is not working at all, always get "Back" as a default with default image =====
let backButtonBackgroundImage = UIImage(named: "backButton")
navigationController.navigationBar.backIndicatorImage = backButtonBackgroundImage
navigationController.navigationBar.backIndicatorTransitionMaskImage = backButtonBackgroundImage
let backBarButtton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationController.navigationItem.backBarButtonItem = backBarButtton
// =========
navigationController.navigationBar.tintColor = .primary
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
Also, I've followed the official documentation but without any success. As default I've set the navigation bar as hidden (because is not needed for multiple times) and I am showing it in ViewWillAppear and hiding in ViewWillDisappear methods.
Is there someone who has an idea of what's going on? Thanks!
The result of this code:
Expected result:
This is what I get with the new code:
SOLUTION:
After using code from Scott I was able to change the image and look of the navigation bar but I lost the ability to swipe back. After adding this code to the UINavigationBar extension I was able to get it back:
extension UINavigationController: UIGestureRecognizerDelegate {
#objc func goBack(sender: Any?) {
self.popViewController(animated: true)
}
override open func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return viewControllers.count > 1
}
}
Below is some Playground code that shows a UINavigationController with a custom back button that is an image.
Note that what it does is hides the system provided back button, then substitutes another button that still performs the "back" action but on a custom UINavigationController.
There may be a more efficient way to duplicate the functionality of "back" that doesn't involve a custom class and a custom target-action setup, but I couldn't find one quickly so finding that solution can be left as an exercise for the reader.
import UIKit
import SwiftUI
import PlaygroundSupport
NSSetUncaughtExceptionHandler { error in
debugPrint(error)
}
class MyNavController : UINavigationController {
#objc func goBack(sender: Any?) {
self.popViewController(animated: true)
}
}
let navDestination1 = UIViewController()
navDestination1.navigationItem.title = "Destination 1"
let navigationController = MyNavController(rootViewController: navDestination1)
if #available(iOS 13.0, *) {
let appearence = UINavigationBarAppearance()
appearence.configureWithOpaqueBackground()
appearence.backgroundColor = .purple
appearence.shadowColor = nil
appearence.shadowImage = nil
navigationController.navigationBar.standardAppearance = appearence
navigationController.navigationBar.scrollEdgeAppearance = navigationController.navigationBar.standardAppearance
} else {
navigationController.navigationBar.isTranslucent = false
navigationController.navigationBar.barTintColor = .purple
navigationController.navigationBar.shadowImage = nil
}
let navDestination2 = UITableViewController()
navDestination2.navigationItem.title = "Destination 2"
navDestination2.navigationItem.hidesBackButton = true
navDestination2.navigationItem.leftBarButtonItem = UIBarButtonItem(
image: UIImage(systemName: "multiply.circle.fill"),
style: UIBarButtonItem.Style.done,
target: navigationController,
action: #selector(MyNavController.goBack))
navigationController.pushViewController(navDestination2, animated: true)
navigationController.view.bounds = CGRect(x: 0,y: 0,width: 320,height: 480)
PlaygroundSupport.PlaygroundPage.current.liveView = navigationController

How to reload UIView after closing UIAlertController

I try to make BarChart using https://github.com/CoreCharts/CoreCharts I have a button, which call UIAlertController with textField.
After I click submit button, new data is added to the array. I need the diagram to reboot.
#IBAction func newWeightButton(_ sender: Any) {
//updateGraph()
let alert = UIAlertController(title: "Enter your new weight", message: nil, preferredStyle: .alert)
alert.addTextField()
let submitAction = UIAlertAction(title: "Add", style: .default) { [unowned alert] _ in
let textField = alert.textFields![0]
textField.keyboardType = UIKeyboardType.numberPad
textField.becomeFirstResponder()
let answer = Double(textField.text!)
self.weightArr.append(answer!)
print(self.weightArr)
self.loadCoreChartData()
}
alert.addAction(submitAction)
present(alert, animated: true)
}
func loadCoreChartData() -> [CoreChartEntry] {
return getWeightList()
}
func getWeightList()->[CoreChartEntry] {
var allResults = [CoreChartEntry]()
//let days = ["Mon","Tue","Wed","Thur","Fri"]
let plateNumber = [80,75,90,88,84]
var count = self.weightArr.count
for index in 0..<count {
let newEntry = CoreChartEntry(id: "\(self.weightArr[index])",
barTitle: "Day",
barHeight: Double(self.weightArr[index]),
barColor: UIColor(red: 0, green: 122/255, blue: 1, alpha: 1))
allResults.append(newEntry)
}
For CoreCharts, call reload() on the chart.
Generally, in iOS, setting properties of the UIView is sufficient to get it to update, but with views that use a datasource of some type (like tables, collection views, and core charts), there is usually some kind of reload...() function to tell the view that the datasource changed.

How to change the title registry using prefersLargeTitles?

I know that it is possible to set font family, font size and color separately for «large» and «small» titles using prefersLargeTitles.
The question is: is there any options for navigation controller to show the «large title» in opened navigation panel with uppercase?
Now I use custom Navigation Controller:
class MyNavigationController: UINavigationController {
public var titleSaved: String?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let topItem = navigationBar.topItem else {
return
}
if navigationBar.frame.size.height > 60 {
topItem.title = topItem.title?.uppercased()
} else {
if let titleSaved = titleSaved {
topItem.title = titleSaved
} else {
topItem.title = topItem.title?.applyingTransform(StringTransform(rawValue: "Title"), reverse: false)
}
}
}
}
Set title from View Controlle:
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
let title = "Sign In"
navigationItem.title = title
if let nc = navigationController as? MyNavigationController {
nc.titleSaved = title
}
}
}
this solution works, but when you switch from "large" title to "small" title and backwards it twitches and it looks pretty buggy
You can have an uppercased title for «large title» and capitalized title for «small title» using Small caps fonts
Change titleTextAttributes with other font and change largeTitleTextAttributes with caps font
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Sign In"
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.red]
self.navigationController?.navigationBar.largeTitleTextAttributes = [.foregroundColor: UIColor.red,
.font:UIFont(name: "Panton-LightCaps", size: 30)!]
}
}
Or You can customize your font. I've created a new style font using OpenSans in http://www.glyphrstudio.com/online/
You can download it here
self.title = "Sign In"
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationBar.titleTextAttributes = [.font:UIFont(name: "OpenSans-Regular", size: 30)!]
self.navigationController?.navigationBar.largeTitleTextAttributes = [.font:UIFont(name: "MyFontRegular", size: 30)!]
you can try like this:
navigationController?.navigationBar.prefersLargeTitles = true
let NavigationTitle = "Sign in"
navigationController?.navigationBar.topItem?.title = NavigationTitle.uppercased()
You just try to set a navigationbar title as uppercased. and set pregfersLargeTitles as true
self.navigationController?.navigationBar.prefersLargeTitles = true
self.title = "title".uppercased()
Check this:

UIAlertController change font color

Here's my code that creates the UIAlertController
// Create the alert controller
var alertController = UIAlertController(title: "Are you sure you want to call \(self.number)?", message: "", preferredStyle: .Alert)
// Create the actions
var okAction = UIAlertAction(title: "Call", style: UIAlertActionStyle.Default) {
UIAlertAction in
var url:NSURL = NSURL(string: "tel://\(self.number)")!
UIApplication.sharedApplication().openURL(url)
}
var cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) {
UIAlertAction in
}
// Add the actions
alertController.addAction(okAction)
alertController.addAction(cancelAction)
// Present the controller
self.presentViewController(alertController, animated: true, completion: nil)
I can't figure out how to change the text color of the cancel and call actions. The title text is currently black and the cancel and call buttons are white. I'm making to make them all black for better visibility. Any ideas? Thanks!
After some trial and error, I found this worked. Hopefully this will help future swift newcomers!
alertController.view.tintColor = UIColor.blackColor()
Piyush's answer helped me the most, but here are some tweaks for Swift 3 and to change the title and message separately.
Title:
alert.setValue(NSAttributedString(string: alert.message, attributes: [NSFontAttributeName : UIFont.systemFont(ofSize: 29, weight: UIFontWeightMedium), NSForegroundColorAttributeName : UIColor.red]), forKey: "attributedTitle")
Message:
alert.setValue(NSAttributedString(string: alert.message, attributes: [NSFontAttributeName : UIFont.systemFont(ofSize: 29, weight: UIFontWeightMedium), NSForegroundColorAttributeName : UIColor.red]), forKey: "attributedMessage")
The big font size is because I actually needed to do it for tvOS, works great on it and iOS.
Swift 4.2
One way of doing this is to make extension on UIAlertController, with this all of your app alerts whil have the same tint color. But it leaves destructive actions in red color.
extension UIAlertController{
open override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.view.tintColor = .yourcolor
}
}
Below code is changing the UIAlertView title color.
let alert = UIAlertController(title: messageTitle, message: messageText, preferredStyle: UIAlertControllerStyle.Alert)
alert.setValue(NSAttributedString(string: messageTitle, attributes: [NSFontAttributeName : UIFont.systemFontOfSize(17),NSForegroundColorAttributeName : UIColor.redColor()]), forKey: "attributedTitle")
alert.addAction(UIAlertAction(title: buttonText, style: UIAlertActionStyle.Default, handler: nil))
parent.presentViewController(alert, animated: true, completion: nil)
If you want to change button color then add following code after present View Controller.
alert.view.tintColor = UIColor.redColor()
Here's an update for Swift 4, using Cody's answer as a base:
Setting a colour for the alert title:
alert.setValue(NSAttributedString(string: alert.title!, attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17, weight: UIFont.Weight.medium), NSAttributedStringKey.foregroundColor : UIColor.blue]), forKey: "attributedTitle")
Setting a colour for the alert message:
alert.setValue(NSAttributedString(string: alert.message, attributes: [NSAttributedStringKey.font : UIFont.systemFont(ofSize: 17, weight: UIFont.Weight.Medium), NSAttributedStringKey.foregroundColor : UIColor.green]), forKey: "attributedMessage")
As per https://developer.apple.com/documentation/foundation/nsattributedstring/key
In Swift 5 and XCode 11.1 and later, colour for the alert title:
alert.setValue(NSAttributedString(string: alert.title!, attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 25, weight: UIFont.Weight.medium), NSAttributedString.Key.foregroundColor : UIColor.red]), forKey: "attributedTitle")
Colour for the alert message:
alert.setValue(NSAttributedString(string: alert.message!, attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 20,weight: UIFont.Weight.medium),NSAttributedString.Key.foregroundColor :UIColor.black]), forKey: "attributedMessage")
Before iOS 9.0 you could simply change the underlying view's tintColor like this:
alertController.view.tintColor = UIColor.redColor()
However, due to a bug introduced in iOS 9, you can either:
Change the app tintColor in the AppDelegate.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
self.window.tintColor = UIColor.redColor()
return true
}
Reapply the color in the completion block.
self.presentViewController(alert, animated: true, completion: {() -> Void in
alert.tintColor = UIColor.redColor()
})
See my other answer here: https://stackoverflow.com/a/37737212/1781087
I have faced the same issue and spent a lot of time trying to find the best way to change it's color for iOS 9 and iOS 10 + because it's implemented in a different way.
Finally I have made an extension for UIViewController. In extension I have added custom function which is almost equal to default function "present", but performs fix of colours. Here you are my solution. Applicable for swift 3+, for projects with target starting from iOS 9:
extension UIViewController {
/// Function for presenting AlertViewController with fixed colors for iOS 9
func presentAlert(alert: UIAlertController, animated flag: Bool, completion: (() -> Swift.Void)? = nil){
// Temporary change global colors
UIView.appearance().tintColor = UIColor.red // Set here whatever color you want for text
UIApplication.shared.keyWindow?.tintColor = UIColor.red // Set here whatever color you want for text
//Present the controller
self.present(alert, animated: flag, completion: {
// Rollback change global colors
UIView.appearance().tintColor = UIColor.black // Set here your default color for your application.
UIApplication.shared.keyWindow?.tintColor = UIColor.black // Set here your default color for your application.
if completion != nil {
completion!()
}
})
}
}
To use this fixed function, you should just call this function instead of default present function. Example:
self.presentAlert(alert: alert, animated: true)
Same solution, but for UIActivityViewController:
extension UIViewController {
/// Function for presenting UIActivityViewController with fixed colors for iOS 9 and 10+
func presentActivityVC(vc: UIActivityViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil) {
// Temporary change global colors for changing "Cancel" button color for iOS 9 and 10+
if UIDevice.current.systemVersion.range(of: "9.") != nil {
UIApplication.shared.keyWindow?.tintColor = ColorThemes.alertViewButtonTextColor
} else {
UILabel.appearance().textColor = ColorThemes.alertViewButtonTextColor
}
self.present(vc, animated: flag) {
// Rollback for changing global colors for changing "Cancel" button color for iOS 9 and 10+
if UIDevice.current.systemVersion.range(of: "9.") != nil {
UIApplication.shared.keyWindow?.tintColor = ColorThemes.tintColor
} else {
UILabel.appearance().textColor = ColorThemes.textColorNormal
}
if completion != nil {
completion!()
}
}
}
}
I hope this will help somebody and will save a lot of time. Because my time was not saved by such detailed answer :)
Try this
alert.setValue(NSAttributedString(string: alert.message, attributes: [NSFontAttributeName : UIFont.systemFont(ofSize: 29, weight: UIFontWeightMedium), NSForegroundColorAttributeName : UIColor.red]), forKey: "attributedTitle")
func showAlertOnVC(title: String,btnTitle:String, btnSubTitle:String, message: String, VC: UIViewController? , getcompleted: #escaping((_ confrmation: Bool)-> ())) {
guard let vc = VC else {
return
}
let alert = UIAlertController(title: title , message: message, preferredStyle: (UIDevice.current.userInterfaceIdiom == UIUserInterfaceIdiom.pad ) ? .alert : .alert)
alert.view.tintColor = UIColor.green
let confirmBtn = UIAlertAction(title: btnTitle, style: .default, handler: {_ in
getcompleted(true)
})
let cancelBtn = UIAlertAction(title: btnSubTitle, style: .cancel, handler: {_ in
getcompleted(false)
})
cancelBtn.setValue(UIColor.red, forKey: "titleTextColor")
alert.addAction(confirmBtn)
alert.addAction(cancelBtn)
vc.present(alert, animated: true, completion: nil)
}

Change title and message font of alert using UIAlertController in Swift

I am trying to change the title and message font for an alert shown using UIAlertController
I am trying to do using an NSAttributedStirng, but it gives compiler error that a NSAttributed string cannot be taken instead of Stirng
I tried something similar to this
var title_attrs = [NSFontAttributeName : CustomFonts.HELVETICA_NEUE_MEDIUM_16]
var msg_attrs = [NSFontAttributeName : CustomFonts.HELVETICA_NEUE_REGULAR_14]
var title = NSMutableAttributedString(string:"Done", attributes:title_attrs)
var msg = NSMutableAttributedString(string:"The job is done ", attributes:msg_attrs)
let alertController = UIAlertController(title: title, message: title , preferredStyle: UIAlertControllerStyle.Alert)
Can someone guide me how can I achieve this?
Swift 3 Version:
extension UIAlertController {
func changeFont(view: UIView, font:UIFont) {
for item in view.subviews {
if item.isKind(of: UICollectionView.self) {
let col = item as! UICollectionView
for row in col.subviews{
changeFont(view: row, font: font)
}
}
if item.isKind(of: UILabel.self) {
let label = item as! UILabel
label.font = font
}else {
changeFont(view: item, font: font)
}
}
}
open override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let font = YOUR_FONT
changeFont(view: self.view, font: font! )
}
}
I think Apple removed the attributedTitle and -message from the API. It was never part of the public API so it might be that Apple will not allow your app in the app store if you used it.
You should use the UIAlertController as is. If you want to customise it a bit see this NSHipster post. If you want more control, create a custom View to display.
let myString = "Alert Title"
var myMutableString = NSMutableAttributedString()
myMutableString = NSMutableAttributedString(string: myString as String, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 18.0)!])
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSRange(location:0,length:myString.characters.count))
alertController.setValue(myMutableString, forKey: "attributedTitle")
alertController.setValue(myMutableString, forKey: "attributedMessage")
extension UIAlertController {
func changeFont(view:UIView,font:UIFont) {
for item in view.subviews {
if item.isKindOfClass(UICollectionView) {
let col = item as! UICollectionView
for row in col.subviews{
changeFont(row, font: font)
}
}
if item.isKindOfClass(UILabel) {
let label = item as! UILabel
label.font = font
}else {
changeFont(item, font: font)
}
}
}
public override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
let font = UIFont(name: YourFontName, size: YourFontSize)
changeFont(self.view, font: font! )
}
}