[實踐]通用iOS AlertView設計與實現

全部文章目錄: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.頭部 2.中間局域 3.底部選項, 各個部分的樣式與功能在使用時決定 [一致性]
  • 新增一種樣式(頭/中間/選項)時, 不會影響到基礎框架, 而且使用方式與別的AlertView一致 [擴展性]
  • 各個業務線在使用時如系統的AlertView同樣簡單, 不須要太多的代碼 [交互方式]
  • 控件大小根據設置的值自動得出, 而且約束自動生成

##1.3 實現效果列舉 demo2 demo4佈局

#2.設計 ##2.1 類圖 (策略模式+工廠模式)atom

通用AlertView 類圖

實際實現類圖: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 需求 要顯示兩行文案, 其中狀態後臺返回, 而且是紅色. 最終效果以下:

平安AlertView

###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 對接入方的優勢是什麼?

  • 已經有的模塊不須要重複建立, 節省工做量;
  • 在每一個新AlertView處理業務邏輯, 簡化了VC, 而且更直觀。

##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

相關文章
相關標籤/搜索