Categories
iOS

Tutorial: Create a custom alert view

8 steps to your own UIAlertViewController

The UIAlertViewController is a common and simple-to-use UI component that serves for asking users to enter some data or to notify them about certain events. Unfortunately, this class cannot be customized and that’s why, in this tutorial, I will show you how to create your own completely custom UIAlertViewController.

This is what you are going to get:

The code for the project can be seen on:

https://github.com/Crystal-Pigeon/CustomAlert

These are steps you’ll have to take:

  1. Create the UI
  2. Create the class and connect the UI with the class
  3. Present the alert
  4. Cancel the alert
  5. Make the background darker
  6. Move the alert when the keyboard gets shown
  7. Enable the OK button
  8. Pass data from the alert to the ViewController

Create the UI

Create a xib file

The first step is to create a xib (XML Interface Builder) file. In this file you can design your custom UIView, similarly to how you design custom UIViewControllers through storyboards. To create this file right click on your project in the project navigator and click new file. Choose the View template and click next. Give a name to your custom alert view and click next.

Create the layout

Open the newly created xib file. Click on the view and open the Attributes Inspector. Change size from inferred to freeform. Now you can resize the view, set it so that it has approximately the intended size.

Create the layout through the interface builder. You can create whatever UI you need. I created a simple view that contains a title, a textfield, a label with a switch, and two buttons.

Create the class and connect the UI with the class

Create a swift file

To add some logic to the created view, you need to create a corresponding swift file. Right-click on your project in the project navigator and click the new file. Choose Swift file. Name this file the same as you named your xib file.

Create a UIView subclass

In that file create a subclass of UIView. This class will represent your custom alert.


import UIKit

class AlertView: UIView {
 
}

Open the xib file, click on the view and open the Identity Inspector. Set the class to the newly created class.

Now you can connect IBOutlets by dragging the views from the xib file to the swift file.


// MARK: – IBOutlets
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var configurationNameTextField: UITextField!
@IBOutlet weak var switchButton: UISwitch!
@IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var okButton: UIButton!

Create initial methods

Create a method in which you will perform the basic setup of your view. In this example the background color of the alert view is set to white, a light gray border with a width of 1 is added, and a corner radius is set to 4. Also, the okButton is initially disabled.

<code class="language-swift">
    private func setupView() {
    self.backgroundColor = .white
    self.layer.borderColor = UIColor.lightGray.cgColor
    self.layer.borderWidth = 1
    self.layer.cornerRadius = 4
    self.okButton.isEnabled = false
}
</code>

Create a class method which will be called from the ViewController where you will instantiate this alert. If you don’t know, class methods are the same as static methods, but they can be overridden by subclasses, whereas static methods can never be overridden. The reason why this method is a class method and not a regular method is that this method will be called to create a new instance, which means that it cannot be called on some instance which is the case with regular methods.

This method creates the alert from the specified nib file and calls the basic setup method. Make sure that the parameter nibName is the same as the name of your xib file. If you’re wondering what a nib(NeXTSTEP Interface Builder) is, it is essentially the same as xib, it’s just that the xib file is used while developing, whereas nib files are produced when you create a build.

<code class="language-swift">
class func instanceFromNib() -> AlertView {
    let view = UINib(nibName: "AlertView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! AlertView
    view.setupView()
    return view
} 
</code>

Present the alert

To be able to show the alert view on a certain screen we will have to create an instance of the AlertView class. Add a lazy instance of the AlertView to the ViewController that will use it.

A lazy modifier ensures that the initial value will not be calculated until the first time this property gets called. This modifier is usually used when the initial value of a property requires complex computation and therefore should not be initialized if it will not be used. That is the case with our custom alert. There is a possibility that the user will not need it.

A closure is being used to initialize this property. It may look like a computed property but this value is calculated only once, which is different in the case of using a computed property.

In the closure, we will call the instanceFromNib class method that we previously created and set the translatedAutoresizingMaskIntoConstraints property to false. This will allow us to create auto layout constraints for the alert.

<code class="language-swift">
private lazy var alertView: AlertView = {
    let view = AlertView.instanceFromNib()
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()
</code>

When you want to show the alert on the screen add it as a subview to the main view of the view controller and add constraints. I set the alert in the vertical center of the screen and put 10 point spacing from the right and left edge of the screen.

<code class="language-swift">
@IBAction func openAlert(_ sender: Any) {
      self.view.addSubview(alertView)
    NSLayoutConstraint.activate([
        alertView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10),
        alertView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10),
            alertView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0)
    ])
}
</code>
And now a simple AlertView is created.

