Browse Source

1.修改Apex Mobile目录结构。

Pen Li 8 years ago
parent
commit
751f484f10

+ 13 - 0
common/customUI/Alert/JLCustomerAlertController.h

@@ -0,0 +1,13 @@
+//
+//  JLAlertController.h
+//  Apex Mobile
+//
+//  Created by Jack on 2018/1/31.
+//  Copyright © 2018年 United Software Applications, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface JLCustomerAlertController : UIViewController
+
+@end

+ 86 - 0
common/customUI/Alert/JLCustomerAlertController.m

@@ -0,0 +1,86 @@
+//
+//  JLAlertController.m
+//  Apex Mobile
+//
+//  Created by Jack on 2018/1/31.
+//  Copyright © 2018年 United Software Applications, Inc. All rights reserved.
+//
+
+#import "JLCustomerAlertController.h"
+#import "JLPresentationController.h"
+
+@interface JLCustomerAlertController () <UIViewControllerTransitioningDelegate>
+
+@end
+
+@implementation JLCustomerAlertController
+
+- (void)comonInit {
+    // present
+    self.transitioningDelegate = self;
+    /**
+     * 非Customer时,
+     * Present会将FromVC.view从Window移除
+     * Dismiss动画结束后VC.view自动添加到Window
+     */
+    self.modalPresentationStyle = UIModalPresentationCustom;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+    self = [super initWithCoder:coder];
+    if (self) {
+        [self comonInit];
+    }
+    return self;
+}
+
+- (instancetype)init {
+    if (self = [super init]) {
+        [self comonInit];
+    }
+    return self;
+}
+
+- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
+    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
+        [self comonInit];
+    }
+    return self;
+}
+
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+    
+    self.view.backgroundColor = [UIColor whiteColor];
+}
+
+- (void)didReceiveMemoryWarning {
+    [super didReceiveMemoryWarning];
+    // Dispose of any resources that can be recreated.
+}
+
+#pragma mark - UIViewControllerTransitioningDelegate
+// ios 8
+- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source {
+    return [[JLPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
+}
+
+#pragma mark - Animator
+
+// present
+- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
+    
+    return nil;
+}
+
+// dismiss
+- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
+   
+    return nil;
+    
+}
+
+
+@end

+ 13 - 0
common/customUI/Alert/JLCustomerNavigationAlertController.h

@@ -0,0 +1,13 @@
+//
+//  JLCustomerNavigationAlertController.h
+//  Apex Mobile
+//
+//  Created by Jack on 2018/1/31.
+//  Copyright © 2018年 United Software Applications, Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface JLCustomerNavigationAlertController : UINavigationController
+
+@end

+ 89 - 0
common/customUI/Alert/JLCustomerNavigationAlertController.m

@@ -0,0 +1,89 @@
+//
+//  JLCustomerNavigationAlertController.m
+//  Apex Mobile
+//
+//  Created by Jack on 2018/1/31.
+//  Copyright © 2018年 United Software Applications, Inc. All rights reserved.
+//
+
+#import "JLCustomerNavigationAlertController.h"
+#import "JLPresentationController.h"
+
+@interface JLCustomerNavigationAlertController () <UIViewControllerTransitioningDelegate>
+
+@end
+
+@implementation JLCustomerNavigationAlertController
+
+- (void)comonInit {
+    // present
+    self.transitioningDelegate = self;
+    /**
+     * 非Customer时,
+     * Present会将FromVC.view从Window移除
+     * Dismiss动画结束后VC.view自动添加到Window
+     */
+    self.modalPresentationStyle = UIModalPresentationCustom;
+}
+
+- (instancetype)initWithCoder:(NSCoder *)coder {
+    self = [super initWithCoder:coder];
+    if (self) {
+        [self comonInit];
+    }
+    return self;
+}
+
+- (instancetype)init {
+    if (self = [super init]) {
+        [self comonInit];
+    }
+    return self;
+}
+
+- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
+    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
+        [self comonInit];
+    }
+    return self;
+}
+
+- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {
+    if (self = [super initWithRootViewController:rootViewController]) {
+        [self comonInit];
+    }
+    return self;
+}
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    // Do any additional setup after loading the view.
+}
+
+- (void)didReceiveMemoryWarning {
+    [super didReceiveMemoryWarning];
+    // Dispose of any resources that can be recreated.
+}
+
+#pragma mark - UIViewControllerTransitioningDelegate
+// ios 8
+- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source {
+    return [[JLPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
+}
+
+#pragma mark - Animator
+
+// present
+- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
+    
+    return nil;
+}
+
+// dismiss
+- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
+    
+    return nil;
+    
+}
+
+@end

+ 13 - 0
common/customUI/Alert/JLPresentationController.h

@@ -0,0 +1,13 @@
+//
+//  JLPresentationController.h
+//  JLTransitionDemo
+//
+//  Created by Jack on 2017/11/20.
+//  Copyright © 2017年 buakaw. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface JLPresentationController : UIPresentationController
+
+@end

+ 96 - 0
common/customUI/Alert/JLPresentationController.m

