cdts/xdts-ios 3/TreeHole/Code/Base/BXYStatefulViewController/BXYViewStateMachine.m
2023-07-27 09:20:00 +08:00

185 lines
5.4 KiB
Objective-C
Executable File

//
// BXYViewStateMachine.m
// BXYStatefulViewController
//
// Created by baixiangyu on 2017/9/20.
//
#import "BXYViewStateMachine.h"
#import "BXYStatefulPlaceHolderView.h"
@interface BXYViewStateMachine ()
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, UIView *> *viewStore;
@property (nonatomic, strong) dispatch_queue_t queue;
@property (nonatomic, strong, readwrite) UIView *containerView;
@property (nonatomic, assign, readwrite) ViewStateMachineState currentState;
@property (nonatomic, assign, readwrite) ViewStateMachineState lastState;
@end
@implementation BXYViewStateMachine
#pragma mark - init
- (instancetype)initWithView:(UIView *)view {
self = [super init];
if (self) {
_viewStore = [[NSMutableDictionary alloc] init];
_queue = dispatch_queue_create("com.baixiangyu.viewStateMachine.queue", DISPATCH_QUEUE_SERIAL);
_view = view;
_currentState = ViewStateMachineStateNone;
_lastState = ViewStateMachineStateNone;
}
return self;
}
#pragma mark - add and remove view states
- (UIView *)viewForState:(ViewStateMachineState)state {
return _viewStore[@(state)];
}
- (void)addView:(UIView *)view forState:(ViewStateMachineState)state {
_viewStore[@(state)] = view;
}
- (void)removeViewForState:(ViewStateMachineState)state {
_viewStore[@(state)] = nil;
}
#pragma mark - Switch view state
- (void)transitionToState:(ViewStateMachineState)state
animated:(BOOL)animated
completion:(void(^)(void))completion {
_lastState = state;
__weak __typeof(self)weakSelf = self;
dispatch_async(_queue, ^{
__strong __typeof__(weakSelf) strongSelf = weakSelf;
if (state == strongSelf.currentState) {
return;
}
// Suspend the queue, it will be resumed in the completion block
dispatch_suspend(strongSelf.queue);
strongSelf.currentState = state;
void(^c)(void) = ^{
dispatch_resume(strongSelf.queue);
if (completion) {
completion();
}
};
// Switch state and update the view
dispatch_async(dispatch_get_main_queue(), ^{
if (state == ViewStateMachineStateNone) {
[strongSelf hideAllViews:animated completion:c];
} else {
[strongSelf showViewForState:state animated:animated completion:c];
}
});
});
}
#pragma mark - private methods
- (void)showViewForState:(ViewStateMachineState)state
animated:(BOOL)animated
completion:(void(^)(void))completion {
// Add the container view
self.containerView.frame = self.view.bounds;
[self.view addSubview:self.containerView];
NSMutableDictionary *store = self.viewStore;
UIView *newView = store[@(state)];
if (newView) {
newView.alpha = animated ? 0.0 : 1.0;
UIEdgeInsets insets;
if ([newView respondsToSelector:@selector(placeholderViewInsets)]) {
insets = [(id<BXYStatefulPlaceHolderView>)newView placeholderViewInsets];
} else {
insets = UIEdgeInsetsZero;
}
CGRect result = UIEdgeInsetsInsetRect(self.containerView.bounds, insets);
newView.frame = result;
[self.containerView addSubview:newView];
}
void(^animations)(void) = ^{
if (newView == store[@(state)]) {
newView.alpha = 1.0;
}
};
void(^animationCompletion)(BOOL) = ^(BOOL finished){
[store enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, UIView *view, BOOL * _Nonnull stop) {
if (key.integerValue != state) {
[view removeFromSuperview];
}
}];
if (completion) {
completion();
}
};
[self animateChanges:animated animations:animations completion:animationCompletion];
}
- (void)hideAllViews:(BOOL)animated completion:(void(^)(void))completion {
NSMutableDictionary *store = self.viewStore;
void(^animations)(void) = ^{
[store enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, UIView *view, BOOL * _Nonnull stop) {
view.alpha = 0.0;
}];
};
void(^animationCompletion)(BOOL) = ^(BOOL finished){
[store enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, UIView *view, BOOL * _Nonnull stop) {
[view removeFromSuperview];
}];
// Remove the container view
[self.containerView removeFromSuperview];
if (completion) {
completion();
}
};
[self animateChanges:animated animations:animations completion:animationCompletion];
}
- (void)animateChanges:(BOOL)animated animations:(void(^)(void))animations completion:(void(^)(BOOL finished))completion {
if (animated) {
[UIView animateWithDuration:0.25 animations:animations completion:completion];
} else {
if (completion) {
completion(YES);
}
}
}
#pragma mark - containerView
- (UIView *)containerView {
if (_containerView == nil) {
_containerView = [[UIView alloc] init];
_containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_containerView.backgroundColor = [UIColor clearColor];
}
return _containerView;
}
@end