… But the cancel button does not work.

Let’s fix it.

Cancel the alert

A great practice to make your custom views reusable in different ViewControllers is to create a delegate protocol. That way, any ViewController that conforms to that protocol will be able to use this alert. Create the delegate protocol and declare a method for removing the alert. 

<code class="language-swift">
protocol AlertViewDelegate {
    func removeAlert(sender: AlertView)
}
</code>

Add an optional delegate property to your alert class. This property has to be public because it will be initialized in the ViewController where the alert gets used.

<code class="language-swift">
// MARK: - Properties
var delegate: AlertViewDelegate?
</code> 

Drag an IBAction from the cancel button and call the removeAlert method of the delegate.

<code class="language-swift">
@IBAction func cancel(_ sender: Any) {
    self.delegate?.removeAlert(sender: self)
}
</code>  

The ViewController should implement the alertViewDelegate protocol. All you have to do is remove the alert from superview.

<code class="language-swift">
extension ViewController: AlertViewDelegate {
    func removeAlert(sender: AlertView) {
        sender.removeFromSuperview()
    }
}
</code>

Set the delegate property of the alert to self when initializing it in the ViewController.

<code class="language-swift">
private lazy var alertView: AlertView = {
    let view = AlertView.instanceFromNib()
    view.translatesAutoresizingMaskIntoConstraints = false
    view.delegate = self
    return view
}()
</code>

Ok, the cancel button now works, but would it not be nice if the background got a little darker so that the alert stands out more?

Make the background darker

Create a UIView called backgroundView in the ViewController. Set the background color and the alpha to your linking. Also set the translatesAutoresizingMaskIntoConstraints property to true, so that we can add our own constraints.

<code class="language-swift">
private lazy var backgroundView: UIView = {
    let view = UIView()
    view.backgroundColor = .black
    view.alpha = 0.5
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
}()
</code>

Just before the code for showing the alert view add this code. First we will add the background as a subview and then pin the background to the edges of the view.

<code class="language-swift">
self.view.addSubview(backgroundView)
NSLayoutConstraint.activate([
            backgroundView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0),
            backgroundView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0),
            backgroundView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0),
            backgroundView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
        ])
</code>

Also, you will have to remove the background view when the alert gets canceled. Add this line of code in the view controllers implementation of the AlertViewDelegate protocol’s method removeAlert()

<code class="language-swift">
self.backgroundView.removeFromSuperview()
</code>
Now the alert stands out much better.

But how can a user now click on cancel when the keyboard is shown?

Let’s add code to fix this case.

Move the alert when the keyboard gets shown

We could move the alert up by adding a constant to the constraint that centers the alert view vertically. But right now that constraint is not reachable anywhere in the class. Therefore, let’s create a property that will represent that constraint.

Create a constraint property

Declare a new property alertViewConstraint in the view controller.

<code class="language-swift">
var alertViewConstraint: NSLayoutConstraint!
</code>

Instantiate it in viewDidLoad method. This constraint defines that the alert is centered vertically in the view controller.

<code class="language-swift">
override func viewDidLoad() {
    super.viewDidLoad()
    self.alertViewConstraint = NSLayoutConstraint(item: self.alertView, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1, constant: 0)
}
</code>

In the method where you show the alert replace this:

<code class="language-swift">
NSLayoutConstraint.activate([
    alertView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10),
    alertView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10),
    alertView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor, constant: 0)
])
</code>

… With this:

<code class="language-swift">
NSLayoutConstraint.activate([
    alertView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10),
    alertView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10),
    alertViewConstraint
])
</code>

The alert should work the same as it did before these changes.

Move the alert view

Next, create two methods. The first one will be called when the keyboard gets shown and in this method, we will raise the alert view up by 100 points.

The second method will be called when the keyboard disappears from the screen, and the alert will be put back in its original position.

You can move the keyboard by fewer or more points, it’s up to you.

<code class="language-swift">
@objc func keyboardWillShow(notification: NSNotification) {
    self.alertViewConstraint.constant = -100
}
   
@objc func keyboardWillHide(notification: NSNotification) {
    self.alertViewConstraint.constant = 0
}
</code>

In the viewdidLoad method add observers for keyboardwillhide and keyboard will show events. And call the method that will set the tap gesture on the screen.

<code class="language-swift">
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
</code>

Hide keyboard when the user taps anywhere on the screen

Add this code to hide the keyboard when the user touches anywhere on the screen.

<code class="language-swift">
func hideKeyboardWhenTappedAround() {
    let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
    tap.cancelsTouchesInView = false
    self.view.addGestureRecognizer(tap)
    }
    
@objc func dismissKeyboard() {
    self.view.endEditing(true)
    }
</code>

In viewDidLoad method call the hidekeyboardWhenTappedAround method.

And here’s the final result you’ll get:

Enable the OK button

You should decide when the ok button should be enabled based on your business logic. In this example, the button should be enabled only when there is some text in the text field.

Firstly, extend the String class and add a new property that will check if the string is blank. The reason why I created a new property instead of using the well-known isEmpty property of String is that if the string contains just whitespaces the isEmpty property will return true and that is not what I want here.

<code class="language-swift">
extension String {
    var isBlank: Bool {
        return allSatisfy({ $0.isWhitespace })
    }
}
</code>

Secondly, create a method that will enable the button if the textfield is not blank and disable the button when the textfield is blank.

<code class="language-swift">
@objc private func editingChanged() {
    if self.configurationNameTextField.text!.isBlank {
        self.okButton.isEnabled = false
    } else {
        self.okButton.isEnabled = true
    }
}
</code>

Lastly, add this line to setupView method. Whenever any change happens to the textfield the method editingChanged will get called.

<code class="language-swift">
self.configurationNameTextField.addTarget(self, action: #selector(editingChanged), for: .editingChanged)
</code>
Now the alert view looks like this:

The last thing to do is to transfer data to the view controller.

Pass data from the alert to the ViewController

Add a new method to the delegate protocol. This method will pass data from the alert to the alert’s delegate.

<code class="language-swift">
protocol AlertViewDelegate {
    func removeAlert(sender: AlertView)
    func handleData(name: String, isAllowed: Bool)
}
</code>

Implement the method in the ViewController. In this example, the data will be shown on two labels in the ViewController.

<code class="language-swift">
func handleData(name: String, isAllowed: Bool) {
    configurationName.text = name
    if isAllowed {
        notificationsAllowedLabel.text = "Notifications are allowed"
    } else {
        notificationsAllowedLabel.text = "Notifications are not allowed"
    }
}
</code>

Drag and IBAction from the ok button in the storyboard to the class code. First, call the removeAlert method from the delegate because the alert should disappear from the screen after the user has clicked ok, and then call the handleData methods from the delegate.

<code class="language-swift">
@IBAction func ok(_ sender: Any) {
    self.delegate?.removeAlert(sender: self)
    self.delegate?.handleData(name: self.configurationNameTextField.text!, isAllowed: switchButton.isOn)
}
</code>
Congratulations! Your alert is finished!
Categories
Business

What made me a better leader

Challenges I faced in my early leadership career

I believe that most leaders when they were children did not use to say „leader“ when asked What would you like to be when you grow up?

The same was with me. As a child, I wanted to be an astronomer, but still, I focused on software engineering. Neither of those two professions specifically directed me towards leadership. However, during my schooling, I went to a competition in entrepreneurship and innovation. I was on the team with four, at the time unknown people. I did not impose myself to be a leader of the team, it happened instinctively and I led a team of people who have been professionals in areas such as economics, marketing, design… I enjoyed watching everyone working as a team to achieve one common goal – victory. Together we reached that goal, we won. Even then, not knowing it, I faced what was one of the main challenges in the leadership world. And that is how to lead people of different profiles, characteristics, and natures.

I recently read a great book on emotional intelligence in leadership written by the outstanding psychologist Dejan Zivkovic. Some of the conclusions he brought to light in his book I found extremely useful. That’s why I think is important to share some of them with you. I would like to start by highlighting the importance of leadership styles because it is these styles that can help leaders when working with different profiles of people.

  • Visionary style – When it comes to leadership, people in most cases imagine a leader as a person having this style. The feature of this style is that the leader has a vision and uses empathy to inspire people in the team to follow that vision and to believe in it. He is doing his best for that vision to be the goal of each individual, but also the goal of the whole team.
  • Affiliate style – A style in which the leader shows that he cares about the people in the team, and does his best to build a great relationship with each member individually. It’s crucial to cultivate empathy for each other among all team members. An affiliate leader inspires others with his example and shows his employees how their work contributes to achieving the goal.
  • Democratic style – Wants to involve the whole team in making important decisions, always asks for the opinion of team members trying to solve the problem together, and knows how to listen to his teammates.
  • Coaching style – A leader who is also a mentor, closely related to the profession in which he leads people. He leads others to find their strengths and weaknesses and to push their limits. „Coach“ mainly directs to what should be tried when solving problems but never tells solutions.
  • Tempo dictating style* – A leader with this style sets high standards and goals for himself and the whole team. He is focused on the quality of work and constant progress.

    *all of the mentioned styles were originally defined by Daniel Goleman.

It can look as if these styles are very similar or even the same. This is probably because when we imagine an ideal leader, we imagine him practicing each of the aforementioned styles. And I would agree with that.

As one of the founders and leaders at Crystal Pigeon, I do my best to master each of the styles above. I do this so my people can trust me, that they can enjoy while working with me, but also so that we can reach our common goal.

When I founded the company and started my career as a software engineer, I was a technical mentor to two people. In the beginning, my main task was to train and guide them technically so that they would become independent as soon as possible and so that we could distribute responsibilities as soon as possible. That’s when I got to know the coaching style. In the meantime, I realized that without a visionary style, I would not have been able to gather a team and inspire them to work with me.

As time went on, the members progressed differently. Each team member was showing individual abilities, and each of them had something to be predominantly good at.

I started to wonder how was it possible that one person, let’s call her Ruby, fails to solve problem X while a person, let’s call her Diamond, solves that problem like out of a hall. On the other hand, when a Y problem arises, Ruby enjoys solving it, while the same is unbelievably boring and stressful for Diamond. I realized that the key is the diversity in personalities.

Imagine that on the one hand, you are leading Diamond, an extremely talented person who needs to be directed towards the goal. At the same time, you are leading Ruby, a very creative person, who knows what the goal is and does her best to reach that goal. She is persistent but lacks a lot of technical experience and psychological support for personal development. If these two people were on the same team, working on the same projects, how would you balance their progress and task completion?

This is when my challenges on the path to a good leader begun.

I intuitively started my leadership career with a coaching style, and as time passed by, it became a sort of a habit for me.

The coaching-style started to lose its effect with Ruby because she often wandered to find a solution. That resulted in losing will, motivation, and results getting weaker. With Diamond, the coaching style continued to be effective as she progressed at her own pace. Still, I decided to try a democratic style within the whole team and let them make decisions for the problems that are in front of them. I wanted to encourage their creativity, and that turned out well at first because they got the impression that they were a part of what they were creating. They felt like they are not only there to solve the problem but to contribute with their ideas and decisions.

However, the problem with this approach was that sometimes the team was afraid because of the presumption that the leader was not sure of his decisions and therefore leaves it to the team to make a decision. Besides that, at times it was very stressful for the team to make decisions. I figured it was necessary to adjust style usage, so I used the democratic style only on adequate occasions. The tempo-dictating style proved to be great in moments when there was a lot of work to do. It allows the team to see and feel the progress, and each individual feels his value. This style should be applied in moderation, as it can often be stressful and lead the burnout.

If I said that the solution came of its own accord, I would be lying. It took lots of experience in leading, and we had to go through all these situations together, as a team, to realize what the solution is – applying different styles, at different times, and adjusting them to different people. Even today, I study and work on choosing the right style at the right time. I often succeed, but I still make mistakes.

However, besides the two people I mentioned above, there are other Crystals in our company that I work with and have to maintain a good and friendly relationship with. When I started to bring in people from the other fields I was not able to mentor them, because I did not have enough experience in their field. That is the time when you have to make decisions jointly. I had to be able to direct them towards the goal and then listen to them and believe they are the ones who will find a solution to any problem that arises in front of them.

I am not a leader with a lifetime of experience behind me, but the one thing I know is that every leader must master the visionary and affiliative style, and should constantly apply and improve them. The leader must be there for his people, take care of them, and sacrifice. That’s how he gains trust and how he gets the same from his people. A leader must be a visionary, he must know the path he leads his people so that they feel safe and see clearly where they are traveling.


Lead your team so that everyone enjoys and progresses because after all, that is the most important thing of them all.