@@ -0,0 +1,96 @@
+//
+//  JLPresentationController.m
+//  JLTransitionDemo
+//
+//  Created by Jack on 2017/11/20.
+//  Copyright © 2017年 buakaw. All rights reserved.
+//
+
+#import "JLPresentationController.h"
+
+@interface JLPresentationController ()
+
+@property (nonatomic,strong) UIView *dimmingView;
+
+@end
+
+@implementation JLPresentationController
+
+- (UIView *)dimmingView {
+    if (!_dimmingView) {
+        _dimmingView = [UIView new];
+        _dimmingView.backgroundColor = [UIColor grayColor];
+    }
+    return _dimmingView;
+}
+
+- (void)presentationTransitionWillBegin {
+    self.dimmingView.frame = self.containerView.bounds;
+    self.dimmingView.alpha = 0.f;
+    
+    self.presentedView.layer.cornerRadius = 10.0f;
+    self.presentedView.layer.masksToBounds = YES;
+    
+    [self.containerView addSubview:self.dimmingView];
+    [self.containerView addSubview:self.presentedView];
+    
+    // 确保我们的动画与其他动画一道儿播放。
+    id<UIViewControllerTransitionCoordinator> transitionCoordiantor = self.presentedViewController.transitionCoordinator;
+    [transitionCoordiantor animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
+        self.dimmingView.alpha = .4f;
+    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
+        
+    }];
+}
+
+- (void)presentationTransitionDidEnd:(BOOL)completed {
+    if (completed) {
+//        [self.dimmingView removeFromSuperview];
+    }
+}
+
+- (CGRect)frameOfPresentedViewInContainerView {
+    CGSize size = self.containerView.bounds.size;
+    
+    UIViewController *presentedVC = self.presentedViewController;
+    size = presentedVC.preferredContentSize;
+    if (size.width <= 0 || size.height <= 0) {
+        size = self.containerView.bounds.size;
+    }
+    
+    CGFloat w = CGRectGetWidth(self.containerView.bounds);
+    CGFloat h = CGRectGetHeight(self.containerView.bounds);
+    
+    return CGRectMake((w - size.width) * 0.5, (h - size.height) * 0.5, size.width, size.height);
+}
+
+- (void)dismissalTransitionWillBegin {
+    // 确保我们的动画与其他动画一道儿播放。
+    id<UIViewControllerTransitionCoordinator> transitionCoordiantor = self.presentedViewController.transitionCoordinator;
+    [transitionCoordiantor animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
+        self.dimmingView.alpha = 0.f;
+    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
+        
+    }];
+}
+
+- (void)dismissalTransitionDidEnd:(BOOL)completed {
+    if (completed) {
+//        [self.dimmingView removeFromSuperview];
+    }
+}
+
+// 屏幕旋转调用此方法
+- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
+    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
+    
+    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
+        self.dimmingView.frame = self.containerView.bounds;
+        self.presentedView.frame = [self frameOfPresentedViewInContainerView];
+    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
+        
+    }];
+}
+
+
+@end

+ 22 - 0
common/customUI/Refresh/Footer/JLRefreshFooter.h

@@ -0,0 +1,22 @@
+//
+//  JLRefreshFooter.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/7.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshBasis.h"
+
+UIKIT_EXTERN CGFloat const JLRefreshFooterHeight;
+
+@interface JLRefreshFooter : JLRefreshBasis
+
+@property (nonatomic,assign) BOOL showDefaultTips;
+
+- (void)noMoreData;
+- (void)setRefreshTitle:(NSString *)title forState:(JLRefreshState)state;
+- (NSString *)refreshTitleForState:(JLRefreshState)state;
+- (void)showTip;
+
+@end

+ 326 - 0
common/customUI/Refresh/Footer/JLRefreshFooter.m

