📜 ⬆️ ⬇️

Making a beautiful Progress Bar in an iOS application

Good afternoon, dear Habrahabr!

In this article I want to describe the way with which we made such a beautiful custom progress bar - in the illustration - in one of the latest projects.

The task was set as follows:
')

Interested in the implementation of asking under the cat.

First of all, create a BSBeautifulProgressBarManager singleton and set it up in BSBeautifulProgressBarManager.h :

Push me!
#import <Foundation/Foundation.h> #define kShouldShowBeautifulProgressBar @"kShouldShowBeautifulProgressBar" #define kShouldHideBeautifulProgressBar @"kShouldHideBeautifulProgressBar" #define beautifulProgressBarManager [BSBeautifulProgressBarManager sharedManager] @interface BSBeautifulProgressBarManager : NSObject + (BSBeautifulProgressBarManager *)sharedManager; @end 

At work, we often use the define trick to get rid of the constant appeal to singletons by class name. Here, instead of [BSBeautifulProgressBarManager sharedManager], we can write just BSBeautifulProgressBarManager. We will not touch the header file anymore.

Defaults of notifications, we usually place in some Config.h - then you do not need to import the manager's header to send notifications. However, in this example, we will add a trick with getting rid of magic constants directly into the singleton header.

Referring to the implementation of a singleton in the BSBeautifulProgressBarManager.m file:

Push me!
 + (BSBeautifulProgressBarManager *)sharedManager { static BSBeautifulProgressBarManager *sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedManager = [self new]; [[NSNotificationCenter defaultCenter] addObserver:sharedManager selector:@selector(showProgressBar) name:kShouldShowBeautifulProgressBar object:nil]; [[NSNotificationCenter defaultCenter] addObserver:sharedManager selector:@selector(hideProgressBar) name:kShouldHideBeautifulProgressBar object:nil]; }); return sharedManager; } 

Nothing much has happened yet - the usual implementation of thread-safe singleton. The only remarkable thing in this method is the subscription to the progress bar'a show / hide notifications at the NSNotificationCenter.

Let's proceed to the implementation of the showProgressBar and hideProgressBar methods:

Push me!
 - (void)showProgressBar { if (isShown) return; isShown = YES; } - (void)hideProgressBar { if (!isShown) return; isShown = NO; } 

So far, they do nothing. However, we have already fulfilled two working conditions for the element: the progress bar will be only one, and it will be called via the NSNotificationCenter. Accordingly, we will add a private variable to the implementation:

Push me!
 @implementation BSBeautifulProgressBarManager { BOOL isShown; } 

Our idea will be this:


Let's start the implementation! First, change the showProgressBar method:

Push me!
 - (void)showProgressBar { if (isShown) return; isShown = YES; [self addGreyView]; [self addWhiteView]; [self addGreyZip]; [self addOrangeZip]; } 

Everything goes according to plan - we encapsulated the functionality of adding elements to separate methods. Let's go through these methods, I will give the comments below:

Push me!
 @implementation BSBeautifulProgressBarManager { BOOL isShown; // 1 UIView *mainView; UIView *whiteView; } <...> - (void)addGreyView { // 2 UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject]; mainView = [[UIView alloc] initWithFrame:window.bounds]; mainView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5]; mainView.alpha = 0.0; [window addSubview:mainView]; // 3 [UIView animateWithDuration:0.3 animations:^{ mainView.alpha = 1.0; }]; } - (void)addWhiteView { // 4 whiteView = [[UIView alloc] initWithFrame:CGRectMake(0, -64, 320, 64)]; whiteView.backgroundColor = [UIColor whiteColor]; [mainView addSubview:whiteView]; // 5 [UIView animateWithDuration:0.3 animations:^{ CGRect frame = whiteView.frame; frame.origin.y = 0; whiteView.frame = frame; }]; } - (void)addGreyZip { // 6 CGRect frame = CGRectMake(0, 39, 320, 14.5); UIImageView *greyImageView = [[UIImageView alloc] initWithFrame:frame]; greyImageView.image = [UIImage imageNamed:@"grey"]; [whiteView addSubview:greyImageView]; } - (void)addOrangeZip { // 7 CGRect frame = CGRectMake(0, 0, 320, 14.5); CGRect frameSmaller = CGRectMake(1, 39, 0, 14.5); // 8 UIView *container = [[UIView alloc] initWithFrame:frameSmaller]; container.clipsToBounds = YES; // 9 UIImageView *redImageView = [[UIImageView alloc] initWithFrame:frame]; redImageView.image = [UIImage imageNamed:@"red"]; [container addSubview:redImageView]; // 10 [whiteView addSubview:container]; // 11 [UIView animateWithDuration:15. animations:^{ CGRect frame = container.frame; frame.size.width = 320; container.frame = frame; }]; } 

Let's go in order:

  1. Add links to the background (mainView) and to the white view (whiteView). We will need to show and hide the background (fade-in and fade-out), and the white view must first be beautifully lowered and then beautifully raised.
  2. Create our background, which will be the main superview of the element, make it black, semi-transparent, and put it on the application window in order to block the entire UI.
  3. We make fade-in for our background, and with it and for all its descendants - the rest of the element.
  4. Create our white view background for the remaining elements, put it on the main view, hide it behind the upper edges of the ancestor.
  5. Beautifully, animatedly lowering the white view with all descendants down. By the way, descendants will be lightning.
  6. Add a static picture of a gray lightning on a white view - nothing complicated.
  7. Orange lightning is harder. We create two views - a UIView container and a UIImageView lightning. The idea is this: add a UIImageView inside the view with a width of 0 and animate the container to the desired size. We get a certain effect of orange lightning “growing” to the right. To do this, we prepare two frames: for the picture (frame) and container (frameSmaller).
  8. We create a container and make it so that its descendants do not go beyond.
  9. Add a picture of an orange lightning into a container - nothing supernatural.
  10. Add our container to the white view.
  11. We animate the container extension.

Wonderful! Now we can show the progress bar when needed. Let us turn to the implementation of the methods of hiding the progress bar:

Push me!
 - (void)hideProgressBar { if (!isShown) return; isShown = NO; [self hideGreyView]; [self hideWhiteView]; [self finish]; } 

The idea is simple: we hide the main view (black-translucent or, simply, gray), at this time we remove the white view with all its insides, and even orange lightning is brought to the end instantly - because if it is not expanded to the end, it means bring to show the user, they say, the action is completed.

We proceed to the implementation, comments below:

Push me!
 - (void)hideGreyView { // 1 [UIView animateWithDuration:0.3 animations:^{ mainView.alpha = 0.0; } completion:^(BOOL finished){ [mainView removeFromSuperview]; }]; } - (void)hideWhiteView { // 2 [UIView animateWithDuration:0.3 animations:^{ CGRect frame = whiteView.frame; frame.origin.y = -64; whiteView.frame = frame; }]; } - (void)finish { // 3 CGRect frame = CGRectMake(0, 39, 320, 14.5); UIImageView *greyImageView = [[UIImageView alloc] initWithFrame:frame]; greyImageView.backgroundColor = [UIColor whiteColor]; greyImageView.image = [UIImage imageNamed:@"grey"]; [whiteView addSubview:greyImageView]; // 4 frame = CGRectMake(1, 39, 320, 14.5); UIImageView *redImageView = [[UIImageView alloc] initWithFrame:frame]; redImageView.image = [UIImage imageNamed:@"red"]; [whiteView addSubview:redImageView]; } 

It's simple:

  1. Make a fade-out with the removal from the ancestor for the main view and all its viscera, respectively.
  2. We remove up the white view with all the entrails
  3. The most cruel and “vlob” method - we put a new gray zipper on top
  4. Put a new orange zipper on top - it will create the feeling that the zipper hidden from below has disappeared

By the way, here are the pictures you need:

Push me!



That's all! You have learned a piece of real, working social network. In fact, they plunged into the code from the production. We can call a custom, beautiful progress bar by sending the necessary notifications.

Conclusion


Thank you for reading to the end! I will be happy to answer any questions you may have. If you have found any typos or inaccuracies, feel free to write to my Habracecenter!

Be sure to write what you want to see in the next article.

Source: https://habr.com/ru/post/231117/


All Articles