add this class to your project
import UIKit
class CustomAlertController:UIViewController {
private var cornerRadius: CGFloat = 8
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.black.withAlphaComponent(0.2)
setupLayout()
}
//MARK: - setupLayout
private func setupLayout() {
// add alertView to view
view.addSubview(alertView)
// add UI Element to alertView
alertView.addSubview(messageTitle)
alertView.addSubview(lineUnderTitleIfNeeded)
alertView.addSubview(containerView)
alertView.addSubview(lineViewBeforeButtons)
alertView.addSubview(actionButtonsContainer)
NSLayoutConstraint.activate([
// set constraints to set alertView in center of view
// centerX and centerY of view
// 250 width
alertView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
alertView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
alertView.widthAnchor.constraint(equalToConstant: 250),
// set constraints to set messageTitle in alertView
// 20 from top of alertView
// 20 from leading trailing
// hight constraint should be dynamic
messageTitle.topAnchor.constraint(equalTo: alertView.topAnchor, constant: 20),
messageTitle.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: 20),
messageTitle.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: -20),
// constraint from top of messageTitle is 0 , from leading and trailing of alertView 0
lineUnderTitleIfNeeded.topAnchor.constraint(equalTo: messageTitle.bottomAnchor, constant: 0),
lineUnderTitleIfNeeded.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: 0),
lineUnderTitleIfNeeded.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: 0),
lineUnderTitleIfNeeded.heightAnchor.constraint(equalToConstant: 0.0) ,// adjust hight if need line under a title
// set constraints for a containerView
// from top with lineUnderTitleIfNeeded
// set leading and trailing is 20 with a alertView
containerView.topAnchor.constraint(equalTo: lineUnderTitleIfNeeded.topAnchor, constant: 8),
containerView.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: 20),
containerView.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: -20),
// Set constraints
// 16 from top of containerView
// 0 from leading and trailing of alertView ,
// height is 0.3
lineViewBeforeButtons.topAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 16),
lineViewBeforeButtons.leadingAnchor.constraint(equalTo: alertView.leadingAnchor ,constant: 0),
lineViewBeforeButtons.trailingAnchor.constraint(equalTo: alertView.trailingAnchor,constant: 0),
lineViewBeforeButtons.heightAnchor.constraint(equalToConstant: 0.3),
// Set constraints
// 0.4 from top of containerView
// 0 from leading and trailing of alertView ,
// -1 from bottom of alertView
actionButtonsContainer.topAnchor.constraint(equalTo: lineViewBeforeButtons.bottomAnchor, constant: 0.4),
actionButtonsContainer.leadingAnchor.constraint(equalTo: alertView.leadingAnchor, constant: 0),
actionButtonsContainer.trailingAnchor.constraint(equalTo: alertView.trailingAnchor, constant: 0),
actionButtonsContainer.bottomAnchor.constraint(equalTo: alertView.bottomAnchor, constant: -1)
])
}
// MARK: - show Alert
func showAlert() {
modalPresentationStyle = .overFullScreen
view.alpha = 0.0
// Get the current scene's key window
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let viewController = windowScene.windows.first?.rootViewController {
viewController.present(self, animated: false) {
UIView.animate(withDuration: 0.2) {
self.view.alpha = 1.0
}
}
}
}
func setTitleWithMessage(title:String , message: String) {
self.messageTitle.text = title
self.messageText.text = message
//add Message To Container View
containerView.addSubview(messageText)
// set constraints for a customView
// 0 from top ,leading , trailing and bottom of containerView
NSLayoutConstraint.activate([
messageText.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0),
messageText.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 0),
messageText.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0),
messageText.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0)
])
}
func setTitleWithCustomView(title:String , customView:UIView){
self.messageTitle.text = title
//add customView To Container View
containerView.addSubview(customView)
// set constraints for a customView
// 0 from top ,leading , trailing and bottom of containerView
NSLayoutConstraint.activate([
customView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 0),
customView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 0),
customView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0),
customView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: 0)
])
}
func setTitleAttribute(font:UIFont,textColor:UIColor = .black){
messageTitle.font = font
messageTitle.textColor = textColor
}
func setMessageAttribute(font:UIFont,textColor:UIColor = .black){
messageText.font = font
messageText.textColor = textColor
}
// MARK: - Custom Action
typealias AlertActionHandler = () -> Void
private var actions: [AlertAction] = []
struct AlertAction {
let title: String
let handler: AlertActionHandler?
}
func addAction(_ action:AlertAction, font: UIFont, textColor: UIColor = .black){
actions.append(action)
let hasLongTitles = actions.contains { $0.title.count > 8 }
if hasLongTitles { actionButtonsContainer.axis = .vertical
} else { actionButtonsContainer.axis = .horizontal }
let alertActionButton = UIButton(type: .system)
alertActionButton.setTitle(action.title, for: .normal)
alertActionButton.titleLabel?.font = font
alertActionButton.setTitleColor(textColor, for: .normal)
alertActionButton.backgroundColor = .white
alertActionButton.addTarget(self, action: #selector(actionButtonTapped(_:)), for: .touchUpInside)
alertActionButton.heightAnchor.constraint(equalToConstant: 44).isActive = true
actionButtonsContainer.addArrangedSubview(alertActionButton)
}
@objc private func actionButtonTapped(_ sender: UIButton) {
guard let index = actionButtonsContainer.arrangedSubviews.firstIndex(of: sender) else { return }
let action = actions[index]
action.handler?()
// Fade out animation
UIView.animate(withDuration: 0.2, animations: {
self.view.alpha = 0.0
}) { _ in
self.dismiss(animated: false, completion: nil)
}
}
//MARK: - UI Element
// View for the alert
private lazy var alertView :UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.cornerRadius = cornerRadius
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// Title label for the alert
private var messageTitle: UILabel = {
let label = UILabel()
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 18)
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
// Container view for the alert content
private var containerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// Message label for the alert
var messageText: UILabel = {
let label = UILabel()
label.numberOfLines = 0
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
// Stack view for action buttons
private lazy var actionButtonsContainer: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.backgroundColor = .gray
stackView.distribution = .fillEqually
stackView.spacing = 0.2
stackView.clipsToBounds = true
stackView.layer.cornerRadius = cornerRadius
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private lazy var lineUnderTitleIfNeeded: UIView = {
let view = UIView()
view.backgroundColor = .gray
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var lineViewBeforeButtons: UIView = {
let view = UIView()
view.backgroundColor = .gray // Customize line color
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
}
usage
@IBAction func showAlert(_ sender: Any) {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 210, height: 200))
view.backgroundColor = .red
let alert = CustomAlertController()
alert.setTitleWithMessage(title: "title", message: "test message test message test message test message ")
alert.setTitleAttribute(font: UIFont(name: "Arial", size: 24)!,textColor: .red)
alert.setMessageAttribute(font: UIFont(name: "Arial", size: 22)!, textColor: .green)
let customAction = CustomAlertController.AlertAction(title: "Action1") {
print("Custom action 1 tapped")
}
let customAction2 = CustomAlertController.AlertAction(title: "Actssdddewo") {
print("Custom action 2 tapped")
}
alert.addAction(customAction, font: UIFont(name: "Arial", size: 18)!,textColor: .blue)
alert.addAction(customAction2,font: UIFont(name: "Arial", size: 18)!,textColor: .brown)
alert.showAlert()
}
result
enter image description here
UIAlertView
since iOS 7. As stated in the documentation: The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified. – Decarbonate