@@ -0,0 +1,326 @@
+//
+//  JLRefreshFooter.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/7.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshFooter.h"
+
+CGFloat const JLRefreshFooterHeight = 30;
+
+@interface JLRefreshFooter ()
+
+@property (nonatomic,strong) NSMutableDictionary *stateTitle;
+@property (nonatomic,strong) UILabel *refreshTitleLabel;
+@property (nonatomic,assign) BOOL stopWithNomore;
+
+@end
+
+@implementation JLRefreshFooter
+
+- (void)prepareInterface {
+    [super prepareInterface];
+    
+    self.jl_height = JLRefreshFooterHeight;
+}
+
+#pragma mark - SubView
+
+- (UILabel *)refreshTitleLabel {
+    if (!_refreshTitleLabel) {
+        _refreshTitleLabel = [[UILabel alloc] init];
+        _refreshTitleLabel.textAlignment = NSTextAlignmentCenter;
+        _refreshTitleLabel.font = [UIFont systemFontOfSize:14.0f];
+//        _refreshTitleLabel.hidden = YES;
+    }
+    return _refreshTitleLabel;
+}
+
+#pragma mark - Private
+
+- (BOOL)contentSizeIsOutOfScrollViewBounds {
+    // top + content > height
+    return self.scrollView.jl_insetTop + self.scrollView.jl_ContentHeight > self.scrollView.jl_height;
+}
+
+- (NSMutableDictionary *)stateTitle {
+    if (!_stateTitle) {
+        _stateTitle = [@{
+                         @(JLRefreshStateIdle)          : @"pull to load more",
+                         @(JLRefreshStatePulling)       : @"pull to load more",
+                         @(JLRefreshStateWillRefresh)   : @"loosen to load more",
+                         @(JLRefreshStateRefreshing)    : @"loading...",
+                         @(JLRefreshStateNoMore)        : @"no more"
+                         } mutableCopy];
+    }
+    return _stateTitle;
+}
+
+- (void)setRefreshTitleForState:(JLRefreshState)state {
+    if (!self.clear) {
+        self.refreshTitleLabel.text = [self refreshTitleForState:state];
+        [self setNeedsLayout];
+    }
+}
+
+
+#pragma mark - Override
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    
+    
+    [self.refreshTitleLabel sizeToFit];
+    CGRect frame = self.refreshTitleLabel.bounds;
+    CGFloat Y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(frame)) * 0.5;
+    CGFloat X = (CGRectGetWidth(self.bounds) - CGRectGetWidth(frame)) * 0.5;
+    self.refreshTitleLabel.jl_y = Y;
+    self.refreshTitleLabel.jl_x = X;
+    
+    [self addSubview:self.refreshTitleLabel];
+    
+}
+
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+    
+    [super willMoveToSuperview:newSuperview];
+    
+    if (newSuperview) { // add
+        
+        self.scrollView.jl_insetBottom += self.jl_height;
+        
+//        self.jl_y = self.scrollView.jl_ContentHeight;
+        // 设置位置
+        self.jl_y = (((int)(self.scrollView.jl_ContentHeight / self.scrollView.jl_height)) + 1) * self.scrollView.jl_height;
+    
+    } else { // remove
+        
+        self.scrollView.jl_insetBottom -= self.jl_height;
+        
+    }
+    
+}
+
+- (void)scrollViewContentSizeDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+    [super scrollViewContentSizeDidChange:change];
+    
+//    self.jl_y = self.scrollView.jl_ContentHeight;
+    // 设置位置
+//    if (self.scrollView.jl_ContentHeight >= self.scrollView.jl_height) {
+//        self.jl_y = self.scrollView.jl_ContentHeight;
+//    } else {
+//        self.jl_y = self.scrollView.jl_height;
+//    }
+    
+    self.jl_y = (((int)(self.scrollView.jl_ContentHeight / self.scrollView.jl_height)) + 1) * self.scrollView.jl_height;
+    
+    if (self.offsetBlock) {
+        self.offsetBlock(0);
+    }
+}
+
+- (void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+    [super scrollViewContentOffsetDidChange:change];
+
+    if (self.offsetBlock) {
+        self.offsetBlock(self.scrollView.jl_ContentHeight - self.scrollView.jl_offsetY - self.scrollView.jl_height);
+    }
+    
+    if (self.state == JLRefreshStateRefreshing) {
+        return;
+    }
+    
+    CGFloat offsetY = self.scrollView.jl_offsetY + self.scrollView.jl_height;
+    CGFloat startY = self.scrollView.jl_insetBottom + self.scrollView.jl_ContentHeight - self.jl_height;
+    CGFloat idle2WillRefresh = startY + self.jl_height * 1.5;
+    
+    if ([self contentSizeIsOutOfScrollViewBounds]) {
+        
+        
+        if (offsetY < startY) {
+            return;
+        }
+       
+        
+        if (self.scrollView.isDragging) {
+            if (self.state == JLRefreshStateNoMore) {
+                
+                self.state = JLRefreshStateIdle;
+                
+            } else if (self.state == JLRefreshStateIdle) { // 1
+                
+                self.state = JLRefreshStatePulling; // 2
+                
+            } else if (self.state == JLRefreshStatePulling && offsetY < startY) {
+                
+                self.state = JLRefreshStateIdle;
+                
+            } else if (self.state == JLRefreshStatePulling && offsetY >= idle2WillRefresh) { // 完全显示出来才刷新
+                
+                self.state = JLRefreshStateWillRefresh; // 4
+                
+            }
+            
+           
+            
+        } else {
+            
+            // 停止拖动的减速过程
+            if (self.state != JLRefreshStateRefreshing && offsetY >= idle2WillRefresh) {
+                self.state = JLRefreshStateRefreshing;
+            } else {
+                if (self.stopWithNomore) {
+                    [self setState:JLRefreshStateNoMore];
+                }
+                if (self.state != JLRefreshStateNoMore) {
+                    self.state = JLRefreshStateIdle;
+                }
+            }
+            
+        }
+        
+        float percentage = (offsetY - startY) / self.jl_height;
+        if (percentage < 0) {
+            percentage = 0;
+        }
+        self.pullPercentage = percentage;
+        
+        [self showTip];
+        if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(jl_pullRefresh:state:percentage:)]) {
+            [self.refreshDelegate jl_pullRefresh:self state:self.state percentage:self.pullPercentage];
+        }
+        
+        
+    } else {
+        
+        CGPoint old = [change[@"old"] CGPointValue];
+        CGPoint new = [change[@"new"] CGPointValue];
+        
+        if (self.scrollView.jl_insetTop == self.scrollViewOriginInsetTop && new.y > -self.scrollViewOriginInsetTop && old.y < new.y) { // 上拉,在未超出height的范围内拖动都视为刷新
+            
+            if (self.state == JLRefreshStateRefreshing) return;
+            
+            if (self.scrollView.isDragging) {
+                self.state = JLRefreshStateWillRefresh;
+                self.pullPercentage = 1;
+                [self showTip];
+                if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(jl_pullRefresh:state:percentage:)]) {
+                    [self.refreshDelegate jl_pullRefresh:self state:self.state percentage:self.pullPercentage];
+                }
+            }
+            
+        }
+
+        
+    }
+    
+    
+    
+    
+}
+
+- (void)scrollViewPanGestureStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+    [super scrollViewPanGestureStateDidChange:change];
+    
+    if (self.pan.state == UIGestureRecognizerStateEnded) {
+        
+        if (self.state == JLRefreshStateWillRefresh) {
+            self.state = JLRefreshStateRefreshing;
+        }
+        
+        
+    }
+    
+}
+
+#pragma mark - Setter
+
+- (void)setState:(JLRefreshState)state {
+    
+    JLRefreshState oldState = self.state;
+    
+    if (state == oldState) {
+        return;
+    }
+    
+    if (self.stateChangeBlock) {
+        self.stateChangeBlock(state);
+    }
+    
+    super.state = state;
+    
+     __weak typeof(self) weakself = self;
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [weakself setRefreshTitleForState:state];
+    });
+
+    
+    
+    if (state == JLRefreshStateIdle || state == JLRefreshStateNoMore) {
+        
+        if (oldState != JLRefreshStateRefreshing) {
+            return;
+        }
+        
+        if (weakself.refreshDelegate && [weakself.refreshDelegate respondsToSelector:@selector(jl_endRefresh:)]) {
+            
+            dispatch_async(dispatch_get_main_queue(), ^{
+                [weakself.refreshDelegate jl_endRefresh:weakself];
+            });
+            
+        }
+        
+    } else if (state == JLRefreshStatePulling) {
+        
+        
+    } else if (state == JLRefreshStateWillRefresh) {
+        
+        
+    } else if (state == JLRefreshStateRefreshing) {
+        
+        if (weakself.refreshDelegate && [weakself.refreshDelegate respondsToSelector:@selector(jl_beginRefresh:)]) {
+            
+            dispatch_async(dispatch_get_main_queue(), ^{
+                weakself.stopWithNomore = NO;
+                [weakself.refreshDelegate jl_beginRefresh:weakself];
+            });
+            
+        }
+        
+    }
+    
+}
+
+#pragma mark - Public
+
+- (void)noMoreData {
+    self.state = JLRefreshStateNoMore;
+    if (self.scrollView.isDecelerating) {
+        self.stopWithNomore = YES; // Scrollviewc重新刷新数据后才能赋值NO
+    }
+}
+
+- (void)showTip {
+    self.refreshTitleLabel.hidden = NO;
+}
+
+- (void)setRefreshTitle:(NSString *)title forState:(JLRefreshState)state {
+    if (title == nil) {
+        return;
+    }
+    
+    [self.stateTitle setObject:title forKey:@(state)];
+}
+
+- (NSString *)refreshTitleForState:(JLRefreshState)state {
+    return [self.stateTitle objectForKey:@(state)];
+}
+
+
+
+@end

+ 17 - 0
common/customUI/Refresh/Header/JLRefreshHeader.h

@@ -0,0 +1,17 @@
+//
+//  JLRefreshHeader.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/6.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshBasis.h"
+
+@interface JLRefreshHeader : JLRefreshBasis
+
+- (void)setRefreshTitle:(NSString *)title forState:(JLRefreshState)state;
+
+- (NSString *)refreshTitleForState:(JLRefreshState)state;
+
+@end

+ 230 - 0
common/customUI/Refresh/Header/JLRefreshHeader.m

@@ -0,0 +1,230 @@
+//
+//  JLRefreshHeader.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/6.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshHeader.h"
+
+@interface JLRefreshHeader ()
+
+@property (nonatomic,strong) NSMutableDictionary *stateTitle;
+
+@property (nonatomic,strong) UILabel *refreshTitleLabel;
+
+@end
+
+@implementation JLRefreshHeader
+
+#pragma mark - private
+
+- (NSMutableDictionary *)stateTitle {
+    if (!_stateTitle) {
+        _stateTitle = [@{
+                         @(JLRefreshStateIdle)          : @"pull to refresh",
+                         @(JLRefreshStatePulling)       : @"pull to refresh",
+                         @(JLRefreshStateWillRefresh)   : @"loosen to refresh",
+                         @(JLRefreshStateRefreshing)    : @"loading..."
+                         } mutableCopy];
+    }
+    return _stateTitle;
+}
+
+- (void)setRefreshTitleForState:(JLRefreshState)state {
+    if (!self.clear) {
+        self.refreshTitleLabel.text = [self refreshTitleForState:state];
+        [self setNeedsLayout];
+    }
+}
+
+#pragma mark - override
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    
+    self.jl_y = -self.jl_height;
+    
+    [self.refreshTitleLabel sizeToFit];
+    CGRect frame = self.refreshTitleLabel.bounds;
+    CGFloat Y = (CGRectGetHeight(self.bounds) - CGRectGetHeight(frame)) * 0.5;
+    CGFloat X = (CGRectGetWidth(self.bounds) - CGRectGetWidth(frame)) * 0.5;
+    self.refreshTitleLabel.jl_y = Y;
+    self.refreshTitleLabel.jl_x = X;
+    
+    [self addSubview:self.refreshTitleLabel];
+}
+
+- (void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+    if (self.offsetBlock) {
+        self.offsetBlock(self.scrollView.jl_offsetY);
+    }
+    
+    // 正在刷新
+    if (self.state == JLRefreshStateRefreshing) {
+        
+        // 解决SectionHeader停留
+        CGFloat insetTop = -self.scrollView.jl_offsetY > self.scrollViewOriginInsetTop ? -self.scrollView.jl_offsetY : self.scrollViewOriginInsetTop;
+        insetTop = insetTop > self.jl_height + self.scrollViewOriginInsetTop ? self.jl_height + self.scrollViewOriginInsetTop : insetTop;
+        self.scrollView.jl_insetTop = insetTop;
+        
+        return;
+    }
+    
+    CGFloat offsetY = self.scrollView.jl_offsetY;
+    CGFloat startY = -self.scrollView.jl_insetTop; // refresh 开始显示的临界点
+    
+    if (offsetY > startY) {
+        return;
+    }
+    
+    CGFloat idle2willRefreshY = startY - self.jl_height; // 正常状态到即将刷新状态临界点
+    
+    if (self.scrollView.isDragging) {
+        
+        if (self.state == JLRefreshStateIdle && offsetY < startY) {
+            
+            self.state = JLRefreshStatePulling;
+            
+        } else if (self.state == JLRefreshStatePulling && offsetY <= idle2willRefreshY) {
+            
+            self.state = JLRefreshStateWillRefresh;
+            
+        } else if (self.state == JLRefreshStatePulling && offsetY > startY) {
+            
+            self.state = JLRefreshStateIdle;
+            
+        } else if (self.state == JLRefreshStateWillRefresh && offsetY > idle2willRefreshY) {
+            
+            self.state = JLRefreshStatePulling;
+        
+        }
+        
+        float percentage = (startY - offsetY) / self.jl_height;
+        if (percentage <= 0) {
+            percentage = 0;
+        }
+        self.pullPercentage = percentage;
+        
+        if (self.refreshDelegate && [self.refreshDelegate respondsToSelector:@selector(jl_pullRefresh:state:percentage:)]) {
+            [self.refreshDelegate jl_pullRefresh:self state:self.state percentage:self.pullPercentage];
+        }
+        
+    }
+
+}
+
+- (void)scrollViewPanGestureStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    if (self.pan.state == UIGestureRecognizerStateEnded && self.state == JLRefreshStateWillRefresh) {
+        // 手松开开始刷新
+        self.state = JLRefreshStateRefreshing;
+    }
+}
+
+- (void)setState:(JLRefreshState)state {
+    JLRefreshState oldState = self.state;
+    
+    if (state == oldState) {
+        return;
+    }
+    
+    if (self.stateChangeBlock) {
+        self.stateChangeBlock(state);
+    }
+    
+    super.state = state;
+    
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [self setRefreshTitleForState:state];
+    });
+    
+    if (state == JLRefreshStateIdle) {
+
+        // 恢复原状态
+        if (oldState != JLRefreshStateRefreshing) {
+            return;
+        }
+        
+        __weak typeof(self) weakself = self;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [UIView animateWithDuration:JLRefreshDuration animations:^{
+                
+                weakself.scrollView.jl_insetTop = weakself.scrollViewOriginInsetTop;
+                
+            } completion:^(BOOL finished) {
+                
+                if (weakself.refreshDelegate && [weakself.refreshDelegate respondsToSelector:@selector(jl_endRefresh:)]) {
+                    
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [weakself.refreshDelegate jl_endRefresh:weakself];
+                    });
+                    
+                }
+                
+            }];
+        });
+
+        
+        
+        
+    } else if (state == JLRefreshStatePulling) {
+        // 下拉刷新
+        
+    } else if (state == JLRefreshStateWillRefresh) {
+        // 松开刷新
+        
+    } else if (state == JLRefreshStateRefreshing) {
+        // 正在刷新
+        __weak typeof(self) weakself = self;
+        dispatch_async(dispatch_get_main_queue(), ^{
+            
+            [UIView animateWithDuration:JLRefreshDuration animations:^{
+                
+                weakself.scrollView.jl_insetTop = weakself.jl_height;
+                
+            } completion:^(BOOL finished) {
+                
+                if (weakself.refreshDelegate && [weakself.refreshDelegate respondsToSelector:@selector(jl_beginRefresh:)]) {
+                    
+                    dispatch_async(dispatch_get_main_queue(), ^{
+                        [weakself.refreshDelegate jl_beginRefresh:weakself];
+                    });
+                    
+                }
+                
+            }];
+            
+        });
+
+    }
+}
+
+#pragma mark - Public
+
+- (void)setRefreshTitle:(NSString *)title forState:(JLRefreshState)state {
+    if (title == nil) {
+        return;
+    }
+    
+    [self.stateTitle setObject:title forKey:@(state)];
+}
+
+- (NSString *)refreshTitleForState:(JLRefreshState)state {
+    return [self.stateTitle objectForKey:@(state)];
+}
+
+#pragma mark - SubView
+
+- (UILabel *)refreshTitleLabel {
+    if (!_refreshTitleLabel) {
+        _refreshTitleLabel = [[UILabel alloc] init];
+        _refreshTitleLabel.textAlignment = NSTextAlignmentCenter;
+        _refreshTitleLabel.font = [UIFont systemFontOfSize:14.0f];
+    }
+    return _refreshTitleLabel;
+}
+
+
+@end

+ 70 - 0
common/customUI/Refresh/JLRefreshBasis.h

@@ -0,0 +1,70 @@
+//
+//  JLRefresh.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "UIScrollView+JLRefresh.h"
+#import "UIView+JLExtension.h"
+
+typedef enum {
+    /**空闲状态*/
+    JLRefreshStateIdle = 1 << 0,
+    /**下拉状态*/
+    JLRefreshStatePulling = 1 << 1,
+    /**即将刷新,还未松开*/
+    JLRefreshStateWillRefresh = 1 << 2,
+    /**刷新状态,松开*/
+    JLRefreshStateRefreshing = 1 << 3,
+    /**没有更多数据*/
+    JLRefreshStateNoMore = 1 << 4
+    
+} JLRefreshState;
+
+UIKIT_EXTERN NSTimeInterval const JLRefreshDuration;
+UIKIT_EXTERN CGFloat const JLRefreshHeight;
+UIKIT_EXTERN NSString *const JLRefreshKeyPathContentOffset;
+UIKIT_EXTERN NSString *const JLRefreshKeyPathContentSize;
+UIKIT_EXTERN NSString *const JLRefreshKeyPathContentInset;
+UIKIT_EXTERN NSString *const JLRefreshKeyPathState;
+
+@class JLRefreshBasis;
+@protocol JLRefreshDelegate <NSObject>
+
+- (void)jl_pullRefresh:(JLRefreshBasis *)refresh state:(JLRefreshState)state percentage:(float)percentage;//percentage's range [0,max)
+
+- (void)jl_beginRefresh:(JLRefreshBasis *)refresh;
+
+- (void)jl_endRefresh:(JLRefreshBasis *)refresh;
+
+@end
+
+@interface JLRefreshBasis : UIView
+
+@property (nonatomic,assign) JLRefreshState state;
+@property (nonatomic,assign) float pullPercentage;
+@property (nonatomic,weak,readonly) UIScrollView *scrollView;
+@property (nonatomic,assign,readonly) CGFloat scrollViewOriginInsetTop; ///<header只管Top
+@property (nonatomic,assign,readonly) CGFloat scrollViewOriginInsetBottom; ///<footer只管Bottom
+@property (nonatomic,strong,readonly) UIPanGestureRecognizer *pan;
+@property (nonatomic,weak) id<JLRefreshDelegate> refreshDelegate;
+@property (nonatomic,assign) float pullPercentate;
+@property (nonatomic,copy) void (^stateChangeBlock)(JLRefreshState state);
+@property (nonatomic,copy) void (^offsetBlock)(CGFloat offset);
+@property (nonatomic,assign) BOOL clear;
+
+- (NSString *)refreshTitleForState:(JLRefreshState)state;
+
+- (void)endRefresh;
+
+- (void)prepareInterface;
+
+// 需要子类实现
+- (void)scrollViewContentSizeDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change;
+- (void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change;
+- (void)scrollViewPanGestureStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change;
+
+@end

+ 152 - 0
common/customUI/Refresh/JLRefreshBasis.m

@@ -0,0 +1,152 @@
+//
+//  JLRefresh.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "JLRefreshBasis.h"
+
+NSTimeInterval const JLRefreshDuration = 0.25;
+CGFloat const JLRefreshHeight = 64;
+NSString *const JLRefreshKeyPathContentOffset = @"contentOffset";
+NSString *const JLRefreshKeyPathContentSize = @"contentSize";
+NSString *const JLRefreshKeyPathContentInset = @"contentInset";
+NSString *const JLRefreshKeyPathState = @"state";
+
+@interface JLRefreshBasis ()
+{
+    __weak UIScrollView *_scrollView;
+    UIPanGestureRecognizer *_pan;
+}
+
+@property (nonatomic,assign) CGFloat scrollViewOriginInsetTop;
+@property (nonatomic,assign) CGFloat scrollViewOriginInsetBottom;
+
+@end
+
+@implementation JLRefreshBasis
+
+- (instancetype)initWithFrame:(CGRect)frame {
+    if (self = [super initWithFrame:frame]) {
+        [self prepareInterface];
+        self.state = JLRefreshStateIdle;
+    }
+    return self;
+}
+
+- (void)prepareInterface {
+    self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+    self.backgroundColor = [UIColor clearColor];
+    self.jl_height = JLRefreshHeight;
+}
+
+- (void)layoutSubviews {
+    [super layoutSubviews];
+    self.jl_width = CGRectGetWidth(self.superview.frame);
+}
+
+//- (void)dealloc {
+//    [self resignObserver];
+//}
+
+/**添加观察者*/
+- (void)registObserver {
+    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
+    
+    [_scrollView addObserver:self forKeyPath:JLRefreshKeyPathContentOffset options:options context:nil];
+    [_scrollView addObserver:self forKeyPath:JLRefreshKeyPathContentSize options:options context:nil];
+    _pan = _scrollView.panGestureRecognizer;
+    [_pan addObserver:self forKeyPath:JLRefreshKeyPathState options:options context:nil];
+    
+}
+
+/**移除观察者*/
+- (void)resignObserver {
+    
+    /** fix crash while exit viewcontroller <Exception: NSInternalInconsistencyException>
+     *   self.superview == _scrollview
+     *   but superview is readonly ,while invoke this method,the _scrollview has been dealloced,then it get a wild pointer
+     *   if we use self.superview to get the object that is nil
+     */
+    [self.superview removeObserver:self forKeyPath:JLRefreshKeyPathContentOffset];
+    [self.superview removeObserver:self forKeyPath:JLRefreshKeyPathContentSize];
+    [_pan removeObserver:self forKeyPath:JLRefreshKeyPathState];
+    _pan = nil;
+    
+    
+}
+
+- (void)willMoveToSuperview:(UIView *)newSuperview {
+    [super willMoveToSuperview:newSuperview];
+    
+    [self resignObserver];
+    if (newSuperview == nil) {
+        _scrollView = nil;
+        return;
+    }
+    // 父控件不是ScrollView,不做设置
+    if (newSuperview && ![newSuperview isKindOfClass:[UIScrollView class]]) {
+        _scrollView = nil;
+        return;
+    }
+
+    _scrollView = (UIScrollView *)newSuperview;
+    _scrollView.alwaysBounceVertical = YES; // 永远支持垂直弹簧效果
+    
+    self.state = JLRefreshStateIdle;
+        
+    self.scrollViewOriginInsetTop = _scrollView.jl_insetTop;
+    self.scrollViewOriginInsetBottom = _scrollView.jl_insetBottom;
+    
+    [self registObserver];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
+    
+    if (!self.isUserInteractionEnabled) {
+        return;
+    }
+    
+    if (keyPath == JLRefreshKeyPathContentSize) {
+        [self scrollViewContentSizeDidChange:change];
+    }
+    
+    if (self.hidden) {
+        return;
+    }
+    
+    if (keyPath == JLRefreshKeyPathContentOffset) {
+        [self scrollViewContentOffsetDidChange:change];
+    }
+    
+    
+    if (keyPath == JLRefreshKeyPathState) {
+        [self scrollViewPanGestureStateDidChange:change];
+    }
+    
+}
+
+- (void)scrollViewContentSizeDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+}
+
+- (void)scrollViewContentOffsetDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+        
+}
+
+- (void)scrollViewPanGestureStateDidChange:(NSDictionary<NSKeyValueChangeKey,id> *)change {
+    
+}
+
+- (void)setState:(JLRefreshState)state {
+    _state = state;
+}
+
+- (void)endRefresh {
+    
+    self.state = JLRefreshStateIdle;
+}
+
+@end

+ 27 - 0
common/customUI/Refresh/UIScrollView+JLRefresh.h

@@ -0,0 +1,27 @@
+//
+//  UIScrollView+JLRefresh.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@class JLRefreshBasis;
+
+@interface UIScrollView (JLRefresh)
+
+@property (nonatomic,strong) JLRefreshBasis *jl_header;
+
+@property (nonatomic,strong) JLRefreshBasis *jl_footer;
+
+@property (nonatomic,assign) CGFloat jl_offsetY;
+
+@property (nonatomic,assign) CGFloat jl_insetTop;
+
+@property (nonatomic,assign) CGFloat jl_insetBottom;
+
+@property (nonatomic,assign) CGFloat jl_ContentHeight;
+
+@end

+ 92 - 0
common/customUI/Refresh/UIScrollView+JLRefresh.m

@@ -0,0 +1,92 @@
+//
+//  UIScrollView+JLRefresh.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "UIScrollView+JLRefresh.h"
+#import "JLRefreshBasis.h"
+#import <objc/runtime.h>
+
+@implementation UIScrollView (JLRefresh)
+
+static const char JLHeaderKey = '\a';
+
+- (void)setJl_header:(JLRefreshBasis *)jl_header {
+    
+    if (jl_header) {
+        [self.jl_header removeFromSuperview];
+        [self insertSubview:jl_header atIndex:0];
+        
+        [self willChangeValueForKey:@"jl_header"];
+        objc_setAssociatedObject(self, &JLHeaderKey, jl_header, OBJC_ASSOCIATION_ASSIGN);
+        [self didChangeValueForKey:@"jl_header"];
+    }
+    
+}
+
+- (JLRefreshBasis *)jl_header {
+    return objc_getAssociatedObject(self, &JLHeaderKey);
+}
+
+static const char JLFooterKey = '\b';
+
+- (void)setJl_footer:(JLRefreshBasis *)jl_footer {
+    
+    if (jl_footer) {
+        [self.jl_footer removeFromSuperview];
+        [self insertSubview:jl_footer atIndex:0];
+        
+        [self willChangeValueForKey:@"jl_footer"];
+        objc_setAssociatedObject(self, &JLFooterKey, jl_footer, OBJC_ASSOCIATION_ASSIGN);
+        [self didChangeValueForKey:@"jl_footer"];
+    }
+}
+
+- (JLRefreshBasis *)jl_footer {
+    return objc_getAssociatedObject(self, &JLFooterKey);
+}
+
+- (void)setJl_offsetY:(CGFloat)jl_offsetY {
+    CGPoint contentOff = self.contentOffset;
+    contentOff.y = jl_offsetY;
+    self.contentOffset = contentOff;
+}
+
+- (CGFloat)jl_offsetY {
+    return self.contentOffset.y;
+}
+
+- (void)setJl_insetTop:(CGFloat)jl_insetTop {
+    UIEdgeInsets inset = self.contentInset;
+    inset.top = jl_insetTop;
+    self.contentInset = inset;
+}
+
+- (CGFloat)jl_insetTop {
+    return self.contentInset.top;
+}
+
+- (void)setJl_insetBottom:(CGFloat)jl_insetBottom {
+    UIEdgeInsets inset = self.contentInset;
+    inset.bottom = jl_insetBottom;
+    self.contentInset = inset;
+}
+
+- (CGFloat)jl_insetBottom {
+    return self.contentInset.bottom;
+}
+
+- (void)setJl_ContentHeight:(CGFloat)jl_ContentHeight {
+    CGSize contentSize = self.contentSize;
+    contentSize.height = jl_ContentHeight;
+    self.contentSize = contentSize;
+}
+
+- (CGFloat)jl_ContentHeight {
+    return self.contentSize.height;
+}
+
+@end

+ 22 - 0
common/customUI/Refresh/UIView+JLExtension.h

@@ -0,0 +1,22 @@
+//
+//  UIView+Extension.h
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface UIView (JLExtension)
+
+#pragma mark - Frame
+
+@property (nonatomic,assign) CGFloat jl_x;
+@property (nonatomic,assign) CGFloat jl_y;
+@property (nonatomic,assign) CGFloat jl_height;
+@property (nonatomic,assign) CGFloat jl_width;
+@property (nonatomic,assign) CGFloat jl_right;
+@property (nonatomic,assign) CGFloat jl_bottom;
+
+@end

+ 71 - 0
common/customUI/Refresh/UIView+JLExtension.m

@@ -0,0 +1,71 @@
+//
+//  UIView+Extension.m
+//  JLRefreshDemo
+//
+//  Created by Jack on 2017/3/3.
+//  Copyright © 2017年 mini1. All rights reserved.
+//
+
+#import "UIView+JLExtension.h"
+
+@implementation UIView (JLExtension)
+
+#pragma mark - Frame
+
+- (void)setJl_x:(CGFloat)jl_x {
+    CGRect frame = self.frame;
+    frame.origin.x = jl_x;
+    self.frame = frame;
+}
+
+- (CGFloat)jl_x {
+    return self.frame.origin.x;
+}
+
+- (void)setJl_y:(CGFloat)jl_y {
+    CGRect frame = self.frame;
+    frame.origin.y = jl_y;
+    self.frame = frame;
+}
+
+- (CGFloat)jl_y {
+    return self.frame.origin.y;
+}
+
+- (void)setJl_width:(CGFloat)jl_width {
+    CGRect frame = self.frame;
+    frame.size.width = jl_width;
+    self.frame = frame;
+}
+
+- (CGFloat)jl_width {
+    return self.frame.size.width;
+}
+
+- (void)setJl_height:(CGFloat)jl_height {
+    CGRect frame = self.frame;
+    frame.size.height = jl_height;
+    self.frame = frame;
+}
+
+- (CGFloat)jl_height {
+    return self.frame.size.height;
+}
+
+- (void)setJl_right:(CGFloat)jl_right {
+    self.jl_x = jl_right - self.jl_width;
+}
+
+- (CGFloat)jl_right {
+    return self.jl_x + self.jl_width;
+}
+
+- (void)setJl_bottom:(CGFloat)jl_bottom {
+    self.jl_y = jl_bottom - self.jl_height;
+}
+
+- (CGFloat)jl_bottom {
+    return self.jl_y + self.jl_height;
+}
+
+@end

+ 147 - 0
common/customUI/Result.xib

