FGPopupScheduler supports Cocopods, the basic components that are easy to use and efficient.


A few days ago, the test feedback was that when new users just opened the APP, because of too many pop-up Windows and the translucent guidance layer, pop-up Windows often covered each other, or even blocked the normal process. In order to solve this kind of problem, we not only need to clarify the dependence between popovers, but also need to deal with the conditions under which popovers themselves appear. And every time a new popover is added, you need to look at the logic of the previous popover. Each step costs development resources.

So what we’re trying to do is figure out how to split up the dependencies between the popovers and display them in the appropriate order at the appropriate time.

Demand analysis

The first is the popover itself

  • Popup window display
  • Popup window hidden
  • The popover shows the conditions that need to be met

And then about popovers and popovers

  • The priority of the popover
  • Whether a popover is affected by a displayed popover

One feature of pop-up display is that only one pop-up will be displayed at the same time, and it can be displayed one after another. If you use queues for management, you will naturally need to handle additional inserts, deletes, empties, traversal, and so on.

This process seems to be solved, but in reality, when all popovers are managed by a single scheduler, we have to consider when it makes more sense to show/hide them.

Of course, the FGPopupScheduler handles all of these chores, but not all of them.

Implementation analysis

Given the variety of the popover itself, it might not even be a View. Therefore, the logic abstraction processing of popup window is put into

by using the protocol. As long as the protocol is followed, the function can be uniformly managed by the scheduler.

@ protocol FGPopupView < NSObject > @ optional / * FGPopupSchedulerStrategyQueue according to - showPopupView: To monitor display logic, if contain animation please implement - showPopupViewWithAnimation: methods * / - (void) showPopupView; / * FGPopupSchedulerStrategyQueue according to - dismissPopupView: To listen to the hidden logic, if contain animation please implement - showPopupViewWithAnimation: methods * / - (void) dismissPopupView; / * FGPopupSchedulerStrategyQueue according to - showPopupViewWithAnimation: To monitor display logic * / - (void) showPopupViewWithAnimation (FGPopupViewAnimationBlock) block; / * FGPopupSchedulerStrategyQueue according to - dismissPopupView: To listen for hidden logic, If they contain animation please implement - dismissPopupViewWithAnimation: methods * / - (void) dismissPopupViewWithAnimation (FGPopupViewAnimationBlock) block; / * * FGPopupSchedulerStrategyQueue according to - canRegisterFirstPopupView judgment, when the queue order whether it can turn become the first priority PopupView response. The default value is YES * / - (BOOL) canRegisterFirstPopupViewResponder; @end

As for the order and priority of pop-up display, the actual operation will also involve inserting or removing operations in the middle of the process. The data structure is more similar to linked list, so here we use the STL standard library of C++ : list.

The specific strategies are as follows

typedef NS_ENUM(NSUInteger, FGPopupSchedulerStrategy) { FGPopupSchedulerStrategyFIFO = 1 << 0, / / fifo FGPopupSchedulerStrategyLIFO = 1 < < 1, / / lifo FGPopupSchedulerStrategyPriority = 1 < < 2 / / priority scheduling};

In fact the user can also be combined FGPopupSchedulerStrategyPriority | FGPopupSchedulerStrategyFIFO used together, to deal with when choosing priority strategy, how to decide the same priority plays the sorting of the window.

If the current hit popover fails to pass the hitTest, the next popover will be obtained from the current list for testing according to the selected scheduler policy.

- (PopupElement *)hitTestFirstPopupResponder{ list<PopupElement*>::iterator itor = _list.begin(); PopupElement *element; do { PopupElement *temp = *itor; id<FGPopupView> data = temp.data; __block BOOL canRegisterFirstPopupViewResponder = YES; if ([data respondsToSelector:@selector(canRegisterFirstPopupViewResponder)]) { dispatch_sync_main_safe(^(){ canRegisterFirstPopupViewResponder = [data canRegisterFirstPopupViewResponder]; }); } if (canRegisterFirstPopupViewResponder) { element = temp; break; } itor++; } while (itor! =_list.end()); return element; }

Since all popups are managed uniformly through the FGPopupScheduler, triggering on the popup requires the component to handle it itself. This author considers a total of three trigger cases

  • When you add popover objects
  • The RunLoop listens for the time when the main thread is idle
  • User active trigger through the above three cases, almost all the use of the scenario can be covered.

In addition, a Suspended state is added to the scheduler to actively suspend/restore popup queues, which controls whether the current scheduler can trigger a HitTest to display logic.

In addition, components support thread safety. Given that the timing of the operation may be on any thread, the component uses pthread_mutex_t to ensure thread-safety. It should be noted that the popover display process is switched to the main thread, so there is no need to do additional processing. Although pthread_mutex_t guarantees that resources will not be simultaneously used, it must ensure that the locks and unlocks are on the same thread. So the component ended up using a semaphore instead of a mutex for thread protection.

At this point, the business of the entire component is relatively clear. The FGPopupScheduler uses the state mode. The component needs to allow the three processing modes to vary freely, so the policy mode is used to handle it. Here is the UML class diagram: