全部文章目錄:http://my.oschina.net/ChenTF/blog/677112git
本篇文章地址: http://my.oschina.net/ChenTF/blog/730068github
會持續的更新全部歷史文章, 因此收藏的話請收藏上面的地址哦。架構
#1.需求 ##1.1 背景框架
當前DJ, SY項目中的AlertView並不具有通用性 與 移植性, 隨着SY業務的發展, AlertView的種類愈來愈多, 各個AlertView內部的處理邏輯都須要特殊處理。形成了維護成本的增高, 與擴展難的現象。ide
具體表現爲: 在原有基礎上新增一個AlertView的成本與重新寫一個AlertView的成本幾乎差很少, 因此發起通用AlertView組件化的技術需求。工具
##1.2 最終目標 插件式設計, 可擴展, 可修改.組件化
##1.3 實現效果列舉 佈局
#2.設計 ##2.1 類圖 (策略模式+工廠模式)atom
實際實現類圖:spa
策略模式經過接口定義基本的組成部分, 並經過子類來拓展功能。爲了保證組成部分的複用性, 因此在功能的子類中, 只作基本的默認樣式, 而且要寫出可供方便的修改樣式接口。
在策略模式的子類中, 實例化本身的組成部分, 並設置本身的樣式與特殊邏輯的處理, 以達到獨特性的要求。
將抽象AlertView與具體子類以工廠模式的方式結合起來, 可以整合全部的對象建立。
#3. 實現 ##3.1 抽象父類 AbstractAlertView類定義了AlertView的基本組成部分(headview/ contentView/ actionView), 定義了回調方式(DJAbstractAlertViewDelegate), 封裝了基本的使用方式(show, dismiss)
#import <UIKit/UIKit.h> #import "Masonry.h" #import "DJAbstractHeadView.h" #import "DJAbstractAlertContentView.h" #import "DJAbstractAlertActionView.h" @protocol DJAbstractAlertViewDelegate; @interface DJAbstractAlertView : UIView<DJAbstractAlertActionViewDelegate @property (nonatomic, strong) DJAbstractHeadView *headView; // 頭部試圖 @property (nonatomic, strong) DJAbstractAlertContentView *contentView; //中間試圖 @property (nonatomic, strong) DJAbstractAlertActionView *actionView; // 底部選項試圖 @property (nonatomic, strong) UIImageView *backgroundImageView; // 背景圖片 @property (nonatomic, weak) id<DJAbstractAlertViewDelegate> delegate; #pragma mark - 佈局 & 樣式 @property (nonatomic, assign) CGFloat alertViewPadding; // alertView 距離屏幕兩側的距離 @property (nonatomic, strong) UIColor *shardowCorlor; // 背景陰影的顏色 - (void)setLayerCornerRadius:(CGFloat)cornerRadius; #pragma mark - Public Methods - (void)show; - (void)dismiss; /** show以後的邏輯處理 */ - (void)showCompletionMethods; /** * 設置AlertView的位置偏移 */ - (void)setViewOffset:(CGPoint)offsetPoint; @end @protocol DJAbstractAlertViewDelegate <NSObject /** * 選中下標回調, 返回YES則彈窗消失(默認YES) */ - (BOOL)AlertView:(DJAbstractAlertView *)alertView Index:(NSUInteger)index; @end
##3.2 抽象組成部分 ###3.2.1 DJAbstractHeadView
#import <UIKit/UIKit.h> #import "Masonry.h" @interface DJAbstractHeadView : UIView @end
###3.2.2 DJAbstractAlertContentView
#import <UIKit/UIKit.h> #import "Masonry.h" @interface DJAbstractAlertContentView : UIView /** 設置ContentView的外邊距, 默認(0, 0, 0, 0) */ @property (nonatomic, assign) UIEdgeInsets contentEdge; @end
###3.2.3 DJAbstractAlertActionView 經過AbstractAlertView的子類來實現DJAbstractAlertActionViewDelegate協議, 達到將Action的點擊事件傳到AlertView中
#import <UIKit/UIKit.h> #import "Masonry.h" @protocol DJAbstractAlertActionViewDelegate; @interface DJAbstractAlertActionView : UIView @property (nonatomic, assign) id<DJAbstractAlertActionViewDelegate> delegate; - (instancetype)initWithDelegate:(id<DJAbstractAlertActionViewDelegate>)delegate; /** * 設置選中的下標 */ - (void)setSelectedIndex:(NSUInteger)index; @end @protocol DJAbstractAlertActionViewDelegate <NSObject> /** 選項選中回調 @param actionView 當前的ActionView對象 @param index 下標 */ - (void)ActionView:(DJAbstractAlertActionView *)actionView SelectedIndex:(NSUInteger)index; @end
##3.3 顯示隊列工具類 需求: 若是有多個AlertView須要顯示, 則須要以前的消失後才彈出新的AlertView. 解決方案: 建立一個單例類, 建立個隊列來存儲須要展現的AlertView, 對隊列進行操做來達到只顯示一個, 而且自動彈出下一個的需求.
DJAlertViewShowHandler
#import <Foundation/Foundation.h> #import "DJAbstractAlertView.h" @class DJAbstractAlertView; @interface DJAlertViewShowHandler : NSObject + (DJAlertViewShowHandler *)sharedInstance; - (void)showAlertView:(DJAbstractAlertView *)alertView; - (void)dismissAlertView:(DJAbstractAlertView *)alertView; @end
#4. 具體AlertView實例 ##4.1 保險提示AlertView ###4.1.1 需求 要顯示兩行文案, 其中狀態後臺返回, 而且是紅色. 最終效果以下:
###4.1.2 實現 分析發現由兩部分組成: 1兩行文案的標題 2.一個按鈕的選項 而後再建立一個集成自 DJAbstractAlertView的子類, 將兩個內容元素綁定, 而且處理當前需求的邏輯
DJAlertDetailHeadView(兩行文案的標題)
import "DJAbstractHeadView.h" typedef NS_ENUM(NSUInteger, DJDetailHeadViewType) { DJDetailHeadViewTypeTitle, // 純標題 DJDetailHeadViewTypeDetail, // 標題+詳情 }; @interface DJAlertDetailHeadView : DJAbstractHeadView @property (nonatomic, assign) DJDetailHeadViewType type; // 標題 @property (nonatomic, strong) UILabel *titleLabel; @property (nonatomic, assign) UIEdgeInsets titleEdge; // default : (0, 10, 0, 10) // 詳情 @property (nonatomic, strong) UILabel *detailLabel; @property (nonatomic, assign) UIEdgeInsets detailEdge; // default : (0, 10, 0, 10) - (instancetype)initWithType:(DJDetailHeadViewType)type; @end
DJAlertSingleActionView(一個按鈕的選項)
#import "DJAbstractAlertActionView.h" @interface DJAlertSingleActionView : DJAbstractAlertActionView @property (nonatomic, strong) UIButton *actionBtn; // 取消 @property (nonatomic, strong) UIView *topLine; #pragma mark - 佈局 & 樣式 @property (nonatomic, assign) CGFloat actionHeight; @end
SYInsuranceProgressAlertView(保險提示彈窗)
.h
#import "DJAbstractAlertView.h" /** 保險進度彈窗 */ @interface SYInsuranceProgressAlertView : DJAbstractAlertView @property (nonatomic, copy) NSString *progressStr; @end
.m
@implementation SYInsuranceProgressAlertView /* // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - (void)drawRect:(CGRect)rect { // Drawing code } */ - (instancetype)init { self = [super init]; if (self) { [self setUpView]; } return self; } - (void)setUpView { DJAlertDetailHeadView *headView = [[DJAlertDetailHeadView alloc] initWithType:DJDetailHeadViewTypeDetail]; headView.titleLabel.text = @"平安保險已收到您的理賠"; headView.titleLabel.textColor = [UIColor ColorOfHex:0x333333]; headView.titleLabel.textAlignment = NSTextAlignmentCenter; headView.titleEdge = UIEdgeInsetsMake(15, 0, 0, 0); headView.detailLabel.textAlignment = NSTextAlignmentCenter; headView.detailEdge = UIEdgeInsetsMake(4, 0, 15, 0); self.headView = headView; DJAlertSingleActionView *actionView = [[DJAlertSingleActionView alloc] initWithDelegate:self]; [actionView.actionBtn setTitle:@"我知道了" forState:UIControlStateNormal]; [actionView.actionBtn setTitleColor:[UIColor ColorOfHex:0xe6454a] forState:UIControlStateNormal]; self.actionView = actionView; } - (void)setProgressStr:(NSString *)progressStr { if ([NSString isEmptyString:progressStr]) { _progressStr = @"處理中.."; } else { _progressStr = [progressStr copy]; } NSString *detailStr = [NSString stringWithFormat:@"當前狀態爲: %@", progressStr]; NSMutableAttributedString *attributeStr = [[NSMutableAttributedString alloc] initWithString:detailStr]; [attributeStr addAttribute:NSForegroundColorAttributeName value:[UIColor ColorOfHex:0x333333] range:NSMakeRange(0, progressStr.length)]; [attributeStr addAttribute:NSForegroundColorAttributeName value:[UIColor ColorOfHex:0xe6454a] range:NSMakeRange(7, progressStr.length)]; ((DJAlertDetailHeadView *)self.headView).detailLabel.attributedText = attributeStr; } @end
#5.討論 ##5.1 我只想建立一個AlertView, 但是要寫這麼多代碼, 豈不更費事? 確實是前面的代碼量很大, 可是隻須要寫一次。
##5.2 這麼寫, 不還得本身建立各個功能模塊, 有什麼意義? 我相信不管想作出多麼優秀的架構, 功能的實現代碼是不可避免的, 並非說用了某個框架, 就不用寫代碼了。
那麼這麼複雜的實現, 意義何在? ---> 意義就是功能模塊只須要實現一次。
我相信一個好的開發模式是"越作越省力, 越作越快", 而不是"越作越累, 越作越難"。
因此這套架構的使用是這樣的:
##5.3 對接入方的優勢是什麼?
##5.4 正確的接入方式 基礎框架是通常均可以複用與提供服務, 因此以pods方式接入;
具體的工廠與AlertView通常都與業務方深度耦合, 因此再業務方內建立。
##5.4 工廠模式的做用 工廠模式是專門用來整合一類對象建立的模式。
使用工廠模式在少許AlertView時看起來沒有用, 可是隨着業務擴展, 你就能發現他的好處。
#6.工廠類 ##6.1 工廠父類 AlertViewFactory
#import "DJAbstractAlertView.h" @interface AlertViewFactory : NSObject - (DJAbstractAlertView *)alertViewWithType:(NSString *)type; @end
##6.2 工廠子類 SYAlertViewFactory
.h
#import <Foundation/Foundation.h> #import "AlertViewFactory.h" #import "SYAddFeeAlertView.h" #import "SYLimitNoticeAlertView.h" #import "SYDispatchingAddFreeAlertView.h" #import "SYOrderAddFreeAlertView.h" #define kSYAlertViewType_SYLimitNoticeAlertView @"SYAlertViewType_SYLimitNoticeAlertView" #define kSYAlertViewType_SYAddFeeAlertView @"SYAlertViewType_SYAddFeeAlertView" #define kSYAlertViewType_SYDispatchingAddFreeAlertView @"SYAlertViewType_SYDispatchingAddFreeAlertView" #define SYAlertViewType_SYOrderAddFreeAlertView @"SYAlertViewType_SYOrderAddFreeAlertView" @interface SYAlertViewFactory : AlertViewFactory @end
.m
#import "SYAlertViewFactory.h" @implementation SYAlertViewFactory - (DJAbstractAlertView *)alertViewWithType:(NSString *)type { DJAbstractAlertView *alertView = nil; if ([type isEqualToString:kSYAlertViewType_SYLimitNoticeAlertView]) { alertView = [[SYLimitNoticeAlertView alloc] init]; } else if ([type isEqualToString:kSYAlertViewType_SYAddFeeAlertView]) { alertView = [[SYAddFeeAlertView alloc] init]; } else if ([type isEqualToString:kSYAlertViewType_SYDispatchingAddFreeAlertView]) { alertView = [[SYDispatchingAddFreeAlertView alloc] init]; } else if ([type isEqualToString:SYAlertViewType_SYOrderAddFreeAlertView]) { alertView = [[SYOrderAddFreeAlertView alloc] init]; } return alertView; } @end
#7.Code DJAlertView