@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ResultViewController">
+            <connections>
+                <outlet property="footer_top_constraint" destination="veQ-No-JHu" id="Iau-rm-kXH"/>
+                <outlet property="h_constraint" destination="OCH-yF-HTJ" id="4PA-KC-Oaf"/>
+                <outlet property="header_top_constraint" destination="g32-lw-wxU" id="itz-AJ-NKi"/>
+                <outlet property="lead_constraint" destination="bbY-b2-jpG" id="buh-DH-2Qm"/>
+                <outlet property="refreshFooterBar" destination="GO7-Wf-2EG" id="K8N-GT-OAy"/>
+                <outlet property="refreshFooterTitleLb" destination="aLC-7L-TFI" id="huf-al-L6p"/>
+                <outlet property="refreshHeaderBar" destination="FS6-OU-h6k" id="0m0-Lt-fpM"/>
+                <outlet property="refreshHeaderTitleLb" destination="i3K-3e-1an" id="LtE-eV-XbL"/>
+                <outlet property="scrollview" destination="SK6-pt-SSv" id="dC1-EA-4pA"/>
+                <outlet property="tableview" destination="zfU-1Y-HJW" id="Uox-t1-3qj"/>
+                <outlet property="titleBar" destination="x6s-gs-coa" id="cK1-q5-4Bf"/>
+                <outlet property="titleLabel" destination="Jzl-DK-3jH" id="vpF-dE-IPa"/>
+                <outlet property="trail_constraint" destination="WJL-rP-AGA" id="vZS-qc-Zvw"/>
+                <outlet property="view" destination="h5p-9r-ydz" id="wdH-Fn-tAh"/>
+                <outlet property="w_constraint" destination="awE-WC-Ds5" id="Pvg-A9-QMU"/>
+            </connections>
+        </placeholder>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="h5p-9r-ydz">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" showsVerticalScrollIndicator="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SK6-pt-SSv">
+                    <rect key="frame" x="0.0" y="44" width="375" height="623"/>
+                    <subviews>
+                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Bl4-Od-H2W">
+                            <rect key="frame" x="0.0" y="0.0" width="375" height="623"/>
+                            <subviews>
+                                <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="zfU-1Y-HJW">
+                                    <rect key="frame" x="0.0" y="0.0" width="375" height="623"/>
+                                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                                    <connections>
+                                        <outlet property="dataSource" destination="-1" id="NDN-Yh-L0u"/>
+                                        <outlet property="delegate" destination="-1" id="wYx-HQ-Lha"/>
+                                    </connections>
+                                </tableView>
+                            </subviews>
+                            <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                            <constraints>
+                                <constraint firstAttribute="trailing" secondItem="zfU-1Y-HJW" secondAttribute="trailing" id="1o4-jr-iSC"/>
+                                <constraint firstItem="zfU-1Y-HJW" firstAttribute="width" secondItem="Bl4-Od-H2W" secondAttribute="width" id="jcZ-Zv-hi4"/>
+                                <constraint firstItem="zfU-1Y-HJW" firstAttribute="top" secondItem="Bl4-Od-H2W" secondAttribute="top" id="tjX-aC-epB"/>
+                                <constraint firstItem="zfU-1Y-HJW" firstAttribute="height" secondItem="Bl4-Od-H2W" secondAttribute="height" id="uxw-6u-AOH"/>
+                                <constraint firstItem="zfU-1Y-HJW" firstAttribute="leading" secondItem="Bl4-Od-H2W" secondAttribute="leading" id="yeO-hg-XPz"/>
+                            </constraints>
+                        </view>
+                    </subviews>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="Bl4-Od-H2W" secondAttribute="trailing" id="1Ti-iV-OHl"/>
+                        <constraint firstItem="Bl4-Od-H2W" firstAttribute="top" secondItem="SK6-pt-SSv" secondAttribute="top" id="9Pr-vR-G66"/>
+                        <constraint firstItem="Bl4-Od-H2W" firstAttribute="height" secondItem="SK6-pt-SSv" secondAttribute="height" id="OCH-yF-HTJ"/>
+                        <constraint firstItem="Bl4-Od-H2W" firstAttribute="width" secondItem="SK6-pt-SSv" secondAttribute="width" id="awE-WC-Ds5"/>
+                        <constraint firstAttribute="bottom" secondItem="Bl4-Od-H2W" secondAttribute="bottom" id="ken-ay-0id"/>
+                        <constraint firstItem="Bl4-Od-H2W" firstAttribute="leading" secondItem="SK6-pt-SSv" secondAttribute="leading" id="tFg-Gm-FgS"/>
+                    </constraints>
+                </scrollView>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FS6-OU-h6k">
+                    <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Header Refresh" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="i3K-3e-1an">
+                            <rect key="frame" x="-0.5" y="-0.5" width="375" height="44"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                            <nil key="textColor"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstItem="i3K-3e-1an" firstAttribute="centerY" secondItem="FS6-OU-h6k" secondAttribute="centerY" id="2od-kC-few"/>
+                        <constraint firstAttribute="height" constant="44" id="5sI-hR-Cxb"/>
+                        <constraint firstItem="i3K-3e-1an" firstAttribute="height" secondItem="FS6-OU-h6k" secondAttribute="height" id="H8N-D6-K9e"/>
+                        <constraint firstItem="i3K-3e-1an" firstAttribute="width" secondItem="FS6-OU-h6k" secondAttribute="width" id="Oyv-wc-jJu"/>
+                        <constraint firstItem="i3K-3e-1an" firstAttribute="centerX" secondItem="FS6-OU-h6k" secondAttribute="centerX" id="txN-Ti-XTN"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="x6s-gs-coa">
+                    <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jzl-DK-3jH">
+                            <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                            <nil key="textColor"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstItem="Jzl-DK-3jH" firstAttribute="centerX" secondItem="x6s-gs-coa" secondAttribute="centerX" id="V5M-Nu-TQm"/>
+                        <constraint firstAttribute="height" constant="44" id="ieE-am-1pd"/>
+                        <constraint firstItem="Jzl-DK-3jH" firstAttribute="centerY" secondItem="x6s-gs-coa" secondAttribute="centerY" id="v8v-ue-WNF"/>
+                        <constraint firstItem="Jzl-DK-3jH" firstAttribute="height" secondItem="x6s-gs-coa" secondAttribute="height" id="vdu-Mr-9yu"/>
+                        <constraint firstItem="Jzl-DK-3jH" firstAttribute="width" secondItem="x6s-gs-coa" secondAttribute="width" id="xIO-zR-s3W"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GO7-Wf-2EG">
+                    <rect key="frame" x="0.0" y="667" width="375" height="30"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Footer Refresh" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aLC-7L-TFI">
+                            <rect key="frame" x="-0.5" y="-0.5" width="375" height="30"/>
+                            <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                            <nil key="textColor"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="30" id="4wG-Gk-LKc"/>
+                        <constraint firstItem="aLC-7L-TFI" firstAttribute="height" secondItem="GO7-Wf-2EG" secondAttribute="height" id="IOY-Vh-zMp"/>
+                        <constraint firstItem="aLC-7L-TFI" firstAttribute="centerY" secondItem="GO7-Wf-2EG" secondAttribute="centerY" id="XUY-M7-qR6"/>
+                        <constraint firstItem="aLC-7L-TFI" firstAttribute="centerX" secondItem="GO7-Wf-2EG" secondAttribute="centerX" id="c08-mP-qdL"/>
+                        <constraint firstItem="aLC-7L-TFI" firstAttribute="width" secondItem="GO7-Wf-2EG" secondAttribute="width" id="nrB-wU-nho"/>
+                    </constraints>
+                </view>
+            </subviews>
+            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+            <constraints>
+                <constraint firstItem="FS6-OU-h6k" firstAttribute="width" secondItem="x6s-gs-coa" secondAttribute="width" id="0Tg-kL-kQm"/>
+                <constraint firstItem="GO7-Wf-2EG" firstAttribute="leading" secondItem="x6s-gs-coa" secondAttribute="leading" id="3QW-0F-LMu"/>
+                <constraint firstItem="FS6-OU-h6k" firstAttribute="leading" secondItem="x6s-gs-coa" secondAttribute="leading" id="Bjx-d0-fOe"/>
+                <constraint firstItem="x6s-gs-coa" firstAttribute="leading" secondItem="h5p-9r-ydz" secondAttribute="leading" id="GCn-RX-zyO"/>
+                <constraint firstItem="SK6-pt-SSv" firstAttribute="bottom" secondItem="h5p-9r-ydz" secondAttribute="bottom" identifier="scroll_bottom" id="PgW-kq-hWQ"/>
+                <constraint firstAttribute="trailing" secondItem="SK6-pt-SSv" secondAttribute="trailing" id="WJL-rP-AGA"/>
+                <constraint firstItem="SK6-pt-SSv" firstAttribute="leading" secondItem="h5p-9r-ydz" secondAttribute="leading" id="bbY-b2-jpG"/>
+                <constraint firstItem="GO7-Wf-2EG" firstAttribute="width" secondItem="x6s-gs-coa" secondAttribute="width" id="fa5-nI-GP6"/>
+                <constraint firstItem="FS6-OU-h6k" firstAttribute="top" secondItem="x6s-gs-coa" secondAttribute="top" id="g32-lw-wxU"/>
+                <constraint firstItem="x6s-gs-coa" firstAttribute="top" secondItem="h5p-9r-ydz" secondAttribute="top" identifier="title_top" id="gxd-aW-mfE"/>
+                <constraint firstItem="SK6-pt-SSv" firstAttribute="top" secondItem="x6s-gs-coa" secondAttribute="bottom" id="mN1-LO-vgQ"/>
+                <constraint firstAttribute="trailing" secondItem="x6s-gs-coa" secondAttribute="trailing" id="vMf-BI-ntc"/>
+                <constraint firstItem="GO7-Wf-2EG" firstAttribute="top" secondItem="SK6-pt-SSv" secondAttribute="bottom" id="veQ-No-JHu"/>
+            </constraints>
+            <point key="canvasLocation" x="-106.5" y="126.5"/>
+        </view>
+    </objects>
+</document>

+ 23 - 0
common/customUI/ResultCell.xib

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait">
+        <adaptation id="fullscreen"/>
+    </device>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="tCell" id="Lic-Ng-p4U">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
+            <autoresizingMask key="autoresizingMask"/>
+            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Lic-Ng-p4U" id="wsG-8E-9L2">
+                <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
+                <autoresizingMask key="autoresizingMask"/>
+            </tableViewCellContentView>
+        </tableViewCell>
+    </objects>
+</document>