小说绘上架版本
This commit is contained in:
+87
@@ -0,0 +1,87 @@
|
||||
//
|
||||
// NSMutableAttributedString+TY.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
@interface NSMutableAttributedString (TY)
|
||||
|
||||
/**
|
||||
* 添加文本颜色属性
|
||||
*
|
||||
* @param color 文本颜色
|
||||
*/
|
||||
- (void)addAttributeTextColor:(UIColor*)color;
|
||||
|
||||
- (void)addAttributeTextColor:(UIColor*)color range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加文本字体属性
|
||||
*
|
||||
* @param font 字体
|
||||
*/
|
||||
- (void)addAttributeFont:(UIFont *)font;
|
||||
|
||||
- (void)addAttributeFont:(UIFont *)font range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加文本字符间隔
|
||||
*
|
||||
* @param characterSpacing 字符间隔
|
||||
*/
|
||||
- (void)addAttributeCharacterSpacing:(unichar)characterSpacing;
|
||||
|
||||
- (void)addAttributeCharacterSpacing:(unichar)characterSpacing range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加下划线样式
|
||||
*
|
||||
* @param style 下划线 (单下划线 双 无)
|
||||
* @param modifier 下划线样式 (点 线)
|
||||
*/
|
||||
- (void)addAttributeUnderlineStyle:(CTUnderlineStyle)style
|
||||
modifier:(CTUnderlineStyleModifiers)modifier;
|
||||
|
||||
- (void)addAttributeUnderlineStyle:(CTUnderlineStyle)style
|
||||
modifier:(CTUnderlineStyleModifiers)modifier
|
||||
range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加空心字
|
||||
*
|
||||
* @param strokeWidth 空心字边框宽
|
||||
* @param strokeColor 空心字边框颜色
|
||||
*/
|
||||
- (void)addAttributeStrokeWidth:(unichar)strokeWidth
|
||||
strokeColor:(UIColor *)strokeColor;
|
||||
|
||||
- (void)addAttributeStrokeWidth:(unichar)strokeWidth
|
||||
strokeColor:(UIColor *)strokeColor
|
||||
range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加文本段落样式
|
||||
*
|
||||
* @param textAlignment 文本对齐样式
|
||||
* @param linesSpacing 文本行间距
|
||||
* @param paragraphSpacing 段落间距
|
||||
* @param lineBreakMode 文本换行样式
|
||||
*/
|
||||
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
|
||||
lineSpaceStyle:(CGFloat)linesSpacing
|
||||
paragraphSpaceStyle:(CGFloat)paragraphSpacing
|
||||
lineBreakStyle:(CTLineBreakMode)lineBreakMode;
|
||||
|
||||
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
|
||||
lineSpaceStyle:(CGFloat)linesSpacing
|
||||
paragraphSpaceStyle:(CGFloat)paragraphSpacing
|
||||
lineBreakStyle:(CTLineBreakMode)lineBreakMode
|
||||
range:(NSRange)range;
|
||||
|
||||
@end
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
//
|
||||
// NSMutableAttributedString+TY.m
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSMutableAttributedString+TY.h"
|
||||
|
||||
@implementation NSMutableAttributedString (TY)
|
||||
|
||||
#pragma mark - 文本颜色属性
|
||||
- (void)addAttributeTextColor:(UIColor*)color
|
||||
{
|
||||
[self addAttributeTextColor:color range:NSMakeRange(0, [self length])];
|
||||
}
|
||||
|
||||
- (void)addAttributeTextColor:(UIColor*)color range:(NSRange)range
|
||||
{
|
||||
if (color.CGColor)
|
||||
{
|
||||
[self removeAttribute:(NSString *)kCTForegroundColorAttributeName range:range];
|
||||
|
||||
[self addAttribute:(NSString *)kCTForegroundColorAttributeName
|
||||
value:(id)color.CGColor
|
||||
range:range];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - 文本字体属性
|
||||
- (void)addAttributeFont:(UIFont *)font
|
||||
{
|
||||
[self addAttributeFont:font range:NSMakeRange(0, [self length])];
|
||||
}
|
||||
|
||||
- (void)addAttributeFont:(UIFont *)font range:(NSRange)range
|
||||
{
|
||||
if (font)
|
||||
{
|
||||
[self removeAttribute:(NSString*)kCTFontAttributeName range:range];
|
||||
|
||||
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"SFUI-Regular", font.pointSize, nil);
|
||||
if (nil != fontRef)
|
||||
{
|
||||
[self addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)fontRef range:range];
|
||||
CFRelease(fontRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 文本字符间隔属性
|
||||
- (void)addAttributeCharacterSpacing:(unichar)characterSpacing
|
||||
{
|
||||
[self addAttributeCharacterSpacing:characterSpacing range:NSMakeRange(0, self.length)];
|
||||
}
|
||||
|
||||
- (void)addAttributeCharacterSpacing:(unichar)characterSpacing range:(NSRange)range
|
||||
{
|
||||
[self removeAttribute:(id)kCTKernAttributeName range:range];
|
||||
|
||||
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt16Type,&characterSpacing);
|
||||
[self addAttribute:(id)kCTKernAttributeName value:(__bridge id)num range:range];
|
||||
CFRelease(num);
|
||||
}
|
||||
|
||||
#pragma mark - 文本下划线属性
|
||||
- (void)addAttributeUnderlineStyle:(CTUnderlineStyle)style
|
||||
modifier:(CTUnderlineStyleModifiers)modifier
|
||||
{
|
||||
[self addAttributeUnderlineStyle:style
|
||||
modifier:modifier
|
||||
range:NSMakeRange(0, self.length)];
|
||||
}
|
||||
|
||||
- (void)addAttributeUnderlineStyle:(CTUnderlineStyle)style
|
||||
modifier:(CTUnderlineStyleModifiers)modifier
|
||||
range:(NSRange)range
|
||||
{
|
||||
[self removeAttribute:(NSString *)kCTUnderlineColorAttributeName range:range];
|
||||
|
||||
if (style != kCTUnderlineStyleNone) {
|
||||
[self addAttribute:(NSString *)kCTUnderlineStyleAttributeName
|
||||
value:[NSNumber numberWithInt:(style|modifier)]
|
||||
range:range];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - 文本空心字及颜色
|
||||
|
||||
- (void)addAttributeStrokeWidth:(unichar)strokeWidth
|
||||
strokeColor:(UIColor *)strokeColor
|
||||
{
|
||||
[self addAttributeStrokeWidth:strokeWidth strokeColor:strokeColor range:NSMakeRange(0, self.length)];
|
||||
}
|
||||
|
||||
- (void)addAttributeStrokeWidth:(unichar)strokeWidth
|
||||
strokeColor:(UIColor *)strokeColor
|
||||
range:(NSRange)range
|
||||
{
|
||||
[self removeAttribute:(id)kCTStrokeWidthAttributeName range:range];
|
||||
if (strokeWidth > 0) {
|
||||
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt16Type,&strokeWidth);
|
||||
|
||||
[self addAttribute:(id)kCTStrokeWidthAttributeName value:(__bridge id)num range:range];
|
||||
|
||||
CFRelease(num);
|
||||
}
|
||||
|
||||
[self removeAttribute:(id)kCTStrokeColorAttributeName range:range];
|
||||
if (strokeColor) {
|
||||
[self addAttribute:(id)kCTStrokeColorAttributeName value:(id)strokeColor.CGColor range:range];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - 文本段落样式属性
|
||||
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
|
||||
lineSpaceStyle:(CGFloat)linesSpacing
|
||||
paragraphSpaceStyle:(CGFloat)paragraphSpacing
|
||||
lineBreakStyle:(CTLineBreakMode)lineBreakMode
|
||||
{
|
||||
[self addAttributeAlignmentStyle:textAlignment lineSpaceStyle:linesSpacing paragraphSpaceStyle:paragraphSpacing lineBreakStyle:lineBreakMode range:NSMakeRange(0, self.length)];
|
||||
}
|
||||
|
||||
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
|
||||
lineSpaceStyle:(CGFloat)linesSpacing
|
||||
paragraphSpaceStyle:(CGFloat)paragraphSpacing
|
||||
lineBreakStyle:(CTLineBreakMode)lineBreakMode
|
||||
range:(NSRange)range
|
||||
{
|
||||
[self removeAttribute:(id)kCTParagraphStyleAttributeName range:range];
|
||||
|
||||
// 创建文本对齐方式
|
||||
CTParagraphStyleSetting alignmentStyle;
|
||||
alignmentStyle.spec = kCTParagraphStyleSpecifierAlignment;//指定为对齐属性
|
||||
alignmentStyle.valueSize = sizeof(textAlignment);
|
||||
alignmentStyle.value = &textAlignment;
|
||||
|
||||
// 创建文本行间距
|
||||
CTParagraphStyleSetting lineSpaceStyle;
|
||||
lineSpaceStyle.spec = kCTParagraphStyleSpecifierLineSpacingAdjustment;
|
||||
lineSpaceStyle.valueSize = sizeof(linesSpacing);
|
||||
lineSpaceStyle.value = &linesSpacing;
|
||||
|
||||
//段落间距
|
||||
CTParagraphStyleSetting paragraphSpaceStyle;
|
||||
paragraphSpaceStyle.spec = kCTParagraphStyleSpecifierParagraphSpacing;
|
||||
paragraphSpaceStyle.value = ¶graphSpacing;
|
||||
paragraphSpaceStyle.valueSize = sizeof(paragraphSpacing);
|
||||
|
||||
//换行模式
|
||||
CTParagraphStyleSetting lineBreakStyle;
|
||||
lineBreakStyle.spec = kCTParagraphStyleSpecifierLineBreakMode;
|
||||
lineBreakStyle.value = &lineBreakMode;
|
||||
lineBreakStyle.valueSize = sizeof(lineBreakMode);
|
||||
|
||||
// 创建样式数组
|
||||
CTParagraphStyleSetting settings[] = {alignmentStyle ,lineSpaceStyle, paragraphSpaceStyle, lineBreakStyle};
|
||||
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0])); // 设置样式
|
||||
|
||||
// 设置段落属性
|
||||
[self addAttribute:(id)kCTParagraphStyleAttributeName value:(id)CFBridgingRelease(paragraphStyle) range:range];
|
||||
}
|
||||
|
||||
@end
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
//
|
||||
// TYAttributedLabel.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
// TYAttributedLabel v2.0 verson
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TYTextContainer.h"
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TYVerticalAlignment) {
|
||||
TYVerticalAlignmentTop,
|
||||
TYVerticalAlignmentCenter,
|
||||
TYVerticalAlignmentBottom,
|
||||
};
|
||||
|
||||
@class TYAttributedLabel;
|
||||
@protocol TYAttributedLabelDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
// 点击代理
|
||||
- (void)attributedLabel:(TYAttributedLabel *)attributedLabel textStorageClicked:(id<TYTextStorageProtocol>)textStorage atPoint:(CGPoint)point;
|
||||
|
||||
// 长按代理 有多个状态 begin, changes, end 都会调用,所以需要判断状态
|
||||
- (void)attributedLabel:(TYAttributedLabel *)attributedLabel textStorageLongPressed:(id<TYTextStorageProtocol>)textStorage onState:(UIGestureRecognizerState)state atPoint:(CGPoint)point;
|
||||
|
||||
// 长按非Container区域代理 有多个状态 begin, changes, end 都会调用,所以需要判断状态
|
||||
- (void)attributedLabel:(TYAttributedLabel *)attributedLabel lableLongPressOnState:(UIGestureRecognizerState)state atPoint:(CGPoint)point;
|
||||
@end
|
||||
|
||||
/**
|
||||
* TYAttributedLabel 属性文本 支持图文混排显示,支持添加image和UIView,支持自定义排版
|
||||
*/
|
||||
@interface TYAttributedLabel : UIView
|
||||
|
||||
@property (nonatomic, assign) id<TYAttributedLabelDelegate> delegate;
|
||||
|
||||
@property (nonatomic, strong) NSString *text;
|
||||
@property (nonatomic, strong) UIColor *textColor; // 文字颜色
|
||||
@property (nonatomic, strong) UIFont *font; // 文字大小
|
||||
@property (nonatomic,assign) NSInteger numberOfLines; //行数
|
||||
|
||||
@property (nonatomic, strong) UIColor *linkColor; //链接颜色
|
||||
@property (nonatomic, strong) UIColor *highlightedLinkColor;//默认nil高亮链接颜色
|
||||
@property (nonatomic, strong) UIColor *highlightedLinkBackgroundColor;//链接高亮背景颜色
|
||||
@property (nonatomic, assign) CGFloat highlightedLinkBackgroundRadius; // 高亮背景圆角
|
||||
|
||||
@property (nonatomic, assign) unichar strokeWidth; // 空心字边框宽
|
||||
@property (nonatomic, strong) UIColor *strokeColor; // 空心字边框颜色
|
||||
|
||||
@property (nonatomic, assign) unichar characterSpacing; // 字距
|
||||
@property (nonatomic, assign) CGFloat linesSpacing; // 行距
|
||||
@property (nonatomic, assign) CGFloat paragraphSpacing; // 段落间距
|
||||
|
||||
@property (nonatomic, assign) CTTextAlignment textAlignment; // 文本对齐方式 kCTTextAlignmentLeft
|
||||
@property (nonatomic, assign) CTLineBreakMode lineBreakMode; // 换行模式 kCTLineBreakByCharWrapping
|
||||
@property (nonatomic, assign) TYVerticalAlignment verticalAlignment; // 垂直对齐方式 默认是向上对齐
|
||||
|
||||
@property (nonatomic, strong) TYTextContainer *textContainer;
|
||||
|
||||
@property (nonatomic, assign) CGFloat preferredMaxLayoutWidth; // Autolayout
|
||||
|
||||
@property (nonatomic, assign) BOOL isWidthToFit; // 宽度自适应 默认NO
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取普通文本内容
|
||||
*/
|
||||
- (NSString *)text;
|
||||
|
||||
/**
|
||||
* 获取属性文本内容
|
||||
*/
|
||||
- (NSAttributedString *)attributedText;
|
||||
|
||||
/**
|
||||
* 设置普通初始化文本内容
|
||||
*
|
||||
* @param text 普通文本内容
|
||||
*/
|
||||
- (void)setText:(NSString *)text;
|
||||
|
||||
/**
|
||||
* 设置属性初始化文本内容
|
||||
*
|
||||
* @param attributedText 属性文本内容
|
||||
*/
|
||||
- (void)setAttributedText: (NSAttributedString *)attributedText;
|
||||
|
||||
/**
|
||||
* 添加 textStorage (自定义显示内容)
|
||||
*
|
||||
* @param textStorage 自定义
|
||||
*/
|
||||
- (void)addTextStorage:(id<TYTextStorageProtocol>)textStorage;
|
||||
|
||||
/**
|
||||
* 添加 textRun数组 (自定义显示内容)
|
||||
*
|
||||
*/
|
||||
- (void)addTextStorageArray:(NSArray *)textStorageArray;
|
||||
|
||||
/**
|
||||
* 调用可以自动计算frame大小(请确定label之前设置了宽度)
|
||||
*/
|
||||
- (void)sizeToFit;
|
||||
|
||||
/**
|
||||
* 获取文本真正的高度
|
||||
*/
|
||||
- (int)getHeightWithWidth:(CGFloat)width;
|
||||
|
||||
/**
|
||||
* 获取文本真正的size
|
||||
*/
|
||||
- (CGSize)getSizeWithWidth:(CGFloat)width;
|
||||
|
||||
/**
|
||||
* 设置文本位置大小 (自动计算高度,根据宽度)
|
||||
*/
|
||||
- (void)setFrameWithOrign:(CGPoint)orign Width:(CGFloat)width;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - 扩展追加内容 (Append)
|
||||
// 追加内容 (添加在AttributedString最后)
|
||||
@interface TYAttributedLabel (AppendAttributedString)
|
||||
/**
|
||||
* 追加(添加到最后) 普通文本
|
||||
*
|
||||
* @param text 普通文本
|
||||
*/
|
||||
- (void)appendText:(NSString *)text;
|
||||
|
||||
/**
|
||||
* 追加(添加到最后) 属性文本
|
||||
*
|
||||
* @param attributedText 属性文本
|
||||
*/
|
||||
- (void)appendTextAttributedString: (NSAttributedString *)attributedText;
|
||||
|
||||
/**
|
||||
* 追加(添加到最后) textStorage (自定义显示内容)
|
||||
*
|
||||
* @param textStorage 自定义Storage(自定义显示内容)
|
||||
*/
|
||||
- (void)appendTextStorage:(id<TYAppendTextStorageProtocol>)textStorage;
|
||||
|
||||
/**
|
||||
* 追加(添加到最后) textStorage 数组
|
||||
*
|
||||
* @param textStorageArray 自定义run数组(需遵循协议TYAppendTextStorageProtocol,否则不会添加)
|
||||
*/
|
||||
- (void)appendTextStorageArray:(NSArray *)textStorageArray;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - 扩展支持Link链接
|
||||
@interface TYAttributedLabel (Link)
|
||||
|
||||
/**
|
||||
* 添加 链接LinkTextStorage
|
||||
*/
|
||||
- (void)addLinkWithLinkData:(id)linkData range:(NSRange )range;
|
||||
|
||||
/**
|
||||
* 添加 链接LinkTextStorage
|
||||
*
|
||||
* @param linkData 链接携带的数据
|
||||
* @param linkColor 链接颜色
|
||||
* @param range 范围
|
||||
*/
|
||||
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor range:(NSRange )range;
|
||||
|
||||
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle range:(NSRange )range;
|
||||
|
||||
/**
|
||||
* 追加 链接LinkTextStorage
|
||||
*/
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkData:(id)linkData;
|
||||
|
||||
/**
|
||||
* 追加 链接LinkTextStorage
|
||||
*
|
||||
* @param linkText 链接文本
|
||||
* @param linkData 链接携带的数据
|
||||
*/
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor linkData:(id)linkData;
|
||||
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle linkData:(id)linkData;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - 扩展支持UIImage
|
||||
@interface TYAttributedLabel (UIImage)
|
||||
|
||||
#pragma mark - addImageStorage
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*/
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*/
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*
|
||||
* @param image image
|
||||
* @param range 所在文本位置
|
||||
* @param size 图片大小
|
||||
* @param alignment 图片对齐方式
|
||||
*/
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range
|
||||
size:(CGSize)size
|
||||
alignment: (TYDrawAlignment)alignment;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*/
|
||||
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*/
|
||||
- (void)addImageWithName:(NSString *)imageName
|
||||
range:(NSRange)range
|
||||
size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*
|
||||
* @param imageName image名
|
||||
* @param range 所在文本位置
|
||||
* @param size 图片大小
|
||||
* @param alignment 图片对齐方式
|
||||
*/
|
||||
- (void)addImageWithName:(NSString *)imageName
|
||||
range:(NSRange)range
|
||||
size:(CGSize)size
|
||||
alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
#pragma mark - appendImageStorage
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*/
|
||||
- (void)appendImage:(UIImage *)image;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*/
|
||||
- (void)appendImage:(UIImage *)image
|
||||
size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*
|
||||
* @param image image
|
||||
* @param size 图片大小
|
||||
* @param alignment 图片对齐
|
||||
*/
|
||||
- (void)appendImage:(UIImage *)image
|
||||
size:(CGSize)size
|
||||
alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*/
|
||||
- (void)appendImageWithName:(NSString *)imageName;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*/
|
||||
- (void)appendImageWithName:(NSString *)imageName size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*
|
||||
* @param imageName imageName
|
||||
* @param size 图片大小
|
||||
* @param alignment 图片对齐
|
||||
*/
|
||||
- (void)appendImageWithName:(NSString *)imageName
|
||||
size:(CGSize)size
|
||||
alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - 扩展支持UIView
|
||||
@interface TYAttributedLabel (UIView)
|
||||
|
||||
/**
|
||||
* 添加 viewStorage (添加 UI控件 需要设置frame)
|
||||
*/
|
||||
- (void)addView:(UIView *)view range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加 viewStorage (添加 UI控件 需要设置frame)
|
||||
*
|
||||
* @param view UIView (UI控件)
|
||||
* @param range 所在文本位置
|
||||
* @param alignment view对齐方式
|
||||
*/
|
||||
- (void)addView:(UIView *)view
|
||||
range:(NSRange)range
|
||||
alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
/**
|
||||
* 追加 viewStorage (添加 UI控件 需要设置frame)
|
||||
*/
|
||||
- (void)appendView:(UIView *)view;
|
||||
|
||||
/**
|
||||
* 追加 viewStorage (添加 UI控件 需要设置frame)
|
||||
*
|
||||
* @param view UIView (UI控件)
|
||||
* @param alignment view对齐
|
||||
*/
|
||||
- (void)appendView:(UIView *)view alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
@end
|
||||
+1018
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// TYDrawStorage.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYTextStorageProtocol.h"
|
||||
|
||||
@interface TYDrawStorage : NSObject<TYDrawStorageProtocol>
|
||||
|
||||
@property (nonatomic, assign) NSInteger tag; // 标识
|
||||
@property (nonatomic, assign) NSRange range; // 文本范围
|
||||
@property (nonatomic, assign) NSRange realRange;
|
||||
@property (nonatomic, assign) UIEdgeInsets margin; // 图片四周间距
|
||||
@property (nonatomic, assign) CGSize size; // 绘画物大小
|
||||
@property (nonatomic, assign) TYDrawAlignment drawAlignment; // 对齐方式
|
||||
|
||||
/**
|
||||
* 获取绘画区域上行高度(默认实现)
|
||||
*/
|
||||
- (CGFloat)getDrawRunAscentHeight;
|
||||
|
||||
/**
|
||||
* 获取绘画区域下行高度 默认实现为0(一般不需要改写)
|
||||
*/
|
||||
- (CGFloat)getDrawRunDescentHeight;
|
||||
|
||||
/**
|
||||
* 获取绘画区域宽度(默认实现)
|
||||
*/
|
||||
- (CGFloat)getDrawRunWidth;
|
||||
|
||||
/**
|
||||
* 释放内存 (一般不需要 已注释 需要在打开)
|
||||
*/
|
||||
//- (void)DrawRunDealloc;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// TYDrawStorage.m
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYDrawStorage.h"
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
@interface TYDrawStorage (){
|
||||
CGFloat _fontAscent;
|
||||
CGFloat _fontDescent;
|
||||
|
||||
NSRange _fixRange;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation TYDrawStorage
|
||||
|
||||
#pragma mark - protocol
|
||||
|
||||
- (void)currentReplacedStringNum:(NSInteger)replacedStringNum
|
||||
{
|
||||
_fixRange = [self fixRange:_range replaceStringNum:replacedStringNum];
|
||||
}
|
||||
|
||||
- (void)setTextfontAscent:(CGFloat)ascent descent:(CGFloat)descent;
|
||||
{
|
||||
_fontAscent = ascent;
|
||||
_fontDescent = -descent;
|
||||
}
|
||||
|
||||
- (void)addTextStorageWithAttributedString:(NSMutableAttributedString *)attributedString
|
||||
{
|
||||
NSRange range = _fixRange;
|
||||
if (range.location == NSNotFound) {
|
||||
return;
|
||||
}else {
|
||||
// 用空白替换
|
||||
[attributedString replaceCharactersInRange:range withString:[self spaceReplaceString]];
|
||||
// 修正range
|
||||
range = NSMakeRange(range.location, 1);
|
||||
_realRange = range;
|
||||
}
|
||||
|
||||
// 设置合适的对齐
|
||||
[self setAppropriateAlignment];
|
||||
|
||||
// 添加文本属性和runDelegate
|
||||
[self addRunDelegateWithAttributedString:attributedString range:range];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)appendTextStorageAttributedString
|
||||
{
|
||||
// 创建空字符属性文本
|
||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[self spaceReplaceString]];
|
||||
// 修正range
|
||||
_range = NSMakeRange(0, 1);
|
||||
|
||||
// 设置合适的对齐
|
||||
[self setAppropriateAlignment];
|
||||
|
||||
// 添加文本属性和runDelegate
|
||||
[self addRunDelegateWithAttributedString:attributedString range:_range];
|
||||
return attributedString;
|
||||
}
|
||||
|
||||
- (void)drawStorageWithRect:(CGRect)rect
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - public
|
||||
|
||||
- (CGFloat)getDrawRunAscentHeight
|
||||
{
|
||||
CGFloat ascent = 0;
|
||||
CGFloat height = self.size.height+_margin.bottom+_margin.top;
|
||||
switch (_drawAlignment)
|
||||
{
|
||||
case TYDrawAlignmentTop:
|
||||
ascent = height - _fontDescent;
|
||||
break;
|
||||
case TYDrawAlignmentCenter:
|
||||
{
|
||||
CGFloat baseLine = (_fontAscent + _fontDescent) / 2 - _fontDescent;
|
||||
ascent = height / 2 + baseLine;
|
||||
break;
|
||||
}
|
||||
case TYDrawAlignmentBottom:
|
||||
ascent = _fontAscent;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ascent;
|
||||
}
|
||||
|
||||
- (CGFloat)getDrawRunWidth
|
||||
{
|
||||
return self.size.width+_margin.left+_margin.right;
|
||||
}
|
||||
|
||||
- (CGFloat)getDrawRunDescentHeight
|
||||
{
|
||||
CGFloat descent = 0;
|
||||
CGFloat height = self.size.height+_margin.bottom+_margin.top;
|
||||
switch (_drawAlignment)
|
||||
{
|
||||
case TYDrawAlignmentTop:
|
||||
descent = _fontDescent;
|
||||
break;
|
||||
case TYDrawAlignmentCenter:
|
||||
{
|
||||
CGFloat baseLine = (_fontAscent + _fontDescent) / 2 - _fontDescent;
|
||||
descent = height / 2 - baseLine;
|
||||
break;
|
||||
}
|
||||
case TYDrawAlignmentBottom:
|
||||
descent = height - _fontAscent;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return descent;
|
||||
}
|
||||
|
||||
- (void)DrawRunDealloc
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - private
|
||||
|
||||
- (NSString *)spaceReplaceString
|
||||
{
|
||||
// 替换字符
|
||||
unichar objectReplacementChar = 0xFFFC;
|
||||
NSString *objectReplacementString = [NSString stringWithCharacters:&objectReplacementChar length:1];
|
||||
return objectReplacementString;
|
||||
}
|
||||
|
||||
- (void)setAppropriateAlignment
|
||||
{
|
||||
// 判断size 大小 小于 _fontAscent 把对齐设为中心 更美观
|
||||
if (_size.height <= _fontAscent + _fontDescent) {
|
||||
_drawAlignment = TYDrawAlignmentCenter;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSRange)fixRange:(NSRange)range replaceStringNum:(NSInteger)replaceStringNum
|
||||
{
|
||||
NSRange fixRange = range;
|
||||
if (range.length <= 1 || replaceStringNum < 0)
|
||||
return fixRange;
|
||||
|
||||
NSInteger location = range.location - replaceStringNum;
|
||||
NSInteger length = range.length - replaceStringNum;
|
||||
|
||||
if (location < 0 && length > 0) {
|
||||
fixRange = NSMakeRange(range.location, length);
|
||||
}else if (location < 0 && length <= 0){
|
||||
fixRange = NSMakeRange(NSNotFound, 0);
|
||||
}else {
|
||||
fixRange = NSMakeRange(range.location - replaceStringNum, range.length);
|
||||
}
|
||||
return fixRange;
|
||||
}
|
||||
|
||||
// 添加文本属性和runDelegate
|
||||
- (void)addRunDelegateWithAttributedString:(NSMutableAttributedString *)attributedString range:(NSRange)range
|
||||
{
|
||||
// 添加文本属性和runDelegate
|
||||
[attributedString addAttribute:kTYTextRunAttributedName value:self range:range];
|
||||
|
||||
//为图片设置CTRunDelegate,delegate决定留给显示内容的空间大小
|
||||
CTRunDelegateCallbacks runCallbacks;
|
||||
runCallbacks.version = kCTRunDelegateVersion1;
|
||||
runCallbacks.dealloc = TYTextRunDelegateDeallocCallback;
|
||||
runCallbacks.getAscent = TYTextRunDelegateGetAscentCallback;
|
||||
runCallbacks.getDescent = TYTextRunDelegateGetDescentCallback;
|
||||
runCallbacks.getWidth = TYTextRunDelegateGetWidthCallback;
|
||||
|
||||
CTRunDelegateRef runDelegate = CTRunDelegateCreate(&runCallbacks, (__bridge void *)(self));
|
||||
[attributedString addAttribute:(__bridge_transfer NSString *)kCTRunDelegateAttributeName value:(__bridge id)runDelegate range:range];
|
||||
CFRelease(runDelegate);
|
||||
}
|
||||
|
||||
//CTRun的回调,销毁内存的回调
|
||||
void TYTextRunDelegateDeallocCallback( void* refCon ){
|
||||
//TYDrawRun *textRun = (__bridge TYDrawRun *)refCon;
|
||||
//[textRun DrawRunDealloc];
|
||||
}
|
||||
|
||||
//CTRun的回调,获取高度
|
||||
CGFloat TYTextRunDelegateGetAscentCallback( void *refCon ){
|
||||
|
||||
TYDrawStorage *drawStorage = (__bridge TYDrawStorage *)refCon;
|
||||
return [drawStorage getDrawRunAscentHeight];
|
||||
}
|
||||
|
||||
CGFloat TYTextRunDelegateGetDescentCallback(void *refCon){
|
||||
TYDrawStorage *drawStorage = (__bridge TYDrawStorage *)refCon;
|
||||
return [drawStorage getDrawRunDescentHeight];
|
||||
}
|
||||
|
||||
//CTRun的回调,获取宽度
|
||||
CGFloat TYTextRunDelegateGetWidthCallback(void *refCon){
|
||||
|
||||
TYDrawStorage *drawStorage = (__bridge TYDrawStorage *)refCon;
|
||||
return [drawStorage getDrawRunWidth];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// TYImageCache.h
|
||||
// TYImageCache
|
||||
//
|
||||
// Created by tanyang on 25/08/15.
|
||||
// Copyright (c) 2015 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TYImageCache : NSObject
|
||||
@property (nonatomic, strong) NSString *localDirectory;
|
||||
|
||||
// 单例cache
|
||||
+ (instancetype)cache;
|
||||
|
||||
// 清除cache
|
||||
- (void)clearCache;
|
||||
|
||||
// 是否在本地找到图片,是否需要缩略图
|
||||
- (void)imageForURL:(NSString *)imageURL needThumImage:(BOOL)needThumImage found:(void(^)(UIImage* image))found notFound:(void(^)(void))notFound;
|
||||
|
||||
// 是否在本地找到图片
|
||||
- (void) imageForURL:(NSString *)imageURL found:(void(^)(UIImage* image))found notFound:(void(^)(void))notFound;
|
||||
|
||||
// 图片是否缓存
|
||||
- (BOOL)imageIsCacheForURL:(NSString *)imageURL;
|
||||
|
||||
// 同步下载保存image
|
||||
- (BOOL)saveImageFromURL:(NSString *)imageName thumbImageSize:(CGSize)thumbImageSize;
|
||||
|
||||
// 保存图片
|
||||
- (BOOL)saveImageFromURL:(NSString *)imageName data:(NSData *)imageData;
|
||||
|
||||
// 保存image和缩略图
|
||||
- (BOOL)saveImageFromURL:(NSString *)imageName thumbImageSize:(CGSize)thumbImageSize data:(NSData *)imageData;
|
||||
|
||||
// 异步下载保存image
|
||||
- (void)saveAsyncImageFromURL:(NSString *)imageURL thumbImageSize:(CGSize)thumbImageSize completion:(void(^)(BOOL isCache))completionBlock;
|
||||
|
||||
// 异步下载保存image数组
|
||||
- (void)saveAsyncImagesFromURLArray:(NSArray *)imageURLArray thumbImageSize:(CGSize)thumbImageSize completion:(void(^)(BOOL isCache))completionBlock;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,295 @@
|
||||
//
|
||||
// DBImageViewCache.m
|
||||
// DBImageView
|
||||
//
|
||||
// Created by iBo on 25/08/14.
|
||||
// Copyright (c) 2014 Daniele Bogo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYImageCache.h"
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
@interface TYImageCache (){
|
||||
NSFileManager *_fileManager;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TYImageCache
|
||||
|
||||
static TYImageCache *_instance;
|
||||
|
||||
+ (id)allocWithZone:(NSZone *)zone
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_instance = [super allocWithZone:zone];
|
||||
});
|
||||
return _instance;
|
||||
}
|
||||
|
||||
+ (instancetype)cache
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_instance = [[self alloc] init];
|
||||
});
|
||||
return _instance;
|
||||
}
|
||||
|
||||
#pragma mark - md5加密
|
||||
+ (NSString *) md5:(NSString *)str
|
||||
{
|
||||
const char *cStr = [str UTF8String];
|
||||
if (cStr == NULL) {
|
||||
cStr = "";
|
||||
}
|
||||
unsigned char result[CC_MD5_DIGEST_LENGTH];
|
||||
CC_MD5( cStr, (CC_LONG)strlen(cStr), result );
|
||||
return [NSString stringWithFormat:
|
||||
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
result[0], result[1], result[2], result[3],
|
||||
result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11],
|
||||
result[12], result[13], result[14], result[15]
|
||||
];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
self= [super init];
|
||||
|
||||
if ( self ) {
|
||||
_fileManager = [NSFileManager new];
|
||||
[self createLocalDirectory];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - 创建缓存图片文件目录
|
||||
- (void) createLocalDirectory
|
||||
{
|
||||
if ( ![_fileManager fileExistsAtPath:self.localDirectory] ) {
|
||||
NSError *error;
|
||||
|
||||
if ( ![[NSFileManager defaultManager] createDirectoryAtPath:self.localDirectory withIntermediateDirectories:YES attributes:nil error:&error] ) {
|
||||
NSLog(@"[%@] ERROR: attempting to write create MyFolder directory", [self class]);
|
||||
NSAssert( FALSE, @"Failed to create directory maybe out of disk space?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 存放image的文件夹路径
|
||||
- (NSString *) localDirectory
|
||||
{
|
||||
if (_localDirectory == nil) {
|
||||
_localDirectory = [NSString stringWithFormat:@"%@/ImageCache", NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]];
|
||||
}
|
||||
|
||||
return _localDirectory;
|
||||
}
|
||||
|
||||
#pragma mark - image名字MD5加密后的路径
|
||||
- (NSString *) pathOnDiskForName:(NSString *)imageName
|
||||
{
|
||||
return [self.localDirectory stringByAppendingPathComponent:[TYImageCache md5:imageName]];
|
||||
}
|
||||
|
||||
#pragma mark - 保存image和缩略图
|
||||
- (BOOL) saveImageFromURL:(NSString *)imageName thumbImageSize:(CGSize)thumbImageSize data:(NSData *)imageData
|
||||
{
|
||||
// 转换gif 到 image
|
||||
UIImage *image = [UIImage imageWithData:imageData];
|
||||
BOOL succeed = [self saveImageFromName:imageName image:image];
|
||||
|
||||
if (!CGSizeEqualToSize(thumbImageSize,CGSizeZero)) {
|
||||
// 保存thumbImage
|
||||
image = [self scaleImage:image ToSize:thumbImageSize];
|
||||
succeed = [self saveImageFromName:[NSString stringWithFormat:@"Thumb%@",imageName] image:image];
|
||||
}
|
||||
return succeed;
|
||||
}
|
||||
|
||||
- (BOOL)saveImageFromURL:(NSString *)imageName data:(NSData *)imageData
|
||||
{
|
||||
return [self saveImageFromURL:imageName thumbImageSize:CGSizeZero data:imageData];
|
||||
}
|
||||
|
||||
#pragma mark - 保存image根据UImage
|
||||
- (BOOL)saveImageFromName:(NSString *)imageName image:(UIImage *)image
|
||||
{
|
||||
if (!image) {
|
||||
return NO;
|
||||
}
|
||||
if ([[imageName lowercaseString] hasSuffix:@".png"] || [[imageName lowercaseString] hasSuffix:@".bmp"]) {
|
||||
// png图片
|
||||
[UIImagePNGRepresentation(image) writeToFile:[self pathOnDiskForName:imageName] options:NSAtomicWrite error:nil];
|
||||
return YES;
|
||||
} else if ([[imageName lowercaseString] hasSuffix:@".jpg"] || [[imageName lowercaseString] hasSuffix:@".jpeg"] || [[imageName lowercaseString] hasSuffix:@".gif"])
|
||||
{
|
||||
//jpg图片
|
||||
[UIImageJPEGRepresentation(image, 1.0) writeToFile:[self pathOnDiskForName:imageName] options:NSAtomicWrite error:nil];
|
||||
return YES;
|
||||
} else {
|
||||
// 未知图片类型
|
||||
NSLog(@"文件后缀名未知! CTImageCache ");
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 从网络下载缓存image
|
||||
// 同步下载保存image
|
||||
- (BOOL)saveImageFromURL:(NSString *)imageName thumbImageSize:(CGSize)thumbImageSize
|
||||
{
|
||||
// 从网络上加载图片
|
||||
NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]];
|
||||
if (!data) {
|
||||
return NO;
|
||||
}
|
||||
// 缓存图片
|
||||
return [self saveImageFromURL:imageName thumbImageSize:thumbImageSize data:data];
|
||||
}
|
||||
|
||||
// 异步下载保存image
|
||||
- (void)saveAsyncImageFromURL:(NSString *)imageURL thumbImageSize:(CGSize)thumbImageSize completion:(void(^)(BOOL isCache))completionBlock
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 异步操作
|
||||
// 从网络上 缓存图片
|
||||
BOOL isCached = [self saveImageFromURL:imageURL thumbImageSize:thumbImageSize];
|
||||
|
||||
if (!completionBlock) {
|
||||
return ;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// 主线程更新
|
||||
completionBlock(isCached);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)saveAsyncImagesFromURLArray:(NSArray *)imageURLArray thumbImageSize:(CGSize)thumbImageSize completion:(void (^)(BOOL))completionBlock
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// 异步操作
|
||||
// 从网络上 缓存图片
|
||||
__block BOOL isCached = NO;
|
||||
[imageURLArray enumerateObjectsUsingBlock:^(NSString *imageURL, NSUInteger idx, BOOL *stop) {
|
||||
isCached = [self saveImageFromURL:imageURL thumbImageSize:thumbImageSize];
|
||||
}];
|
||||
|
||||
if (!completionBlock) {
|
||||
return ;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// 主线程更新
|
||||
completionBlock(isCached);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 同步获取image
|
||||
- (UIImage *)imageForURL:(NSString *)imageURL
|
||||
{
|
||||
NSString *path = [self pathOnDiskForName:imageURL];
|
||||
return [[UIImage alloc] initWithContentsOfFile:path];
|
||||
}
|
||||
|
||||
#pragma mark - image是否存在
|
||||
- (void) imageForURL:(NSString *)imageURL found:(void(^)(UIImage* image))found notFound:(void(^)(void))notFound
|
||||
{
|
||||
[self imageForURL:imageURL needThumImage:NO found:found notFound:notFound];
|
||||
}
|
||||
|
||||
#pragma mark - image是否存在,是否需要缩略图,如果知道返回缩略图 否则返回原图
|
||||
- (void) imageForURL:(NSString *)imageURL needThumImage:(BOOL)needThumImage found:(void(^)(UIImage* image))found notFound:(void(^)(void))notFound
|
||||
{
|
||||
if ( !imageURL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *imageName = imageURL;
|
||||
if (needThumImage ) {
|
||||
imageName = [NSString stringWithFormat:@"Thumb%@",imageURL];
|
||||
}
|
||||
|
||||
// 图片路径
|
||||
UIImage* image = [self imageForURL:imageName];
|
||||
|
||||
if (!image && needThumImage) {
|
||||
image = [self imageForURL:imageURL];
|
||||
}
|
||||
|
||||
if (image) {
|
||||
found(image);
|
||||
}else {
|
||||
notFound();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - 是否缓存图片
|
||||
- (BOOL) imageIsCacheForURL:(NSString *)imageURL {
|
||||
|
||||
return [_fileManager fileExistsAtPath:[self pathOnDiskForName:imageURL]];
|
||||
}
|
||||
|
||||
#pragma mark - 清除内存
|
||||
- (void) clearCache
|
||||
{
|
||||
NSError *error;
|
||||
|
||||
[_fileManager removeItemAtPath:self.localDirectory error:&error];
|
||||
|
||||
if ( ![_fileManager createDirectoryAtPath:self.localDirectory withIntermediateDirectories:NO attributes:nil error:&error] )
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma mark - scale image
|
||||
|
||||
// 返回适应到targetSize的合适图片image
|
||||
- (UIImage *)scaleImage:(UIImage *)sourceImage ToSize:(CGSize)targetSize
|
||||
{
|
||||
CGFloat width = sourceImage.size.width;
|
||||
CGFloat height = sourceImage.size.height;
|
||||
CGFloat targetWidth = targetSize.width;
|
||||
CGFloat targetHeight = targetSize.height;
|
||||
CGFloat scaleFactor = 0.0;
|
||||
CGFloat scaledWidth = targetWidth;
|
||||
CGFloat scaledHeight = targetHeight;
|
||||
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
|
||||
if (CGSizeEqualToSize(sourceImage.size, targetSize) == NO) {
|
||||
CGFloat widthFactor = targetWidth / width;
|
||||
CGFloat heightFactor = targetHeight / height;
|
||||
if (widthFactor < heightFactor)
|
||||
scaleFactor = widthFactor;
|
||||
else
|
||||
scaleFactor = heightFactor;
|
||||
scaledWidth = width * scaleFactor;
|
||||
scaledHeight = height * scaleFactor;
|
||||
// center the image
|
||||
if (widthFactor < heightFactor) {
|
||||
|
||||
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
|
||||
} else if (widthFactor > heightFactor) {
|
||||
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
|
||||
}
|
||||
}
|
||||
// this is actually the interesting part:
|
||||
UIGraphicsBeginImageContext(targetSize);
|
||||
CGRect thumbnailRect = CGRectZero;
|
||||
thumbnailRect.origin = thumbnailPoint;
|
||||
thumbnailRect.size.width = scaledWidth;
|
||||
thumbnailRect.size.height = scaledHeight;
|
||||
[sourceImage drawInRect:thumbnailRect];
|
||||
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
if(newImage == nil)
|
||||
NSLog(@"could not scale image");
|
||||
return newImage ;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// TYDrawImageStorage.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYDrawStorage.h"
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
TYImageAlignmentCenter, // 图片居中
|
||||
TYImageAlignmentLeft, // 图片左对齐
|
||||
TYImageAlignmentRight, // 图片右对齐
|
||||
TYImageAlignmentFill // 图片拉伸填充
|
||||
} TYImageAlignment;
|
||||
|
||||
@interface TYImageStorage : TYDrawStorage<TYViewStorageProtocol>
|
||||
|
||||
@property (nonatomic, strong) UIImage *image;
|
||||
|
||||
@property (nonatomic, strong) NSString *imageName;
|
||||
|
||||
@property (nonatomic, strong) NSURL *imageURL;
|
||||
|
||||
@property (nonatomic, strong) NSString *placeholdImageName;
|
||||
|
||||
@property (nonatomic, assign) TYImageAlignment imageAlignment; // default center
|
||||
|
||||
@property (nonatomic, assign) BOOL cacheImageOnMemory; // default NO ,if YES can improve performance,but increase memory
|
||||
@end
|
||||
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// TYDrawImageStorage.m
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYImageStorage.h"
|
||||
#import "TYImageCache.h"
|
||||
|
||||
@interface TYImageStorage ()
|
||||
@property (nonatomic, weak) UIView *ownerView;
|
||||
@property (nonatomic, assign) BOOL isNeedUpdateFrame;
|
||||
@end
|
||||
|
||||
@implementation TYImageStorage
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_cacheImageOnMemory = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - protocol
|
||||
|
||||
- (void)setOwnerView:(UIView *)ownerView
|
||||
{
|
||||
_ownerView = ownerView;
|
||||
|
||||
if (!ownerView || !_imageURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([_imageURL isKindOfClass:[NSURL class]]
|
||||
&& ![[TYImageCache cache] imageIsCacheForURL:_imageURL.absoluteString]) {
|
||||
|
||||
[[TYImageCache cache]saveAsyncImageFromURL:_imageURL.absoluteString thumbImageSize:self.size completion:^(BOOL isCache) {
|
||||
|
||||
if (self.isNeedUpdateFrame) {
|
||||
if (ownerView && isCache) {
|
||||
[ownerView setNeedsDisplay];
|
||||
}
|
||||
self.isNeedUpdateFrame = NO;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawStorageWithRect:(CGRect)rect
|
||||
{
|
||||
__block UIImage *image = nil;
|
||||
if (_image) {
|
||||
// 本地图片名
|
||||
image = _image;
|
||||
}else if (_imageName){
|
||||
// 图片网址
|
||||
image = [UIImage imageNamed:_imageName];
|
||||
if (_cacheImageOnMemory) {
|
||||
_image = image;
|
||||
}
|
||||
} else if (_imageURL){
|
||||
// 图片数据
|
||||
[[TYImageCache cache] imageForURL:_imageURL.absoluteString needThumImage:NO found:^(UIImage *loaceImage) {
|
||||
image = loaceImage;
|
||||
if (self.cacheImageOnMemory) {
|
||||
self.image = image;
|
||||
}
|
||||
} notFound:^{
|
||||
image = self.placeholdImageName ? [UIImage imageNamed:self.placeholdImageName] : nil;
|
||||
self.isNeedUpdateFrame = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
if (image) {
|
||||
CGRect fitRect = [self rectFitOriginSize:image.size byRect:rect];
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
CGContextDrawImage(context, fitRect, image.CGImage);
|
||||
}
|
||||
}
|
||||
|
||||
- (CGRect)rectFitOriginSize:(CGSize)size byRect:(CGRect)byRect{
|
||||
if (_imageAlignment == TYImageAlignmentFill) {
|
||||
return byRect;
|
||||
}
|
||||
CGRect scaleRect = byRect;
|
||||
CGFloat targetWidth = byRect.size.width;
|
||||
CGFloat targetHeight = byRect.size.height;
|
||||
CGFloat widthFactor = targetWidth / size.width;
|
||||
CGFloat heightFactor = targetHeight / size.height;
|
||||
CGFloat scaleFactor = MIN(widthFactor, heightFactor);
|
||||
CGFloat scaledWidth = size.width * scaleFactor;
|
||||
CGFloat scaledHeight = size.height * scaleFactor;
|
||||
scaleRect.size = CGSizeMake(scaledWidth, scaledHeight);
|
||||
// center the image
|
||||
if (widthFactor < heightFactor) {
|
||||
scaleRect.origin.y += (targetHeight - scaledHeight) * 0.5;
|
||||
} else if (widthFactor > heightFactor) {
|
||||
switch (_imageAlignment) {
|
||||
case TYImageAlignmentCenter:
|
||||
scaleRect.origin.x += (targetWidth - scaledWidth) * 0.5;
|
||||
break;
|
||||
case TYImageAlignmentRight:
|
||||
scaleRect.origin.x += (targetWidth - scaledWidth);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return scaleRect;
|
||||
}
|
||||
|
||||
// override
|
||||
- (void)didNotDrawRun
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// TYLinkTextStorage.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYTextStorage.h"
|
||||
|
||||
@interface TYLinkTextStorage : TYTextStorage<TYLinkStorageProtocol>
|
||||
|
||||
// textColor 链接颜色 如未设置就是TYAttributedLabel的linkColor
|
||||
// TYAttributedLabel的 highlightedLinkBackgroundColor 高亮背景颜色
|
||||
// underLineStyle 下划线样式(无,单 双) 默认单
|
||||
// modifier 下划线样式 (点 线)默认线
|
||||
|
||||
@property (nonatomic, strong) id linkData; // 链接携带的数据
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// TYLinkTextStorage.m
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYLinkTextStorage.h"
|
||||
|
||||
@implementation TYLinkTextStorage
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
self.underLineStyle = kCTUnderlineStyleSingle;
|
||||
self.modifier = kCTUnderlinePatternSolid;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - protocol
|
||||
|
||||
- (void)addTextStorageWithAttributedString:(NSMutableAttributedString *)attributedString
|
||||
{
|
||||
[super addTextStorageWithAttributedString:attributedString];
|
||||
[attributedString addAttribute:kTYTextRunAttributedName value:self range:self.range];
|
||||
self.text = [attributedString.string substringWithRange:self.range];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// TYTextContainer+Extended.m
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/6/7.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYTextContainer.h"
|
||||
|
||||
@implementation TYTextContainer (Link)
|
||||
|
||||
#pragma mark - addLink
|
||||
- (void)addLinkWithLinkData:(id)linkData range:(NSRange)range
|
||||
{
|
||||
[self addLinkWithLinkData:linkData linkColor:nil range:range];
|
||||
}
|
||||
|
||||
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor range:(NSRange )range;
|
||||
{
|
||||
[self addLinkWithLinkData:linkData linkColor:linkColor underLineStyle:kCTUnderlineStyleSingle range:range];
|
||||
}
|
||||
|
||||
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle range:(NSRange )range
|
||||
{
|
||||
TYLinkTextStorage *linkTextStorage = [[TYLinkTextStorage alloc] init];
|
||||
linkTextStorage.range = range;
|
||||
linkTextStorage.textColor = linkColor;
|
||||
linkTextStorage.linkData = linkData;
|
||||
linkTextStorage.underLineStyle = underLineStyle;
|
||||
[self addTextStorage:linkTextStorage];
|
||||
}
|
||||
|
||||
#pragma mark - appendLink
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkData:(id)linkData
|
||||
{
|
||||
[self appendLinkWithText:linkText linkFont:linkFont linkColor:nil linkData:linkData];
|
||||
}
|
||||
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor linkData:(id)linkData
|
||||
{
|
||||
[self appendLinkWithText:linkText linkFont:linkFont linkColor:linkColor underLineStyle:kCTUnderlineStyleSingle linkData:linkData];
|
||||
}
|
||||
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle linkData:(id)linkData
|
||||
{
|
||||
TYLinkTextStorage *linkTextStorage = [[TYLinkTextStorage alloc] init];
|
||||
linkTextStorage.text = linkText;
|
||||
linkTextStorage.font = linkFont;
|
||||
linkTextStorage.textColor = linkColor;
|
||||
linkTextStorage.linkData = linkData;
|
||||
linkTextStorage.underLineStyle = underLineStyle;
|
||||
[self appendTextStorage:linkTextStorage];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TYTextContainer (UIImage)
|
||||
|
||||
#pragma mark addImage
|
||||
|
||||
- (void)addImageContent:(id)imageContent range:(NSRange)range size:(CGSize)size alignment:(TYDrawAlignment)alignment
|
||||
{
|
||||
TYImageStorage *imageStorage = [[TYImageStorage alloc] init];
|
||||
if ([imageContent isKindOfClass:[UIImage class]]) {
|
||||
imageStorage.image = imageContent;
|
||||
}else if ([imageContent isKindOfClass:[NSString class]]){
|
||||
imageStorage.imageName = imageContent;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
imageStorage.drawAlignment = alignment;
|
||||
imageStorage.range = range;
|
||||
imageStorage.size = size;
|
||||
[self addTextStorage:imageStorage];
|
||||
}
|
||||
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range size:(CGSize)size alignment:(TYDrawAlignment)alignment
|
||||
{
|
||||
[self addImageContent:image range:range size:size alignment:alignment];
|
||||
}
|
||||
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range size:(CGSize)size
|
||||
{
|
||||
[self addImage:image range:range size:size alignment:TYDrawAlignmentTop];
|
||||
}
|
||||
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range
|
||||
{
|
||||
[self addImage:image range:range size:image.size];
|
||||
}
|
||||
|
||||
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range size:(CGSize)size alignment:(TYDrawAlignment)alignment
|
||||
{
|
||||
[self addImageContent:imageName range:range size:size alignment:alignment];
|
||||
}
|
||||
|
||||
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range size:(CGSize)size
|
||||
{
|
||||
[self addImageWithName:imageName range:range size:size alignment:TYDrawAlignmentTop];
|
||||
}
|
||||
|
||||
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range
|
||||
{
|
||||
[self addImageWithName:imageName range:range size:CGSizeMake(self.font.pointSize, self.font.ascender)];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - appendImage
|
||||
|
||||
- (void)appendImageContent:(id)imageContent size:(CGSize)size alignment:(TYDrawAlignment)alignment
|
||||
{
|
||||
TYImageStorage *imageStorage = [[TYImageStorage alloc] init];
|
||||
if ([imageContent isKindOfClass:[UIImage class]]) {
|
||||
imageStorage.image = imageContent;
|
||||
}else if ([imageContent isKindOfClass:[NSString class]]){
|
||||
imageStorage.imageName = imageContent;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
imageStorage.drawAlignment = alignment;
|
||||
imageStorage.size = size;
|
||||
[self appendTextStorage:imageStorage];
|
||||
}
|
||||
|
||||
- (void)appendImage:(UIImage *)image size:(CGSize)size alignment:(TYDrawAlignment)alignment
|
||||
{
|
||||
[self appendImageContent:image size:size alignment:alignment];
|
||||
}
|
||||
|
||||
- (void)appendImage:(UIImage *)image size:(CGSize)size
|
||||
{
|
||||
[self appendImage:image size:size alignment:TYDrawAlignmentTop];
|
||||
}
|
||||
|
||||
- (void)appendImage:(UIImage *)image
|
||||
{
|
||||
[self appendImage:image size:image.size];
|
||||
}
|
||||
|
||||
- (void)appendImageWithName:(NSString *)imageName size:(CGSize)size alignment:(TYDrawAlignment)alignment
|
||||
{
|
||||
[self appendImageContent:imageName size:size alignment:alignment];
|
||||
}
|
||||
|
||||
- (void)appendImageWithName:(NSString *)imageName size:(CGSize)size
|
||||
{
|
||||
[self appendImageWithName:imageName size:size alignment:TYDrawAlignmentTop];
|
||||
}
|
||||
|
||||
- (void)appendImageWithName:(NSString *)imageName
|
||||
{
|
||||
[self appendImageWithName:imageName size:CGSizeMake(self.font.pointSize, self.font.ascender)];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation TYTextContainer (UIView)
|
||||
|
||||
#pragma mark - addView
|
||||
|
||||
- (void)addView:(UIView *)view range:(NSRange)range alignment:(TYDrawAlignment)alignment
|
||||
{
|
||||
TYViewStorage *viewStorage = [[TYViewStorage alloc] init];
|
||||
viewStorage.drawAlignment = alignment;
|
||||
viewStorage.view = view;
|
||||
viewStorage.range = range;
|
||||
|
||||
[self addTextStorage:viewStorage];
|
||||
}
|
||||
|
||||
- (void)addView:(UIView *)view range:(NSRange)range
|
||||
{
|
||||
[self addView:view range:range alignment:TYDrawAlignmentTop];
|
||||
}
|
||||
|
||||
#pragma mark - appendView
|
||||
|
||||
- (void)appendView:(UIView *)view alignment:(TYDrawAlignment)alignment
|
||||
{
|
||||
TYViewStorage *viewStorage = [[TYViewStorage alloc] init];
|
||||
viewStorage.drawAlignment = alignment;
|
||||
viewStorage.view = view;
|
||||
|
||||
[self appendTextStorage:viewStorage];
|
||||
}
|
||||
|
||||
- (void)appendView:(UIView *)view
|
||||
{
|
||||
[self appendView:view alignment:TYDrawAlignmentTop];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,297 @@
|
||||
//
|
||||
// TYTextContainer.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/6/4.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TYTextStorageProtocol.h"
|
||||
#import "NSMutableAttributedString+TY.h"
|
||||
#import "TYTextStorage.h"
|
||||
#import "TYLinkTextStorage.h"
|
||||
#import "TYDrawStorage.h"
|
||||
#import "TYImageStorage.h"
|
||||
#import "TYViewStorage.h"
|
||||
|
||||
@interface TYTextContainer : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSString *text;
|
||||
@property (nonatomic, strong) NSAttributedString *attributedText;
|
||||
|
||||
@property (nonatomic, assign) NSInteger numberOfLines; //行数
|
||||
@property (nonatomic, strong) UIColor *textColor; // 文字颜色
|
||||
@property (nonatomic, strong) UIColor *linkColor; //链接颜色
|
||||
@property (nonatomic, strong) UIFont *font; // 文字大小
|
||||
|
||||
@property (nonatomic, assign) unichar strokeWidth; // 空心字边框宽
|
||||
@property (nonatomic, strong) UIColor *strokeColor; // 空心字边框颜色
|
||||
|
||||
@property (nonatomic, assign) unichar characterSpacing; // 字距
|
||||
@property (nonatomic, assign) CGFloat linesSpacing; // 行距
|
||||
@property (nonatomic, assign) CGFloat paragraphSpacing; // 段落间距
|
||||
|
||||
@property (nonatomic, assign) CTTextAlignment textAlignment; // 文本对齐方式 kCTTextAlignmentLeft
|
||||
@property (nonatomic, assign) CTLineBreakMode lineBreakMode; // 换行模式 kCTLineBreakByCharWrapping
|
||||
|
||||
@property (nonatomic, assign) BOOL isWidthToFit; // 宽度自适应
|
||||
|
||||
// after createTextContainer, have value
|
||||
@property (nonatomic, assign, readonly) CGFloat textHeight;
|
||||
@property (nonatomic, assign, readonly) CGFloat textWidth;
|
||||
// after createTextContainer, have value
|
||||
@property (nonatomic, strong, readonly) NSArray *textStorages;
|
||||
|
||||
/**
|
||||
* 生成文本容器textContainer
|
||||
*/
|
||||
- (instancetype)createTextContainerWithTextWidth:(CGFloat)textWidth;
|
||||
|
||||
- (instancetype)createTextContainerWithContentSize:(CGSize)contentSize;
|
||||
|
||||
/**
|
||||
* 生成属性字符串
|
||||
*/
|
||||
- (NSAttributedString *)createAttributedString;
|
||||
|
||||
/**
|
||||
* 获取文本的size
|
||||
*/
|
||||
- (CGSize)getSuggestedSizeWithFramesetter:(CTFramesetterRef)framesetter width:(CGFloat)width;
|
||||
|
||||
/**
|
||||
* 获取文本高度
|
||||
*/
|
||||
- (CGFloat)getHeightWithFramesetter:(CTFramesetterRef)framesetter width:(CGFloat)width;
|
||||
|
||||
@end
|
||||
|
||||
@interface TYTextContainer (Add)
|
||||
/**
|
||||
* 添加 textStorage (自定义显示内容)
|
||||
*
|
||||
* @param textStorage 自定义
|
||||
*/
|
||||
- (void)addTextStorage:(id<TYTextStorageProtocol>)textStorage;
|
||||
|
||||
/**
|
||||
* 添加 textRun数组 (自定义显示内容)
|
||||
*
|
||||
*/
|
||||
- (void)addTextStorageArray:(NSArray *)textStorageArray;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - append
|
||||
@interface TYTextContainer (Append)
|
||||
|
||||
/**
|
||||
* 追加(添加到最后) 普通文本
|
||||
*
|
||||
* @param text 普通文本
|
||||
*/
|
||||
- (void)appendText:(NSString *)text;
|
||||
|
||||
/**
|
||||
* 追加(添加到最后) 属性文本
|
||||
*
|
||||
* @param attributedText 属性文本
|
||||
*/
|
||||
- (void)appendTextAttributedString: (NSAttributedString *)attributedText;
|
||||
|
||||
/**
|
||||
* 追加(添加到最后) textStorage (自定义显示内容)
|
||||
*
|
||||
* @param textStorage 自定义Storage(自定义显示内容)
|
||||
*/
|
||||
- (void)appendTextStorage:(id<TYAppendTextStorageProtocol>)textStorage;
|
||||
|
||||
/**
|
||||
* 追加(添加到最后) textStorage 数组
|
||||
*
|
||||
* @param textStorageArray 自定义run数组(需遵循协议TYAppendTextStorageProtocol,否则不会添加)
|
||||
*/
|
||||
- (void)appendTextStorageArray:(NSArray *)textStorageArray;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Link
|
||||
@interface TYTextContainer (Link)
|
||||
|
||||
/**
|
||||
* 添加 链接LinkTextStorage
|
||||
*/
|
||||
- (void)addLinkWithLinkData:(id)linkData range:(NSRange )range;
|
||||
|
||||
/**
|
||||
* 添加 链接LinkTextStorage
|
||||
*/
|
||||
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor range:(NSRange )range;
|
||||
|
||||
/**
|
||||
* 添加 链接LinkTextStorage
|
||||
*
|
||||
* @param linkData 链接携带的数据
|
||||
* @param linkColor 链接颜色
|
||||
* @param underLineStyle 下划线样式(无,单 双) 默认单
|
||||
* @param range 范围
|
||||
*/
|
||||
- (void)addLinkWithLinkData:(id)linkData linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle range:(NSRange )range;
|
||||
|
||||
/**
|
||||
* 追加 链接LinkTextStorage
|
||||
*/
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkData:(id)linkData;
|
||||
|
||||
/**
|
||||
* 追加 链接LinkTextStorage
|
||||
*/
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor linkData:(id)linkData;
|
||||
|
||||
/**
|
||||
* 追加 链接LinkTextStorage
|
||||
*
|
||||
* @param linkText 链接文本
|
||||
* @param linkData 链接携带的数据
|
||||
* @param underLineStyle 下划线样式(无,单 双) 默认单
|
||||
*/
|
||||
- (void)appendLinkWithText:(NSString *)linkText linkFont:(UIFont *)linkFont linkColor:(UIColor *)linkColor underLineStyle:(CTUnderlineStyle)underLineStyle linkData:(id)linkData;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - 扩展支持UIImage
|
||||
@interface TYTextContainer (UIImage)
|
||||
|
||||
#pragma mark - addImageStorage
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*/
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*/
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*
|
||||
* @param image image
|
||||
* @param range 所在文本位置
|
||||
* @param size 图片大小
|
||||
* @param alignment 图片对齐方式
|
||||
*/
|
||||
- (void)addImage:(UIImage *)image range:(NSRange)range
|
||||
size:(CGSize)size
|
||||
alignment: (TYDrawAlignment)alignment;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*/
|
||||
- (void)addImageWithName:(NSString *)imageName range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*/
|
||||
- (void)addImageWithName:(NSString *)imageName
|
||||
range:(NSRange)range
|
||||
size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* 添加 imageStorage image数据
|
||||
*
|
||||
* @param imageName image名
|
||||
* @param range 所在文本位置
|
||||
* @param size 图片大小
|
||||
* @param alignment 图片对齐方式
|
||||
*/
|
||||
- (void)addImageWithName:(NSString *)imageName
|
||||
range:(NSRange)range
|
||||
size:(CGSize)size
|
||||
alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
#pragma mark - appendImageStorage
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*/
|
||||
- (void)appendImage:(UIImage *)image;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*/
|
||||
- (void)appendImage:(UIImage *)image
|
||||
size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*
|
||||
* @param image image
|
||||
* @param size 图片大小
|
||||
* @param alignment 图片对齐
|
||||
*/
|
||||
- (void)appendImage:(UIImage *)image
|
||||
size:(CGSize)size
|
||||
alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*/
|
||||
- (void)appendImageWithName:(NSString *)imageName;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*/
|
||||
- (void)appendImageWithName:(NSString *)imageName size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* 追加 imageStorage image数据
|
||||
*
|
||||
* @param imageName imageName
|
||||
* @param size 图片大小
|
||||
* @param alignment 图片对齐
|
||||
*/
|
||||
- (void)appendImageWithName:(NSString *)imageName
|
||||
size:(CGSize)size
|
||||
alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - 扩展支持UIView
|
||||
@interface TYTextContainer (UIView)
|
||||
|
||||
/**
|
||||
* 添加 viewStorage (添加 UI控件 需要设置frame)
|
||||
*/
|
||||
- (void)addView:(UIView *)view range:(NSRange)range;
|
||||
|
||||
/**
|
||||
* 添加 viewStorage (添加 UI控件 需要设置frame)
|
||||
*
|
||||
* @param view UIView (UI控件)
|
||||
* @param range 所在文本位置
|
||||
* @param alignment view对齐方式
|
||||
*/
|
||||
- (void)addView:(UIView *)view
|
||||
range:(NSRange)range
|
||||
alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
/**
|
||||
* 追加 viewStorage (添加 UI控件 需要设置frame)
|
||||
*/
|
||||
- (void)appendView:(UIView *)view;
|
||||
|
||||
/**
|
||||
* 追加 viewStorage (添加 UI控件 需要设置frame)
|
||||
*
|
||||
* @param view UIView (UI控件)
|
||||
* @param alignment view对齐
|
||||
*/
|
||||
- (void)appendView:(UIView *)view alignment:(TYDrawAlignment)alignment;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,678 @@
|
||||
//
|
||||
// TYTextContainer.m
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/6/4.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYTextContainer.h"
|
||||
|
||||
#define kTextColor [UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1]
|
||||
#define kLinkColor [UIColor colorWithRed:0/255.0 green:91/255.0 blue:255/255.0 alpha:1]
|
||||
|
||||
// this code quote TTTAttributedLabel
|
||||
static inline CGSize CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(CTFramesetterRef framesetter, NSAttributedString *attributedString, CGSize size, NSUInteger numberOfLines) {
|
||||
CFRange rangeToSize = CFRangeMake(0, (CFIndex)[attributedString length]);
|
||||
CGSize constraints = CGSizeMake(size.width, MAXFLOAT);
|
||||
|
||||
if (numberOfLines > 0) {
|
||||
// If the line count of the label more than 1, limit the range to size to the number of lines that have been set
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGPathAddRect(path, NULL, CGRectMake(0.0f, 0.0f, constraints.width, MAXFLOAT));
|
||||
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
|
||||
CFArrayRef lines = CTFrameGetLines(frame);
|
||||
|
||||
if (CFArrayGetCount(lines) > 0) {
|
||||
NSInteger lastVisibleLineIndex = MIN((CFIndex)numberOfLines, CFArrayGetCount(lines)) - 1;
|
||||
CTLineRef lastVisibleLine = CFArrayGetValueAtIndex(lines, lastVisibleLineIndex);
|
||||
|
||||
CFRange rangeToLayout = CTLineGetStringRange(lastVisibleLine);
|
||||
rangeToSize = CFRangeMake(0, rangeToLayout.location + rangeToLayout.length);
|
||||
}
|
||||
|
||||
CFRelease(frame);
|
||||
CFRelease(path);
|
||||
}
|
||||
|
||||
CGSize suggestedSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, rangeToSize, NULL, constraints, NULL);
|
||||
|
||||
return CGSizeMake(ceil(suggestedSize.width), ceil(suggestedSize.height));
|
||||
}
|
||||
|
||||
@interface TYTextContainer()
|
||||
@property (nonatomic, strong) NSMutableArray *textStorageArray; // run数组
|
||||
@property (nonatomic, strong) NSArray *textStorages; // run array copy
|
||||
|
||||
@property (nonatomic, strong) NSDictionary *drawRectDictionary;
|
||||
@property (nonatomic, strong) NSDictionary *runRectDictionary; // runRect字典
|
||||
@property (nonatomic, strong) NSDictionary *linkRectDictionary; // linkRect字典
|
||||
|
||||
@property (nonatomic, assign) NSInteger replaceStringNum; // 图片替换字符数
|
||||
@property (nonatomic, strong) NSMutableAttributedString *attString;
|
||||
@property (nonatomic, assign) CTFrameRef frameRef;
|
||||
@property (nonatomic, assign) CGFloat textHeight;
|
||||
@property (nonatomic, assign) CGFloat textWidth;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TYTextContainer
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
[self setupProperty];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - getter
|
||||
|
||||
- (NSMutableArray *)textStorageArray
|
||||
{
|
||||
if (_textStorageArray == nil) {
|
||||
_textStorageArray = [NSMutableArray array];
|
||||
}
|
||||
return _textStorageArray;
|
||||
}
|
||||
|
||||
- (NSString *)text{
|
||||
return _attString.string;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedText
|
||||
{
|
||||
return [_attString copy];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)createAttributedString
|
||||
{
|
||||
[self addTextStoragesWithAtrributedString:_attString];
|
||||
if (_attString == nil) {
|
||||
_attString = [[NSMutableAttributedString alloc] init];
|
||||
}
|
||||
return [_attString copy];
|
||||
}
|
||||
|
||||
#pragma mark - setter
|
||||
- (void)setupProperty
|
||||
{
|
||||
_font = [UIFont systemFontOfSize:15];
|
||||
_characterSpacing = 1;
|
||||
_linesSpacing = 2;
|
||||
_paragraphSpacing = 0;
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED <= 9000
|
||||
_textAlignment = kCTLeftTextAlignment;
|
||||
#else
|
||||
_textAlignment = kCTTextAlignmentLeft;
|
||||
#endif
|
||||
_lineBreakMode = kCTLineBreakByCharWrapping;
|
||||
_textColor = kTextColor;
|
||||
_linkColor = kLinkColor;
|
||||
_replaceStringNum = 0;
|
||||
}
|
||||
|
||||
- (void)resetAllAttributed
|
||||
{
|
||||
[self resetRectDictionary];
|
||||
_textStorageArray = nil;
|
||||
_textStorages = nil;
|
||||
_replaceStringNum = 0;
|
||||
}
|
||||
|
||||
- (void)resetRectDictionary
|
||||
{
|
||||
_drawRectDictionary = nil;
|
||||
_linkRectDictionary = nil;
|
||||
_runRectDictionary = nil;
|
||||
}
|
||||
|
||||
- (void)resetFrameRef
|
||||
{
|
||||
if (_frameRef) {
|
||||
CFRelease(_frameRef);
|
||||
_frameRef = nil;
|
||||
}
|
||||
_textHeight = 0;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
_attString = [self createTextAttibuteStringWithText:text];
|
||||
[self resetAllAttributed];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText
|
||||
{
|
||||
if (attributedText == nil) {
|
||||
_attString = [[NSMutableAttributedString alloc] init];
|
||||
}else if ([attributedText isKindOfClass:[NSMutableAttributedString class]]) {
|
||||
_attString = (NSMutableAttributedString *)attributedText;
|
||||
}else {
|
||||
_attString = [[NSMutableAttributedString alloc] initWithAttributedString:attributedText];
|
||||
}
|
||||
[self resetAllAttributed];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
|
||||
- (void)setTextColor:(UIColor *)textColor
|
||||
{
|
||||
if (textColor && _textColor != textColor){
|
||||
_textColor = textColor;
|
||||
|
||||
[_attString addAttributeTextColor:textColor];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont *)font
|
||||
{
|
||||
if (font && _font != font){
|
||||
_font = font;
|
||||
|
||||
[_attString addAttributeFont:font];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setStrokeWidth:(unichar)strokeWidth
|
||||
{
|
||||
if (_strokeWidth != strokeWidth) {
|
||||
_strokeWidth = strokeWidth;
|
||||
[_attString addAttributeStrokeWidth:strokeWidth strokeColor:_strokeColor];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setStrokeColor:(UIColor *)strokeColor
|
||||
{
|
||||
if (strokeColor && _strokeColor != strokeColor) {
|
||||
_strokeColor = strokeColor;
|
||||
[_attString addAttributeStrokeWidth:_strokeWidth strokeColor:strokeColor];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setCharacterSpacing:(unichar)characterSpacing
|
||||
{
|
||||
if (_characterSpacing != characterSpacing) {
|
||||
_characterSpacing = characterSpacing;
|
||||
|
||||
[_attString addAttributeCharacterSpacing:characterSpacing];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setLinesSpacing:(CGFloat)linesSpacing
|
||||
{
|
||||
if (_linesSpacing != linesSpacing) {
|
||||
_linesSpacing = linesSpacing;
|
||||
|
||||
[self addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setParagraphSpacing:(CGFloat)paragraphSpacing
|
||||
{
|
||||
if (_paragraphSpacing != paragraphSpacing) {
|
||||
_paragraphSpacing = paragraphSpacing;
|
||||
[self addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTextAlignment:(CTTextAlignment)textAlignment
|
||||
{
|
||||
if (_textAlignment != textAlignment) {
|
||||
_textAlignment = textAlignment;
|
||||
|
||||
[self addAttributeAlignmentStyle:textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setLineBreakMode:(CTLineBreakMode)lineBreakMode
|
||||
{
|
||||
if (_lineBreakMode != lineBreakMode) {
|
||||
_lineBreakMode = lineBreakMode;
|
||||
|
||||
[self addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode];
|
||||
[self resetFrameRef];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - create text attibuteString
|
||||
- (NSMutableAttributedString *)createTextAttibuteStringWithText:(NSString *)text
|
||||
{
|
||||
if (text.length <= 0) {
|
||||
return [[NSMutableAttributedString alloc] init];
|
||||
}
|
||||
// 创建属性文本
|
||||
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:text];
|
||||
|
||||
// 添加文本颜色 字体属性
|
||||
[self addTextColorAndFontWithAtrributedString:attString];
|
||||
|
||||
// 添加文本段落样式
|
||||
[self addTextParaphStyleWithAtrributedString:attString];
|
||||
|
||||
return attString;
|
||||
}
|
||||
|
||||
// 添加文本颜色 字体属性
|
||||
- (void)addTextColorAndFontWithAtrributedString:(NSMutableAttributedString *)attString
|
||||
{
|
||||
// 添加文本字体
|
||||
[attString addAttributeFont:_font];
|
||||
|
||||
// 添加文本颜色
|
||||
[attString addAttributeTextColor:_textColor];
|
||||
|
||||
// 添加空心字体
|
||||
if (_strokeWidth > 0) {
|
||||
[attString addAttributeStrokeWidth:_strokeWidth strokeColor:_strokeColor];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 添加文本段落样式
|
||||
- (void)addTextParaphStyleWithAtrributedString:(NSMutableAttributedString *)attString
|
||||
{
|
||||
// 字体间距
|
||||
if (_characterSpacing)
|
||||
{
|
||||
[attString addAttributeCharacterSpacing:_characterSpacing];
|
||||
}
|
||||
|
||||
// 添加文本段落样式
|
||||
[self addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:_lineBreakMode];
|
||||
}
|
||||
|
||||
- (void)addAttributeAlignmentStyle:(CTTextAlignment)textAlignment
|
||||
lineSpaceStyle:(CGFloat)linesSpacing
|
||||
paragraphSpaceStyle:(CGFloat)paragraphSpacing
|
||||
lineBreakStyle:(CTLineBreakMode)lineBreakMode
|
||||
{
|
||||
if (lineBreakMode == kCTLineBreakByTruncatingTail)
|
||||
{
|
||||
lineBreakMode = _numberOfLines == 1 ? kCTLineBreakByCharWrapping : kCTLineBreakByWordWrapping;
|
||||
}
|
||||
[_attString addAttributeAlignmentStyle:_textAlignment lineSpaceStyle:_linesSpacing paragraphSpaceStyle:_paragraphSpacing lineBreakStyle:lineBreakMode];
|
||||
}
|
||||
|
||||
#pragma mark - add text storage atrributed
|
||||
- (void)addTextStoragesWithAtrributedString:(NSMutableAttributedString *)attString
|
||||
{
|
||||
if (attString && _textStorageArray.count > 0) {
|
||||
|
||||
// 排序range
|
||||
[self sortTextStorageArray:_textStorageArray];
|
||||
|
||||
for (id<TYTextStorageProtocol> textStorage in _textStorageArray) {
|
||||
|
||||
// 修正图片替换字符来的误差
|
||||
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol) ]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([textStorage conformsToProtocol:@protocol(TYLinkStorageProtocol)]) {
|
||||
if (!((id<TYLinkStorageProtocol>)textStorage).textColor) {
|
||||
((id<TYLinkStorageProtocol>)textStorage).textColor = _linkColor;
|
||||
}
|
||||
}
|
||||
|
||||
// 验证范围
|
||||
if (NSMaxRange(textStorage.range) <= attString.length) {
|
||||
[textStorage addTextStorageWithAttributedString:attString];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (id<TYTextStorageProtocol> textStorage in _textStorageArray) {
|
||||
textStorage.realRange = NSMakeRange(textStorage.range.location-_replaceStringNum, textStorage.range.length);
|
||||
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol)]) {
|
||||
id<TYDrawStorageProtocol> drawStorage = (id<TYDrawStorageProtocol>)textStorage;
|
||||
NSInteger currentLenght = attString.length;
|
||||
[drawStorage setTextfontAscent:_font.ascender descent:_font.descender];
|
||||
[drawStorage currentReplacedStringNum:_replaceStringNum];
|
||||
[drawStorage addTextStorageWithAttributedString:attString];
|
||||
_replaceStringNum += currentLenght - attString.length;
|
||||
}
|
||||
}
|
||||
_textStorages = [_textStorageArray copy];
|
||||
[_textStorageArray removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sortTextStorageArray:(NSMutableArray *)textStorageArray
|
||||
{
|
||||
[textStorageArray sortUsingComparator:^NSComparisonResult(id<TYTextStorageProtocol> obj1, id<TYTextStorageProtocol> obj2) {
|
||||
if (obj1.range.location < obj2.range.location) {
|
||||
return NSOrderedAscending;
|
||||
} else if (obj1.range.location > obj2.range.location){
|
||||
return NSOrderedDescending;
|
||||
}else {
|
||||
return obj1.range.length > obj2.range.length ? NSOrderedAscending:NSOrderedDescending;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)saveTextStorageRectWithFrame:(CTFrameRef)frame
|
||||
{
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
// 获取每行
|
||||
CFArrayRef lines = CTFrameGetLines(frame);
|
||||
CGPoint lineOrigins[CFArrayGetCount(lines)];
|
||||
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);
|
||||
CGFloat viewWidth = _textWidth;
|
||||
|
||||
NSInteger numberOfLines = _numberOfLines > 0 ? MIN(_numberOfLines, CFArrayGetCount(lines)) : CFArrayGetCount(lines);
|
||||
|
||||
NSMutableDictionary *runRectDictionary = [NSMutableDictionary dictionary];
|
||||
NSMutableDictionary *linkRectDictionary = [NSMutableDictionary dictionary];
|
||||
NSMutableDictionary *drawRectDictionary = [NSMutableDictionary dictionary];
|
||||
// 获取每行有多少run
|
||||
for (int i = 0; i < numberOfLines; i++) {
|
||||
CTLineRef line = CFArrayGetValueAtIndex(lines, i);
|
||||
CGFloat lineAscent;
|
||||
CGFloat lineDescent;
|
||||
CGFloat lineLeading;
|
||||
CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);
|
||||
|
||||
CFArrayRef runs = CTLineGetGlyphRuns(line);
|
||||
// 获得每行的run
|
||||
for (int j = 0; j < CFArrayGetCount(runs); j++) {
|
||||
CGFloat runAscent;
|
||||
CGFloat runDescent;
|
||||
CGPoint lineOrigin = lineOrigins[i];
|
||||
CTRunRef run = CFArrayGetValueAtIndex(runs, j);
|
||||
// run的属性字典
|
||||
NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);
|
||||
id<TYTextStorageProtocol> textStorage = [attributes objectForKey:kTYTextRunAttributedName];
|
||||
|
||||
if (textStorage) {
|
||||
CGFloat runWidth = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, NULL);
|
||||
|
||||
if (viewWidth > 0 && runWidth > viewWidth) {
|
||||
runWidth = viewWidth;
|
||||
}
|
||||
CGRect runRect = CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL), lineOrigin.y - runDescent, runWidth, runAscent + runDescent);
|
||||
|
||||
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol)]) {
|
||||
[drawRectDictionary setObject:textStorage forKey:[NSValue valueWithCGRect:runRect]];
|
||||
} else if ([textStorage conformsToProtocol:@protocol(TYLinkStorageProtocol)]) {
|
||||
[linkRectDictionary setObject:textStorage forKey:[NSValue valueWithCGRect:runRect]];
|
||||
}
|
||||
|
||||
[runRectDictionary setObject:textStorage forKey:[NSValue valueWithCGRect:runRect]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drawRectDictionary.count > 0) {
|
||||
_drawRectDictionary = [drawRectDictionary copy];
|
||||
}else {
|
||||
_drawRectDictionary = nil;
|
||||
}
|
||||
|
||||
if (runRectDictionary.count > 0) {
|
||||
// 添加响应点击rect
|
||||
[self addRunRectDictionary:[runRectDictionary copy]];
|
||||
}
|
||||
|
||||
if (linkRectDictionary.count > 0) {
|
||||
_linkRectDictionary = [linkRectDictionary copy];
|
||||
}else {
|
||||
_linkRectDictionary = nil;
|
||||
}
|
||||
}
|
||||
|
||||
// 添加响应点击rect
|
||||
- (void)addRunRectDictionary:(NSDictionary *)runRectDictionary
|
||||
{
|
||||
if (runRectDictionary.count < _runRectDictionary.count) {
|
||||
NSMutableArray *drawStorageArray = [[_runRectDictionary allValues]mutableCopy];
|
||||
// 剔除已经画出来的
|
||||
[drawStorageArray removeObjectsInArray:[runRectDictionary allValues]];
|
||||
|
||||
// 遍历不会画出来的
|
||||
for (id<TYTextStorageProtocol>drawStorage in drawStorageArray) {
|
||||
if ([drawStorage conformsToProtocol:@protocol(TYViewStorageProtocol)]) {
|
||||
[(id<TYViewStorageProtocol>)drawStorage didNotDrawRun];
|
||||
}
|
||||
}
|
||||
}
|
||||
_runRectDictionary = runRectDictionary;
|
||||
}
|
||||
|
||||
- (CGSize)getSuggestedSizeWithFramesetter:(CTFramesetterRef)framesetter width:(CGFloat)width
|
||||
{
|
||||
if (_attString == nil || width <= 0) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
if (_textHeight > 0) {
|
||||
return CGSizeMake(_textWidth > 0 ? _textWidth : width, _textHeight);
|
||||
}
|
||||
|
||||
// 是否需要更新frame
|
||||
if (framesetter == nil) {
|
||||
|
||||
framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)[self createAttributedString]);
|
||||
}else {
|
||||
CFRetain(framesetter);
|
||||
}
|
||||
|
||||
// 获得建议的size
|
||||
CGSize suggestedSize = CTFramesetterSuggestFrameSizeForAttributedStringWithConstraints(framesetter, _attString, CGSizeMake(width,MAXFLOAT), _numberOfLines);
|
||||
|
||||
CFRelease(framesetter);
|
||||
|
||||
return CGSizeMake(_isWidthToFit ? suggestedSize.width : width, suggestedSize.height+1);
|
||||
}
|
||||
- (CGFloat)getHeightWithFramesetter:(CTFramesetterRef)framesetter width:(CGFloat)width
|
||||
{
|
||||
return [self getSuggestedSizeWithFramesetter:framesetter width:width].height;
|
||||
}
|
||||
|
||||
- (CTFrameRef)createFrameRefWithFramesetter:(CTFramesetterRef)framesetter textSize:(CGSize)textSize
|
||||
{
|
||||
// 这里你需要创建一个用于绘制文本的路径区域,通过 self.bounds 使用整个视图矩形区域创建 CGPath 引用。
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGFloat textHeight = [self getHeightWithFramesetter:framesetter width:textSize.width];
|
||||
CGPathAddRect(path, NULL, CGRectMake(0, 0, textSize.width, MAX(textHeight, textSize.height)));
|
||||
|
||||
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [_attString length]), path, NULL);
|
||||
CFRelease(path);
|
||||
return frameRef;
|
||||
}
|
||||
|
||||
- (instancetype)createTextContainerWithTextWidth:(CGFloat)textWidth
|
||||
{
|
||||
return [self createTextContainerWithContentSize:CGSizeMake(textWidth, 0)];
|
||||
}
|
||||
|
||||
- (instancetype)createTextContainerWithContentSize:(CGSize)contentSize
|
||||
{
|
||||
if (_frameRef) {
|
||||
return self;
|
||||
}
|
||||
NSAttributedString *attStr = [self createAttributedString];
|
||||
// 创建CTFramesetter
|
||||
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attStr);
|
||||
|
||||
// 获得建议的size
|
||||
CGSize size = [self getSuggestedSizeWithFramesetter:framesetter width:contentSize.width];
|
||||
_textWidth = size.width;
|
||||
_textHeight = size.height;
|
||||
|
||||
// 创建CTFrameRef
|
||||
_frameRef = [self createFrameRefWithFramesetter:framesetter textSize:CGSizeMake(_textWidth, contentSize.height > 0 ? contentSize.height : _textHeight)];
|
||||
|
||||
// 释放内存
|
||||
CFRelease(framesetter);
|
||||
|
||||
// 保存run rect
|
||||
[self saveTextStorageRectWithFrame:_frameRef];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - enumerate runRect
|
||||
|
||||
- (BOOL)existRunRectDictionary
|
||||
{
|
||||
return _runRectDictionary.count != 0;
|
||||
}
|
||||
|
||||
- (BOOL)existLinkRectDictionary
|
||||
{
|
||||
return _linkRectDictionary.count != 0;
|
||||
}
|
||||
|
||||
- (BOOL)existDrawRectDictionary
|
||||
{
|
||||
return _drawRectDictionary.count != 0;
|
||||
}
|
||||
|
||||
- (void)enumerateDrawRectDictionaryUsingBlock:(void (^)(id<TYDrawStorageProtocol> drawStorage, CGRect rect))block
|
||||
{
|
||||
[_drawRectDictionary enumerateKeysAndObjectsUsingBlock:^(NSValue *rectValue, id<TYDrawStorageProtocol> drawStorage, BOOL * stop) {
|
||||
if (block) {
|
||||
block(drawStorage,[rectValue CGRectValue]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)enumerateRunRectContainPoint:(CGPoint)point viewHeight:(CGFloat)viewHeight successBlock:(void (^)(id<TYTextStorageProtocol> textStorage))successBlock
|
||||
{
|
||||
return [self enumerateRunRect:_runRectDictionary ContainPoint:point viewHeight:viewHeight successBlock:successBlock];
|
||||
}
|
||||
|
||||
- (BOOL)enumerateLinkRectContainPoint:(CGPoint)point viewHeight:(CGFloat)viewHeight successBlock:(void (^)(id<TYLinkStorageProtocol> textStorage))successBlock
|
||||
{
|
||||
return [self enumerateRunRect:_linkRectDictionary ContainPoint:point viewHeight:viewHeight successBlock:successBlock];
|
||||
}
|
||||
|
||||
- (BOOL)enumerateRunRect:(NSDictionary *)runRectDic ContainPoint:(CGPoint)point viewHeight:(CGFloat)viewHeight successBlock:(void (^)(id textStorage))successBlock
|
||||
{
|
||||
if (runRectDic.count == 0) {
|
||||
return NO;
|
||||
}
|
||||
// CoreText context coordinates are the opposite to UIKit so we flip the bounds
|
||||
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(0, viewHeight), 1.f, -1.f);
|
||||
|
||||
__block BOOL find = NO;
|
||||
// 遍历run位置字典
|
||||
[runRectDic enumerateKeysAndObjectsUsingBlock:^(NSValue *keyRectValue, id<TYTextStorageProtocol> textStorage, BOOL *stop) {
|
||||
|
||||
CGRect imgRect = [keyRectValue CGRectValue];
|
||||
CGRect rect = CGRectApplyAffineTransform(imgRect, transform);
|
||||
|
||||
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol) ]) {
|
||||
rect = UIEdgeInsetsInsetRect(rect,((id<TYDrawStorageProtocol>)textStorage).margin);
|
||||
}
|
||||
|
||||
// point 是否在rect里
|
||||
if(CGRectContainsPoint(rect, point)){
|
||||
find = YES;
|
||||
*stop = YES;
|
||||
if (successBlock) {
|
||||
successBlock(textStorage);
|
||||
}
|
||||
}
|
||||
}];
|
||||
return find;
|
||||
}
|
||||
|
||||
- (void)dealloc{
|
||||
[self resetFrameRef];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - add textStorage
|
||||
@implementation TYTextContainer (Add)
|
||||
|
||||
- (void)addTextStorage:(id<TYTextStorageProtocol>)textStorage
|
||||
{
|
||||
if (textStorage) {
|
||||
[self.textStorageArray addObject:textStorage];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addTextStorageArray:(NSArray *)textStorageArray
|
||||
{
|
||||
if (textStorageArray) {
|
||||
for (id<TYTextStorageProtocol> textStorage in textStorageArray) {
|
||||
if ([textStorage conformsToProtocol:@protocol(TYTextStorageProtocol)]) {
|
||||
[self addTextStorage:textStorage];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
#pragma mark - append textStorage
|
||||
@implementation TYTextContainer (Append)
|
||||
|
||||
- (void)appendText:(NSString *)text
|
||||
{
|
||||
NSAttributedString *attributedText = [self createTextAttibuteStringWithText:text];
|
||||
[self appendTextAttributedString:attributedText];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
|
||||
- (void)appendTextAttributedString:(NSAttributedString *)attributedText
|
||||
{
|
||||
if (attributedText == nil) {
|
||||
return;
|
||||
}
|
||||
if (_attString == nil) {
|
||||
_attString = [[NSMutableAttributedString alloc] init];
|
||||
}
|
||||
|
||||
if ([attributedText isKindOfClass:[NSMutableAttributedString class]]) {
|
||||
[self addTextParaphStyleWithAtrributedString:(NSMutableAttributedString *)attributedText];
|
||||
}
|
||||
|
||||
[_attString appendAttributedString:attributedText];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
|
||||
- (void)appendTextStorage:(id<TYAppendTextStorageProtocol>)textStorage
|
||||
{
|
||||
if (textStorage) {
|
||||
if ([textStorage conformsToProtocol:@protocol(TYDrawStorageProtocol)]) {
|
||||
[(id<TYDrawStorageProtocol>)textStorage setTextfontAscent:_font.ascender descent:_font.descender];
|
||||
} else if ([textStorage conformsToProtocol:@protocol(TYLinkStorageProtocol)]) {
|
||||
if (!((id<TYLinkStorageProtocol>)textStorage).textColor) {
|
||||
((id<TYLinkStorageProtocol>)textStorage).textColor = _linkColor;
|
||||
}
|
||||
}
|
||||
|
||||
NSAttributedString *attAppendString = [textStorage appendTextStorageAttributedString];
|
||||
textStorage.realRange = NSMakeRange(_attString.length, attAppendString.length);
|
||||
[self appendTextAttributedString:attAppendString];
|
||||
[self resetFrameRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)appendTextStorageArray:(NSArray *)textStorageArray
|
||||
{
|
||||
if (textStorageArray) {
|
||||
for (id<TYAppendTextStorageProtocol> textStorage in textStorageArray) {
|
||||
if ([textStorage conformsToProtocol:@protocol(TYAppendTextStorageProtocol)]) {
|
||||
[self appendTextStorage:textStorage];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// TYTextStorage.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYTextStorageProtocol.h"
|
||||
#import <CoreText/CoreText.h>
|
||||
|
||||
@interface TYTextStorage : NSObject<TYAppendTextStorageProtocol>
|
||||
|
||||
@property (nonatomic, assign) NSInteger tag; // 标识
|
||||
@property (nonatomic, assign) NSRange range; //如果appendStorage, range只针对追加的文本
|
||||
@property (nonatomic, assign) NSRange realRange; // label文本中实际位置,因为某些文本被替换,会导致位置偏移
|
||||
@property (nonatomic, strong) NSString *text; // 只针对追加text文本
|
||||
@property (nonatomic, strong) UIColor *textColor; // 文本颜色
|
||||
@property (nonatomic, strong) UIFont *font; // 字体
|
||||
|
||||
@property (nonatomic, assign) CTUnderlineStyle underLineStyle;// 下划线样式(单 双)(默认没有)
|
||||
@property (nonatomic, assign) CTUnderlineStyleModifiers modifier;// 下划线样式 (点 线)(默认线)
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// TYTextStorage.m
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYTextStorage.h"
|
||||
#import "NSMutableAttributedString+TY.h"
|
||||
|
||||
@implementation TYTextStorage
|
||||
|
||||
#pragma mark - protocol
|
||||
|
||||
- (void)addTextStorageWithAttributedString:(NSMutableAttributedString *)attributedString
|
||||
{
|
||||
|
||||
// 颜色
|
||||
if (_textColor) {
|
||||
[attributedString addAttributeTextColor:_textColor range:_range];
|
||||
}
|
||||
// 字体
|
||||
if (_font) {
|
||||
[attributedString addAttributeFont:_font range:_range];
|
||||
}
|
||||
|
||||
// 下划线
|
||||
if (_underLineStyle) {
|
||||
[attributedString addAttributeUnderlineStyle:_underLineStyle modifier:_modifier range:_range];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSAttributedString *)appendTextStorageAttributedString
|
||||
{
|
||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:_text];
|
||||
|
||||
// 验证范围
|
||||
if (NSEqualRanges(_range, NSMakeRange(0, 0))) {
|
||||
_range = NSMakeRange(0, attributedString.length);
|
||||
}
|
||||
|
||||
[self addTextStorageWithAttributedString:attributedString];
|
||||
return [attributedString copy];
|
||||
}
|
||||
|
||||
@end
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// TYTextStorageProtocol.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/8.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
TYDrawAlignmentTop, // 底部齐平 向上伸展
|
||||
TYDrawAlignmentCenter, // 中心齐平
|
||||
TYDrawAlignmentBottom, // 顶部齐平 向下伸展
|
||||
} TYDrawAlignment;
|
||||
|
||||
extern NSString *const kTYTextRunAttributedName;
|
||||
|
||||
@protocol TYTextStorageProtocol <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
* 范围(如果是appendStorage,range只针对追加的文本)
|
||||
*/
|
||||
@property (nonatomic,assign) NSRange range;
|
||||
|
||||
/**
|
||||
* 文本中实际位置,因为某些文本被替换,会导致位置偏移
|
||||
*/
|
||||
@property (nonatomic,assign) NSRange realRange;
|
||||
|
||||
/**
|
||||
* 添加属性到全文attributedString addTextStorage调用
|
||||
*
|
||||
* @param attributedString 属性字符串
|
||||
*/
|
||||
- (void)addTextStorageWithAttributedString:(NSMutableAttributedString *)attributedString;
|
||||
|
||||
@end
|
||||
|
||||
@protocol TYAppendTextStorageProtocol <TYTextStorageProtocol>
|
||||
|
||||
@required
|
||||
|
||||
/**
|
||||
* 追加attributedString属性 appendTextStorage调用
|
||||
*
|
||||
* @return 返回需要追加的attributedString属性
|
||||
*/
|
||||
- (NSAttributedString *)appendTextStorageAttributedString;
|
||||
|
||||
@end
|
||||
|
||||
@protocol TYLinkStorageProtocol <TYAppendTextStorageProtocol>
|
||||
|
||||
@property (nonatomic, strong) UIColor *textColor; // 文本颜色
|
||||
|
||||
@end
|
||||
|
||||
@protocol TYDrawStorageProtocol <TYAppendTextStorageProtocol>
|
||||
|
||||
@property (nonatomic, assign) UIEdgeInsets margin; // 四周间距
|
||||
|
||||
/**
|
||||
* 添加View 或 绘画 到该区域
|
||||
*
|
||||
* @param rect 绘画区域
|
||||
*/
|
||||
- (void)drawStorageWithRect:(CGRect)rect;
|
||||
|
||||
/**
|
||||
* 设置字体高度 当前字符串替换数
|
||||
*/
|
||||
- (void)setTextfontAscent:(CGFloat)ascent descent:(CGFloat)descent;
|
||||
|
||||
// 当前替换字符数
|
||||
- (void)currentReplacedStringNum:(NSInteger)replacedStringNum;
|
||||
|
||||
@end
|
||||
|
||||
@protocol TYViewStorageProtocol <NSObject>
|
||||
|
||||
/**
|
||||
* 设置所属的view
|
||||
*/
|
||||
- (void)setOwnerView:(UIView *)ownerView;
|
||||
|
||||
/**
|
||||
* 不会把你绘画出来
|
||||
*/
|
||||
- (void)didNotDrawRun;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// TYDrawViewStorage.h
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/9.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYDrawStorage.h"
|
||||
|
||||
@interface TYViewStorage : TYDrawStorage<TYViewStorageProtocol>
|
||||
|
||||
@property (nonatomic, strong) UIView *view; // 添加view
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// TYDrawViewStorage.m
|
||||
// TYAttributedLabelDemo
|
||||
//
|
||||
// Created by tanyang on 15/4/9.
|
||||
// Copyright (c) 2015年 tanyang. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TYViewStorage.h"
|
||||
|
||||
@interface TYViewStorage ()
|
||||
@property (nonatomic, weak) UIView *superView;
|
||||
@end
|
||||
|
||||
@implementation TYViewStorage
|
||||
|
||||
#pragma mark - protocol
|
||||
|
||||
- (void)setView:(UIView *)view
|
||||
{
|
||||
_view = view;
|
||||
|
||||
if (CGSizeEqualToSize(self.size, CGSizeZero)) {
|
||||
if ([NSThread isMainThread]) {
|
||||
self.size = view.frame.size;
|
||||
} else {
|
||||
dispatch_semaphore_t signal = dispatch_semaphore_create(0);
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
self.size = view.frame.size;
|
||||
dispatch_semaphore_signal(signal);
|
||||
});
|
||||
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setOwnerView:(UIView *)ownerView
|
||||
{
|
||||
if (_view.superview) {
|
||||
[_view removeFromSuperview];
|
||||
}
|
||||
|
||||
_superView = ownerView;
|
||||
}
|
||||
|
||||
- (void)didNotDrawRun
|
||||
{
|
||||
[_view removeFromSuperview];
|
||||
}
|
||||
|
||||
- (void)drawStorageWithRect:(CGRect)rect
|
||||
{
|
||||
if (_view == nil || _superView == nil) return;
|
||||
// 设置frame 注意 转换rect CoreText context coordinates are the opposite to UIKit so we flip the bounds
|
||||
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(0, _superView.bounds.size.height), 1.f, -1.f);
|
||||
rect = CGRectApplyAffineTransform(rect, transform);
|
||||
[_view setFrame:rect];
|
||||
[_superView addSubview:_view];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self.view performSelectorOnMainThread:@selector(removeFromSuperview) withObject:nil waitUntilDone:[NSThread isMainThread]];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// WXYZ_GCDTimer.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/6/18.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WXYZ_GCDTimerState) {
|
||||
WXYZ_GCDTimerStateStoped,
|
||||
WXYZ_GCDTimerStateRunning,
|
||||
WXYZ_GCDTimerStatePausing
|
||||
};
|
||||
|
||||
@interface WXYZ_GCDTimer : NSObject
|
||||
|
||||
@property (nonatomic, copy) void (^timerStartBlock)(void); // 定时器开始回调
|
||||
|
||||
@property (nonatomic, copy) void (^timerSuspendBlock)(void); // 定时器暂停回调
|
||||
|
||||
@property (nonatomic, copy) void (^timerResumeBlock)(void); // 定时器恢复回调
|
||||
|
||||
@property (nonatomic, copy) void (^timerTerminateBlock)(void); // 定时器终止回调
|
||||
|
||||
@property (nonatomic, copy) void (^timerFinishedBlock)(void); // 定时器完成回调
|
||||
|
||||
@property (nonatomic, copy) void (^timerRunningBlock)(NSUInteger runTimes, CGFloat currentTime); // 定时器运行回调
|
||||
|
||||
@property (nonatomic, assign, readonly) WXYZ_GCDTimerState timerState; // 定时器状态
|
||||
|
||||
/**
|
||||
循环定时器(每秒循环一次)
|
||||
|
||||
@param immediatelyCallBack 开启定时器时是否立即进行运行回调
|
||||
*/
|
||||
- (instancetype)initCycleTimerWithImmediatelyCallBack:(BOOL)immediatelyCallBack;
|
||||
|
||||
/**
|
||||
循环计时器
|
||||
|
||||
@param interval 计时间隔
|
||||
@param immediatelyCallBack 开启定时器时是否立即进行运行回调
|
||||
*/
|
||||
- (instancetype)initCycleTimerWithInterval:(double)interval immediatelyCallBack:(BOOL)immediatelyCallBack;
|
||||
|
||||
/**
|
||||
倒计时计时器(每秒循环一次)
|
||||
|
||||
@param timeDuration 倒计时时长
|
||||
@param immediatelyCallBack 开启定时器时是否立即进行运行回调
|
||||
*/
|
||||
- (instancetype)initCountdownTimerWithTimeDuration:(double)timeDuration immediatelyCallBack:(BOOL)immediatelyCallBack;
|
||||
|
||||
/**
|
||||
倒计时计时器
|
||||
|
||||
@param timeDuration 倒计时时长
|
||||
@param interval 计时间隔
|
||||
@param immediatelyCallBack 开启定时器时是否立即进行运行回调
|
||||
*/
|
||||
- (instancetype)initCountdownTimerWithTimeDuration:(double)timeDuration interval:(double)interval immediatelyCallBack:(BOOL)immediatelyCallBack;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* 定时器开启
|
||||
*/
|
||||
- (void)startTimer;
|
||||
|
||||
/**
|
||||
* 定时器开启
|
||||
*/
|
||||
- (void)startTimerWithTimeDuration:(double)timeDuration;
|
||||
|
||||
/**
|
||||
* 定时器停止
|
||||
*/
|
||||
- (void)stopTimer;
|
||||
|
||||
/**
|
||||
* 定时器恢复
|
||||
*/
|
||||
- (void)resumeTimer;
|
||||
|
||||
/**
|
||||
* 定时器暂停
|
||||
*/
|
||||
- (void)suspendTimer;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,214 @@
|
||||
//
|
||||
// WXYZ_GCDTimer.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2020/6/18.
|
||||
// Copyright © 2020 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_GCDTimer.h"
|
||||
|
||||
@interface WXYZ_GCDTimer ()
|
||||
{
|
||||
dispatch_source_t __block timer;
|
||||
WXYZ_GCDTimerState _timerState;
|
||||
|
||||
double _timeDuration;
|
||||
double _interval;
|
||||
BOOL _immediatelyCallBack;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_GCDTimer
|
||||
|
||||
// 循环定时器(每秒循环一次)
|
||||
- (instancetype)initCycleTimerWithImmediatelyCallBack:(BOOL)immediatelyCallBack
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_timerState = WXYZ_GCDTimerStateStoped;
|
||||
_immediatelyCallBack = immediatelyCallBack;
|
||||
_timeDuration = 0;
|
||||
_interval = 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// 循环计时器
|
||||
- (instancetype)initCycleTimerWithInterval:(double)interval immediatelyCallBack:(BOOL)immediatelyCallBack
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_timerState = WXYZ_GCDTimerStateStoped;
|
||||
_immediatelyCallBack = immediatelyCallBack;
|
||||
_timeDuration = 0;
|
||||
_interval = interval;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// 倒计时计时器(每秒循环一次)
|
||||
- (instancetype)initCountdownTimerWithTimeDuration:(double)timeDuration immediatelyCallBack:(BOOL)immediatelyCallBack
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_timerState = WXYZ_GCDTimerStateStoped;
|
||||
_immediatelyCallBack = immediatelyCallBack;
|
||||
_timeDuration = timeDuration;
|
||||
_interval = 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// 设定时间间隔倒计时计时器
|
||||
- (instancetype)initCountdownTimerWithTimeDuration:(double)timeDuration interval:(double)interval immediatelyCallBack:(BOOL)immediatelyCallBack
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_timerState = WXYZ_GCDTimerStateStoped;
|
||||
_immediatelyCallBack = immediatelyCallBack;
|
||||
_timeDuration = timeDuration;
|
||||
_interval = interval;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器开启
|
||||
*/
|
||||
- (void)startTimer
|
||||
{
|
||||
if (timer && _timerState == WXYZ_GCDTimerStatePausing) {
|
||||
[self resumeTimer];
|
||||
}
|
||||
|
||||
[self stopTimer];
|
||||
[self createzhishudaliTimer];
|
||||
}
|
||||
|
||||
- (void)startTimerWithTimeDuration:(double)timeDuration
|
||||
{
|
||||
_timeDuration = timeDuration;
|
||||
[self startTimer];
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器停止
|
||||
*/
|
||||
- (void)stopTimer
|
||||
{
|
||||
if(timer) {
|
||||
if (_timerState == WXYZ_GCDTimerStateRunning) {
|
||||
dispatch_source_cancel(timer);
|
||||
timer = nil;
|
||||
_timerState = WXYZ_GCDTimerStateStoped;
|
||||
if (self.timerTerminateBlock) {
|
||||
self.timerTerminateBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器恢复
|
||||
*/
|
||||
- (void)resumeTimer
|
||||
{
|
||||
if(timer) {
|
||||
if (_timerState == WXYZ_GCDTimerStatePausing) {
|
||||
dispatch_resume(timer);
|
||||
_timerState = WXYZ_GCDTimerStateRunning;
|
||||
if (self.timerResumeBlock) {
|
||||
self.timerResumeBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时器暂停
|
||||
*/
|
||||
- (void)suspendTimer
|
||||
{
|
||||
if(timer){
|
||||
if (_timerState == WXYZ_GCDTimerStateRunning) {
|
||||
dispatch_suspend(timer);
|
||||
_timerState = WXYZ_GCDTimerStatePausing;
|
||||
if (self.timerSuspendBlock) {
|
||||
self.timerSuspendBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma privite
|
||||
|
||||
/**
|
||||
* 定时器任务完成
|
||||
*/
|
||||
- (void)finishTimer
|
||||
{
|
||||
if(timer) {
|
||||
if (_timerState == WXYZ_GCDTimerStateRunning) {
|
||||
dispatch_source_cancel(timer);
|
||||
timer = nil;
|
||||
_timerState = WXYZ_GCDTimerStateStoped;
|
||||
if (self.timerFinishedBlock) {
|
||||
self.timerFinishedBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createzhishudaliTimer
|
||||
{
|
||||
WS(weakSelf)
|
||||
|
||||
BOOL __block kImmediatelyCallBack = _immediatelyCallBack;
|
||||
double __block kTimeDuration = _timeDuration <= 0 ? HUGE_VAL : _timeDuration;
|
||||
|
||||
// 计时器时间不正确
|
||||
if (kTimeDuration <= 0) {
|
||||
if (self.timerFinishedBlock) {
|
||||
self.timerFinishedBlock();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_interval == 0) {
|
||||
_interval = 1;
|
||||
}
|
||||
|
||||
NSUInteger __block runTimes = 0;
|
||||
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
|
||||
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
|
||||
dispatch_source_set_timer(timer,dispatch_walltime(NULL, 0), _interval * NSEC_PER_SEC, 0);
|
||||
dispatch_source_set_event_handler(timer, ^{
|
||||
runTimes ++;
|
||||
|
||||
_timerState = WXYZ_GCDTimerStateRunning;
|
||||
|
||||
if (kTimeDuration <= 0) {
|
||||
[weakSelf finishTimer];
|
||||
|
||||
} else {
|
||||
kTimeDuration = kTimeDuration - _interval;
|
||||
if (kImmediatelyCallBack) {
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
if (weakSelf.timerRunningBlock) {
|
||||
weakSelf.timerRunningBlock(runTimes, kTimeDuration != INFINITY ?kTimeDuration:0.f);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
kImmediatelyCallBack = YES;
|
||||
}
|
||||
});
|
||||
dispatch_resume(timer);
|
||||
|
||||
if (self.timerStartBlock) {
|
||||
self.timerStartBlock();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// TFCollectionViewFlowLayout.h
|
||||
// TFPhotoBrowser
|
||||
//
|
||||
// Created by zhengwenming on 2018/6/11.
|
||||
// Copyright © 2018年 zhengwenming. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TFCollectionViewFlowLayout : UICollectionViewFlowLayout
|
||||
|
||||
@property (nonatomic ,assign) CGFloat imgaeGap;
|
||||
|
||||
@end
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// TFCollectionViewFlowLayout.m
|
||||
// TFPhotoBrowser
|
||||
//
|
||||
// Created by zhengwenming on 2018/6/11.
|
||||
// Copyright © 2018年 zhengwenming. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFCollectionViewFlowLayout.h"
|
||||
|
||||
@implementation TFCollectionViewFlowLayout
|
||||
|
||||
- (void)prepareLayout
|
||||
{
|
||||
[super prepareLayout];
|
||||
|
||||
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
|
||||
|
||||
CGSize size = self.collectionView.bounds.size;
|
||||
self.itemSize = CGSizeMake(size.width, size.height);
|
||||
self.minimumLineSpacing = 0;
|
||||
self.minimumInteritemSpacing = 10;
|
||||
}
|
||||
|
||||
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
|
||||
{
|
||||
NSArray<UICollectionViewLayoutAttributes *> *layoutAttsArray = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
|
||||
|
||||
CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width/2.0;
|
||||
|
||||
__block CGFloat min = CGFLOAT_MAX;
|
||||
__block NSUInteger minIdx;
|
||||
[layoutAttsArray enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger index, BOOL * _Nonnull stop) {
|
||||
if (ABS(centerX - obj.center.x) < min) {
|
||||
min = ABS(centerX - obj.center.x);
|
||||
minIdx = index;
|
||||
}
|
||||
}];
|
||||
|
||||
[layoutAttsArray enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger index, BOOL * _Nonnull stop) {
|
||||
if (minIdx - 1 == index) {
|
||||
obj.center = CGPointMake(obj.center.x - self.imgaeGap, obj.center.y);
|
||||
}
|
||||
if (minIdx + 1 == index) {
|
||||
obj.center = CGPointMake(obj.center.x + self.imgaeGap, obj.center.y);
|
||||
}
|
||||
}];
|
||||
return layoutAttsArray;
|
||||
}
|
||||
|
||||
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// TFPhotoBrowser.h
|
||||
// TFPhotoBrowser
|
||||
//
|
||||
// Created by zhengwenming on 2018/1/2.
|
||||
// Copyright © 2018年 zhengwenming. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef void(^DeleteBlock)(NSMutableArray *dataSource, NSUInteger currentIndex, UICollectionView *collectionView);
|
||||
typedef void(^DownLoadBlock)(NSMutableArray *dataSource, UIImage *image, NSError *error);
|
||||
|
||||
@interface TFPhotoBrowser : UIViewController
|
||||
/**
|
||||
* 需要预览的照片数组
|
||||
*/
|
||||
@property (nonatomic ,strong) NSMutableArray *dataSource;
|
||||
|
||||
/**
|
||||
* 需要展示的当前的图片index
|
||||
*/
|
||||
@property (nonatomic ,assign) NSInteger currentPhotoIndex;
|
||||
|
||||
/**
|
||||
* 是否需要下载
|
||||
*/
|
||||
@property (nonatomic ,assign) BOOL downLoadNeeded;
|
||||
|
||||
/**
|
||||
* 是否需要删除
|
||||
*/
|
||||
@property (nonatomic ,assign) BOOL deleteNeeded;
|
||||
|
||||
/**
|
||||
* 下载回调
|
||||
*/
|
||||
@property (nonatomic ,copy) DownLoadBlock downLoadBlock;
|
||||
|
||||
/**
|
||||
* 删除回调
|
||||
*/
|
||||
@property (nonatomic ,copy) DeleteBlock deleteBlock;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
//
|
||||
// TFPhotoBrowser.m
|
||||
// TFPhotoBrowser
|
||||
//
|
||||
// Created by zhengwenming on 2018/1/2.
|
||||
// Copyright © 2018年 zhengwenming. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFPhotoBrowser.h"
|
||||
#import "TFPhotoBrowserCell.h"
|
||||
#import "TFCollectionViewFlowLayout.h"
|
||||
|
||||
@interface TFPhotoBrowser ()<UICollectionViewDataSource ,UICollectionViewDelegate ,UIScrollViewDelegate>
|
||||
|
||||
@property(nonatomic ,assign) BOOL isHideNaviBar;
|
||||
@property(nonatomic ,strong) UICollectionView *collectionView;
|
||||
@property(nonatomic ,strong) UIPageControl *pageControl;
|
||||
@end
|
||||
|
||||
@implementation TFPhotoBrowser
|
||||
|
||||
- (BOOL)fullScreenGestureShouldBegin
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 是否支持自动转屏
|
||||
- (BOOL)shouldAutorotate
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
// 支持哪些屏幕方向
|
||||
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
|
||||
{
|
||||
return UIInterfaceOrientationMaskAllButUpsideDown;
|
||||
}
|
||||
|
||||
// 默认的屏幕方向(当前ViewController必须是通过模态出来的UIViewController(模态带导航的无效)方式展现出来的,才会调用这个方法)
|
||||
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
|
||||
{
|
||||
return UIInterfaceOrientationPortrait;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
if (@available(ios 11.0,*)) {
|
||||
// UIScrollView.appearance.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
|
||||
// UITableView.appearance.estimatedRowHeight = 0;
|
||||
// UITableView.appearance.estimatedSectionFooterHeight = 0;
|
||||
// UITableView.appearance.estimatedSectionHeaderHeight = 0;
|
||||
} else {
|
||||
if ([self respondsToSelector:@selector(automaticallyAdjustsScrollViewInsets)]) {
|
||||
self.automaticallyAdjustsScrollViewInsets = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[self.navigationController setNavigationBarHidden:NO animated:YES];
|
||||
}
|
||||
|
||||
- (void)deleteTheImage:(UIBarButtonItem *)sender
|
||||
{
|
||||
if (self.dataSource.count == 1) {
|
||||
[self.dataSource removeObjectAtIndex:self.currentPhotoIndex];
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
|
||||
} else {
|
||||
[self.dataSource removeObjectAtIndex:self.currentPhotoIndex];
|
||||
|
||||
self.title = [NSString stringWithFormat:@"%@/%@", [TFUtilsHelper formatStringWithInteger:self.currentPhotoIndex + 1], [TFUtilsHelper formatStringWithInteger:self.dataSource.count]];
|
||||
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
if (self.deleteBlock) {
|
||||
self.deleteBlock(weakSelf.dataSource, weakSelf.currentPhotoIndex, weakSelf.collectionView);
|
||||
}
|
||||
}
|
||||
|
||||
- (UICollectionView *)collectionView
|
||||
{
|
||||
if (_collectionView == nil) {
|
||||
TFCollectionViewFlowLayout *layout = [[TFCollectionViewFlowLayout alloc] init];
|
||||
layout.imgaeGap = 20;
|
||||
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) collectionViewLayout:layout];
|
||||
_collectionView.backgroundColor = [UIColor blackColor];
|
||||
_collectionView.dataSource = self;
|
||||
_collectionView.delegate = self;
|
||||
_collectionView.pagingEnabled = YES;
|
||||
_collectionView.scrollsToTop = NO;
|
||||
[_collectionView registerClass:[TFPhotoBrowserCell class] forCellWithReuseIdentifier:@"TFPhotoBrowserCell"];
|
||||
_collectionView.showsHorizontalScrollIndicator = NO;
|
||||
_collectionView.contentOffset = CGPointMake(0, 0);
|
||||
_collectionView.contentSize = CGSizeMake(self.view.frame.size.width * self.dataSource.count, self.view.frame.size.height);
|
||||
}
|
||||
return _collectionView;
|
||||
}
|
||||
|
||||
// 视图发生了大小改变的时候会调用此方法 大小改变 == 横竖切换
|
||||
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
|
||||
{
|
||||
if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft || [UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight) {
|
||||
|
||||
self.collectionView.frame = CGRectMake(0, 0, size.width, size.height);
|
||||
self.pageControl.frame = CGRectMake(0, size.height - 30, size.width, 30);
|
||||
self.pageControl.centerX = self.view.centerX;
|
||||
} else {
|
||||
self.collectionView.frame = CGRectMake(0, 0, size.width, size.height);
|
||||
self.pageControl.frame = CGRectMake(0, size.height - 30, size.width, 30);
|
||||
self.pageControl.centerX = self.view.centerX;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
if (self.downLoadNeeded) {
|
||||
UIButton *saveImageBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
saveImageBtn.frame = CGRectMake(0, 0, 40, 40);
|
||||
saveImageBtn.autoresizingMask = UIViewAutoresizingFlexibleHeight;
|
||||
[saveImageBtn setImage:[UIImage imageNamed:@"savePicture"] forState:UIControlStateNormal];
|
||||
[saveImageBtn setImage:[UIImage imageNamed:@"savePicture"] forState:UIControlStateHighlighted];
|
||||
[saveImageBtn addTarget:self action:@selector(saveImage) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:saveImageBtn];
|
||||
} else if(self.deleteNeeded) {
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemTrash target:self action:@selector(deleteTheImage:)];
|
||||
}
|
||||
|
||||
self.title = self.title ? self.title : [NSString stringWithFormat:@"%@/%@", [TFUtilsHelper formatStringWithInteger:self.currentPhotoIndex + 1], [TFUtilsHelper formatStringWithInteger:self.dataSource.count]];
|
||||
self.view.backgroundColor = [UIColor blackColor];
|
||||
self.isHideNaviBar = NO;
|
||||
[self.view addSubview:self.collectionView];
|
||||
|
||||
if (self.dataSource.count) {
|
||||
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:(self.currentPhotoIndex) inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
|
||||
}
|
||||
[self.view addSubview:self.pageControl];
|
||||
self.pageControl.numberOfPages = self.dataSource.count;
|
||||
self.pageControl.currentPage = self.currentPhotoIndex;
|
||||
}
|
||||
|
||||
- (UIPageControl *)pageControl
|
||||
{
|
||||
if (!_pageControl) {
|
||||
_pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height-30, self.view.frame.size.width, 30)];
|
||||
_pageControl.numberOfPages = 5;
|
||||
_pageControl.pageIndicatorTintColor = [UIColor darkGrayColor];
|
||||
_pageControl.currentPageIndicatorTintColor = [UIColor whiteColor];
|
||||
_pageControl.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return _pageControl;
|
||||
}
|
||||
|
||||
- (void)saveImage
|
||||
{
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:self.currentPhotoIndex inSection:0];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
TFPhotoBrowserCell *currentCell = (TFPhotoBrowserCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
|
||||
UIImageWriteToSavedPhotosAlbum(currentCell.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
|
||||
{
|
||||
if (error) {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusError promptTitle:TFLocalizedString(@"保存失败")];
|
||||
} else {
|
||||
[TFPromptManager showPromptViewWithStatus:TFPromptStatusSuccess promptTitle:TFLocalizedString(@"保存成功")];
|
||||
}
|
||||
__weak typeof(self) weakSelf = self;
|
||||
if (self.downLoadBlock) {
|
||||
self.downLoadBlock(weakSelf.dataSource,image,error);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
|
||||
// if (self.currentPhotoIndex==0) {
|
||||
// scrollView.bounces = NO;
|
||||
// }else{
|
||||
// scrollView.bounces = YES;
|
||||
// }
|
||||
}
|
||||
|
||||
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
|
||||
{
|
||||
if ([self.title isEqualToString:TFLocalizedString(@"图片预览")]) {
|
||||
|
||||
} else {
|
||||
CGPoint offSet = scrollView.contentOffset;
|
||||
self.currentPhotoIndex = offSet.x / self.view.width;
|
||||
self.title = [NSString stringWithFormat:@"%@/%@", [TFUtilsHelper formatStringWithInteger:self.currentPhotoIndex + 1], [TFUtilsHelper formatStringWithInteger:self.dataSource.count]];
|
||||
self.pageControl.currentPage = self.currentPhotoIndex;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return self.dataSource.count;
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TFPhotoBrowserCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TFPhotoBrowserCell" forIndexPath:indexPath];
|
||||
cell.model = self.dataSource[indexPath.row];
|
||||
|
||||
WS(weakSelf)
|
||||
if (!cell.singleTapGestureBlock) {
|
||||
cell.singleTapGestureBlock = ^(){
|
||||
if (weakSelf.isHideNaviBar == YES) {
|
||||
[weakSelf.navigationController setNavigationBarHidden:NO animated:YES];
|
||||
} else {
|
||||
[weakSelf.navigationController setNavigationBarHidden:YES animated:YES];
|
||||
}
|
||||
weakSelf.isHideNaviBar = !weakSelf.isHideNaviBar;
|
||||
[weakSelf dismissViewControllerAnimated:YES completion:^{
|
||||
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
if (!cell.longPressGestureBlock) {
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
cell.longPressGestureBlock = ^(TFPhotoBrowserCell *cell) {
|
||||
|
||||
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
if (is_iPad) {
|
||||
UIPopoverPresentationController *popover = actionSheet.popoverPresentationController;
|
||||
|
||||
if (popover) {
|
||||
popover.sourceView = weakSelf.view;
|
||||
popover.sourceRect = weakSelf.view.bounds;
|
||||
|
||||
popover.permittedArrowDirections = UIPopoverArrowDirectionDown;
|
||||
}
|
||||
}
|
||||
[actionSheet addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"保存到相册") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
UIImageWriteToSavedPhotosAlbum(cell.imageView.image, weakSelf,
|
||||
@selector(image:didFinishSavingWithError:contextInfo:), nil);
|
||||
}]];
|
||||
|
||||
[actionSheet addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"取消") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
|
||||
NSLog(@"取消");
|
||||
}]];
|
||||
[weakSelf presentViewController:actionSheet animated:YES completion:nil];
|
||||
};
|
||||
}
|
||||
cell.currentIndexPath = indexPath;
|
||||
|
||||
self.title = [NSString stringWithFormat:@"%@/%@", [TFUtilsHelper formatStringWithInteger:self.currentPhotoIndex + 1], [TFUtilsHelper formatStringWithInteger:self.dataSource.count]];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TFPhotoBrowserCell.h
|
||||
// TFPhotoBrowser
|
||||
//
|
||||
// Created by zhengwenming on 2018/1/2.
|
||||
// Copyright © 2018年 zhengwenming. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TFPhotoBrowserCell : UICollectionViewCell
|
||||
|
||||
@property (nonatomic ,copy) NSIndexPath *currentIndexPath;
|
||||
@property (nonatomic ,strong) UIImageView *imageView;
|
||||
@property (nonatomic ,retain) id model;
|
||||
@property (nonatomic ,copy) void (^singleTapGestureBlock)(void);
|
||||
@property (nonatomic ,copy) void (^longPressGestureBlock)(TFPhotoBrowserCell *cell);
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,192 @@
|
||||
//
|
||||
// TFPhotoBrowserCell.m
|
||||
// TFPhotoBrowser
|
||||
//
|
||||
// Created by zhengwenming on 2018/1/2.
|
||||
// Copyright © 2018年 zhengwenming. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TFPhotoBrowserCell.h"
|
||||
|
||||
@interface TFPhotoBrowserCell ()<UIGestureRecognizerDelegate, UIScrollViewDelegate> {
|
||||
CGFloat _aspectRatio;
|
||||
}
|
||||
@property (nonatomic ,strong) UIScrollView *scrollView;
|
||||
@property (nonatomic ,strong) UIView *imageContainerView;
|
||||
@end
|
||||
|
||||
@implementation TFPhotoBrowserCell
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
|
||||
self.backgroundColor = [UIColor blackColor];
|
||||
self.scrollView = [[UIScrollView alloc] init];
|
||||
self.scrollView.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
|
||||
self.scrollView.bouncesZoom = YES;
|
||||
self.scrollView.backgroundColor = [UIColor blackColor];
|
||||
self.scrollView.maximumZoomScale = 2.5;//放大比例
|
||||
self.scrollView.minimumZoomScale = 1.0;//缩小比例
|
||||
self.scrollView.multipleTouchEnabled = YES;
|
||||
self.scrollView.delegate = self;
|
||||
self.scrollView.scrollsToTop = NO;
|
||||
self.scrollView.showsHorizontalScrollIndicator = NO;
|
||||
self.scrollView.showsVerticalScrollIndicator = NO;
|
||||
self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.scrollView.delaysContentTouches = NO;
|
||||
self.scrollView.canCancelContentTouches = YES;
|
||||
self.scrollView.alwaysBounceVertical = NO;
|
||||
[self.contentView addSubview:self.scrollView];
|
||||
|
||||
self.imageContainerView = [[UIView alloc] init];
|
||||
self.imageContainerView.clipsToBounds = YES;
|
||||
[self.scrollView addSubview:self.imageContainerView];
|
||||
|
||||
self.imageView = [[UIImageView alloc] init];
|
||||
self.imageView.backgroundColor = [UIColor colorWithWhite:1.000 alpha:0.500];
|
||||
self.imageView.clipsToBounds = YES;
|
||||
self.imageView.userInteractionEnabled = YES;
|
||||
[self.imageContainerView addSubview:self.imageView];
|
||||
|
||||
// 单击
|
||||
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
|
||||
[self.contentView addGestureRecognizer:singleTap];
|
||||
|
||||
// 双击
|
||||
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)];
|
||||
doubleTap.numberOfTapsRequired = 2;
|
||||
[singleTap requireGestureRecognizerToFail:doubleTap];
|
||||
[self.contentView addGestureRecognizer:doubleTap];
|
||||
|
||||
// 长按
|
||||
UILongPressGestureRecognizer *longPressGes = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressToSavePhoto:)];
|
||||
[self.contentView addGestureRecognizer:longPressGes];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)longPressToSavePhoto:(UILongPressGestureRecognizer *)sender
|
||||
{
|
||||
if (sender.state == UIGestureRecognizerStateBegan) {
|
||||
if (self.longPressGestureBlock) {
|
||||
self.longPressGestureBlock(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)resizeSubviews
|
||||
{
|
||||
self.imageContainerView.origin = CGPointZero;
|
||||
self.imageContainerView.width = self.width;
|
||||
|
||||
UIImage *image = self.imageView.image;
|
||||
|
||||
if (image.size.height/image.size.width > self.height/self.width) {
|
||||
self.imageContainerView.height = floor(image.size.height / (image.size.width / self.width));
|
||||
} else {
|
||||
CGFloat height = image.size.height / image.size.width * self.width;
|
||||
if (height < 1 || isnan(height)) height = self.height;
|
||||
height = floor(height);
|
||||
|
||||
self.imageContainerView.height = height;
|
||||
self.imageContainerView.centerY = self.height / 2;
|
||||
}
|
||||
|
||||
if (self.imageContainerView.height > self.height && self.imageContainerView.height - self.height <= 1) {
|
||||
self.imageContainerView.height = self.height;
|
||||
}
|
||||
|
||||
self.scrollView.contentSize = CGSizeMake(self.width, MAX(self.imageContainerView.height, self.height));
|
||||
[self.scrollView scrollRectToVisible:self.bounds animated:NO];
|
||||
self.scrollView.alwaysBounceVertical = self.imageContainerView.height <= self.height ? NO : YES;
|
||||
self.imageView.frame = self.imageContainerView.bounds;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
[self resizeSubviews];
|
||||
}
|
||||
|
||||
- (void)doubleTap:(UITapGestureRecognizer *)tap
|
||||
{
|
||||
if (self.scrollView.zoomScale > 1.0) {
|
||||
[self.scrollView setZoomScale:1.0 animated:YES];
|
||||
} else {
|
||||
CGPoint touchPoint = [tap locationInView:self.imageView];
|
||||
CGFloat newZoomScale = self.scrollView.maximumZoomScale;
|
||||
CGFloat xsize = self.frame.size.width / newZoomScale;
|
||||
CGFloat ysize = self.frame.size.height / newZoomScale;
|
||||
|
||||
[self.scrollView zoomToRect:CGRectMake(touchPoint.x - xsize/2, touchPoint.y - ysize/2, xsize, ysize) animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setModel:(id)model
|
||||
{
|
||||
_model = model;
|
||||
|
||||
[self.scrollView setZoomScale:1.0 animated:NO];
|
||||
|
||||
if ([model isKindOfClass:[UIImage class]]) {
|
||||
UIImage *aImage = (UIImage *)model;
|
||||
self.imageView.image = aImage;
|
||||
|
||||
} else if ([model isKindOfClass:[NSString class]]) {
|
||||
NSString *aString = (NSString *)model;
|
||||
if ([aString rangeOfString:@"http"].location != NSNotFound) {
|
||||
WS(weakSelf)
|
||||
[self.imageView setImageWithURL:[NSURL URLWithString:aString] placeholder:HoldImage options:YYWebImageOptionSetImageWithFadeAnimation completion:^(UIImage * _Nullable image, NSURL * _Nonnull url, YYWebImageFromType from, YYWebImageStage stage, NSError * _Nullable error) {
|
||||
[weakSelf resizeSubviews];
|
||||
}];
|
||||
} else {
|
||||
self.imageView.image = [UIImage imageNamed:aString];
|
||||
}
|
||||
} else if ([model isKindOfClass:[NSURL class]]) {
|
||||
NSURL *aURL = (NSURL *)model;
|
||||
WS(weakSelf)
|
||||
[self.imageView setImageWithURL:aURL placeholder:HoldImage options:YYWebImageOptionSetImageWithFadeAnimation completion:^(UIImage * _Nullable image, NSURL * _Nonnull url, YYWebImageFromType from, YYWebImageStage stage, NSError * _Nullable error) {
|
||||
[weakSelf resizeSubviews];
|
||||
}];
|
||||
}
|
||||
|
||||
[self resizeSubviews];
|
||||
}
|
||||
|
||||
- (void)singleTap:(UITapGestureRecognizer *)tap
|
||||
{
|
||||
if (self.singleTapGestureBlock) {
|
||||
self.singleTapGestureBlock();
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
|
||||
{
|
||||
return self.imageContainerView;
|
||||
}
|
||||
|
||||
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
|
||||
{
|
||||
CGFloat offsetX = (scrollView.width > scrollView.contentSize.width) ? (scrollView.width - scrollView.contentSize.width) * 0.5 : 0.0;
|
||||
CGFloat offsetY = (scrollView.height > scrollView.contentSize.height) ? (scrollView.height - scrollView.contentSize.height) * 0.5 : 0.0;
|
||||
self.imageContainerView.center = CGPointMake(scrollView.contentSize.width * 0.5 + offsetX, scrollView.contentSize.height * 0.5 + offsetY);
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
|
||||
{
|
||||
if (self.scrollView.contentOffset.x <= 0) {
|
||||
if ([otherGestureRecognizer.delegate isKindOfClass:NSClassFromString(@"_FDFullscreenPopGestureRecognizerDelegate")]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// DPImagePicker.h
|
||||
//
|
||||
// Created by Andrew on 2017/9/11.
|
||||
// Copyright © 2017年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
DPCameraDeviceFront, //前置摄像头
|
||||
DPCameraDeviceRear, //后置摄像头
|
||||
} DPCameraDevice;
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
DPCameraFlashModeOff, //关闭闪光灯
|
||||
DPCameraFlashModeAuto, //自动闪光灯
|
||||
DPCameraFlashModeOn, //开启闪光灯
|
||||
} DPCameraFlashMode;
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
DPChooseImageTypeCamera,
|
||||
DPChooseImageTypeLibrary,
|
||||
DPChooseImageTypeUnknow
|
||||
} DPChooseImageType;
|
||||
|
||||
typedef void(^ChooseImageStyleBlock)(DPChooseImageType type);
|
||||
|
||||
@protocol DPImagePickerDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
图片选择完毕
|
||||
|
||||
@param originalImage 未编辑原图
|
||||
@param editedImage 编辑后图片
|
||||
*/
|
||||
- (void)imagePickerDidFinishPickingWithOriginalImage:(UIImage *)originalImage editedImage:(UIImage *)editedImage;
|
||||
|
||||
- (void)imagePickerDidCancel;
|
||||
|
||||
@end
|
||||
|
||||
@interface WXYZ_ImagePicker : UIView <UIActionSheetDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>
|
||||
|
||||
@property (nonatomic, weak) id <DPImagePickerDelegate> delegate;
|
||||
|
||||
@property (nonatomic, copy) ChooseImageStyleBlock chooseImageStyleBlock;
|
||||
/**
|
||||
是否可以编辑图片
|
||||
default is NO
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL editPhoto;
|
||||
|
||||
/**
|
||||
前置/后置摄像头
|
||||
default is Rear
|
||||
*/
|
||||
@property (nonatomic, assign) DPCameraDevice cameraDevice;
|
||||
|
||||
/**
|
||||
闪光灯类型
|
||||
default is Auto
|
||||
*/
|
||||
@property (nonatomic, assign) DPCameraFlashMode cameraFlashMode;
|
||||
|
||||
interface_singleton
|
||||
|
||||
/**
|
||||
显示图片选择器
|
||||
|
||||
@param showController 承载的Controller
|
||||
*/
|
||||
- (void)showInController:(UIViewController *)showController;
|
||||
|
||||
/**
|
||||
只显示相机
|
||||
|
||||
@param showController 承载Controller
|
||||
*/
|
||||
- (void)showCameraInController:(UIViewController *)showController;
|
||||
|
||||
/**
|
||||
只显示相册
|
||||
|
||||
@param showController 承载Controller
|
||||
*/
|
||||
- (void)showLibraryInController:(UIViewController *)showController;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// DPImagePicker.m
|
||||
//
|
||||
// Created by Andrew on 2017/9/11.
|
||||
// Copyright © 2017年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ImagePicker.h"
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <Photos/Photos.h>
|
||||
#import <Photos/Photos.h>
|
||||
|
||||
@class UIActionSheetDelegateImpl;
|
||||
static UIActionSheetDelegateImpl * delegateImpl;
|
||||
|
||||
@implementation WXYZ_ImagePicker
|
||||
{
|
||||
UIViewController *_showController;
|
||||
}
|
||||
|
||||
implementation_singleton(WXYZ_ImagePicker)
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_cameraFlashMode = DPCameraFlashModeAuto;
|
||||
_cameraDevice = DPCameraDeviceFront;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)showInController:(UIViewController *)showController
|
||||
{
|
||||
_showController = showController;
|
||||
|
||||
WS(weakSelf)
|
||||
UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
|
||||
if (is_iPad) {
|
||||
UIPopoverPresentationController *popover = actionSheet.popoverPresentationController;
|
||||
|
||||
if (popover) {
|
||||
popover.sourceView = self;
|
||||
popover.sourceRect = self.bounds;
|
||||
|
||||
popover.permittedArrowDirections = UIPopoverArrowDirectionDown;
|
||||
}
|
||||
}
|
||||
|
||||
[actionSheet addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"拍照") style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
|
||||
if (weakSelf.chooseImageStyleBlock) {
|
||||
weakSelf.chooseImageStyleBlock(DPChooseImageTypeCamera);
|
||||
}
|
||||
//相机
|
||||
[weakSelf turnOnCamera];
|
||||
}]];
|
||||
|
||||
[actionSheet addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"从相册中选择") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
if (weakSelf.chooseImageStyleBlock) {
|
||||
weakSelf.chooseImageStyleBlock(DPChooseImageTypeLibrary);
|
||||
}
|
||||
//相册
|
||||
[weakSelf turnOnLibrary];
|
||||
}]];
|
||||
|
||||
[actionSheet addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"取消") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||
|
||||
}]];
|
||||
|
||||
[[TFViewHelper getWindowRootController] presentViewController:actionSheet animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)showCameraInController:(UIViewController *)showController
|
||||
{
|
||||
_showController = showController;
|
||||
[self turnOnCamera];
|
||||
}
|
||||
|
||||
- (void)showLibraryInController:(UIViewController *)showController
|
||||
{
|
||||
_showController = showController;
|
||||
[self turnOnLibrary];
|
||||
}
|
||||
|
||||
- (void)turnOnCamera
|
||||
{
|
||||
//当前设备没有摄像头
|
||||
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
|
||||
UIAlertController *alertView = [UIAlertController alertControllerWithTitle:TFLocalizedString(@"提示") message:TFLocalizedString(@"当前设备没有摄像头") preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertView addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"确定") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||
|
||||
}]];
|
||||
[[TFViewHelper getWindowRootController] presentViewController:alertView animated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
//无法获取相机权限
|
||||
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == kCLAuthorizationStatusDenied){
|
||||
UIAlertController *alertView = [UIAlertController alertControllerWithTitle:TFLocalizedString(@"提示") message:TFLocalizedString(@"请前往设置->隐私->相机授权应用拍照权限") preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertView addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"确定") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||
|
||||
}]];
|
||||
[[TFViewHelper getWindowRootController] presentViewController:alertView animated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
|
||||
imagePickerController.delegate = self;
|
||||
imagePickerController.allowsEditing = _editPhoto;
|
||||
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
|
||||
imagePickerController.cameraDevice = _cameraDevice == DPCameraDeviceFront?UIImagePickerControllerCameraDeviceFront:UIImagePickerControllerCameraDeviceRear;
|
||||
switch (_cameraFlashMode) {
|
||||
case DPCameraFlashModeAuto:
|
||||
imagePickerController.cameraFlashMode = UIImagePickerControllerCameraFlashModeAuto;
|
||||
break;
|
||||
case DPCameraFlashModeOn:
|
||||
imagePickerController.cameraFlashMode = UIImagePickerControllerCameraFlashModeOn;
|
||||
break;
|
||||
case DPCameraFlashModeOff:
|
||||
imagePickerController.cameraFlashMode = UIImagePickerControllerCameraFlashModeOff;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
imagePickerController.mediaTypes = @[(NSString *)kUTTypeImage];
|
||||
imagePickerController.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
[_showController presentViewController:imagePickerController animated:YES completion:nil];
|
||||
|
||||
}
|
||||
|
||||
- (void)turnOnLibrary
|
||||
{
|
||||
//当前设备没有相册
|
||||
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
|
||||
UIAlertController *alertView = [UIAlertController alertControllerWithTitle:TFLocalizedString(@"提示") message:TFLocalizedString(@"当前设备没有相册") preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertView addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"确定") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||
|
||||
}]];
|
||||
[[TFViewHelper getWindowRootController] presentViewController:alertView animated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
|
||||
switch (status) {
|
||||
case PHAuthorizationStatusAuthorized:
|
||||
break;
|
||||
case PHAuthorizationStatusDenied:
|
||||
break;
|
||||
case PHAuthorizationStatusNotDetermined:
|
||||
break;
|
||||
case PHAuthorizationStatusRestricted:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
//无法访问相册
|
||||
if ([PHPhotoLibrary authorizationStatus] == PHAuthorizationStatusDenied) {
|
||||
UIAlertController *alertView = [UIAlertController alertControllerWithTitle:TFLocalizedString(@"提示") message:TFLocalizedString(@"请前往设置->隐私->相册授权应用访问相册权限") preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertView addAction:[UIAlertAction actionWithTitle:TFLocalizedString(@"确定") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
|
||||
|
||||
}]];
|
||||
[[TFViewHelper getWindowRootController] presentViewController:alertView animated:YES completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
|
||||
imagePickerController.delegate = self;
|
||||
imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
|
||||
imagePickerController.allowsEditing = _editPhoto;
|
||||
imagePickerController.mediaTypes = @[(NSString *)kUTTypeImage];
|
||||
imagePickerController.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
[_showController presentViewController:imagePickerController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - UIImagePickerControllerDelegate
|
||||
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
|
||||
{
|
||||
picker.delegate = nil;
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
|
||||
UIImage *originalImage = info[UIImagePickerControllerOriginalImage];
|
||||
UIImage *editedImage = info[UIImagePickerControllerEditedImage];
|
||||
if ([self.delegate respondsToSelector:@selector(imagePickerDidFinishPickingWithOriginalImage:editedImage:)]) {
|
||||
[self.delegate imagePickerDidFinishPickingWithOriginalImage:originalImage editedImage:editedImage];
|
||||
}
|
||||
|
||||
//如果是拍摄的照片,将自动保存到相册
|
||||
if ([[info objectForKey:UIImagePickerControllerMediaType] isEqualToString:(NSString *)kUTTypeImage] && picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
||||
UIImageWriteToSavedPhotosAlbum(originalImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
|
||||
}
|
||||
}
|
||||
|
||||
//点击Cancel按钮后执行方法
|
||||
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
|
||||
{
|
||||
[_showController dismissViewControllerAnimated:YES completion:nil];
|
||||
if ([self.delegate respondsToSelector:@selector(imagePickerDidCancel)]) {
|
||||
[self.delegate imagePickerDidCancel];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
|
||||
if ([UIDevice currentDevice].systemVersion.floatValue < 11) {
|
||||
return;
|
||||
}
|
||||
if ([viewController isKindOfClass:NSClassFromString(@"PUPhotoPickerHostViewController")]) {
|
||||
[viewController.view.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if (obj.bounds.size.width < SCREEN_WIDTH / 9 && !obj.isHidden) {
|
||||
obj.hidden = YES;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
//保存照片成功后的回调
|
||||
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// WXReaderAnimationLayer.h
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2018/6/8.
|
||||
// Copyright © 2018年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, WXReaderAnimationState) {
|
||||
WXReaderAnimationStateStoped = 0,
|
||||
WXReaderAnimationStateRunning = 1,
|
||||
WXReaderAnimationStatePausing = 2
|
||||
};
|
||||
|
||||
typedef void(^ReaderAutoReadBlock)(void);
|
||||
|
||||
@interface WXYZ_ReaderAnimationLayer : NSObject
|
||||
|
||||
// 触发自动翻页
|
||||
@property (nonatomic, copy) ReaderAutoReadBlock readerAutoReadBlock;
|
||||
|
||||
- (instancetype)initWithView:(UIView *)keyView;
|
||||
|
||||
// 开始动画
|
||||
- (void)startReadingAnimation;
|
||||
|
||||
//暂停动画
|
||||
- (void)pauseAnimation;
|
||||
|
||||
//继续动画
|
||||
- (void)resumeAnimation;
|
||||
|
||||
// 停止动画
|
||||
- (void)stopAnimation;
|
||||
|
||||
// 重启动画
|
||||
- (void)resetAnimation;
|
||||
|
||||
// 重置时间
|
||||
- (void)resetDuration:(CFTimeInterval)animationDuration;
|
||||
|
||||
@end
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// WXReaderAnimationLayer.m
|
||||
// WXReader
|
||||
//
|
||||
// Created by Andrew on 2018/6/8.
|
||||
// Copyright © 2018年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WXYZ_ReaderAnimationLayer.h"
|
||||
#import "TFReaderSettingHelper.h"
|
||||
|
||||
#define animationKey @"wxreader_animation_layer_key"
|
||||
|
||||
@interface WXYZ_ReaderAnimationLayer () <CAAnimationDelegate>
|
||||
{
|
||||
CFTimeInterval _animationDuration;
|
||||
UIView *_layerView;
|
||||
}
|
||||
|
||||
@property (nonatomic, assign) WXReaderAnimationState state;
|
||||
|
||||
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
|
||||
|
||||
@property (nonatomic, strong) CABasicAnimation *pathAnimation;
|
||||
|
||||
@end
|
||||
|
||||
@implementation WXYZ_ReaderAnimationLayer
|
||||
|
||||
- (instancetype)initWithView:(UIView *)keyView
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_layerView = keyView;
|
||||
_animationDuration = [[TFReaderSettingHelper sharedManager] getReadSpeed];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// 开始动画
|
||||
- (void)startReadingAnimation
|
||||
{
|
||||
self.state = WXReaderAnimationStateRunning;
|
||||
[self.shapeLayer addAnimation:self.pathAnimation forKey:animationKey];
|
||||
[_layerView.layer addSublayer:self.shapeLayer];
|
||||
}
|
||||
|
||||
//暂停动画
|
||||
- (void)pauseAnimation
|
||||
{
|
||||
self.state = WXReaderAnimationStatePausing;
|
||||
CFTimeInterval pausedTime = [self.shapeLayer convertTime:CACurrentMediaTime() fromLayer:nil];
|
||||
self.shapeLayer.speed = 0.0;
|
||||
self.shapeLayer.timeOffset = pausedTime;
|
||||
}
|
||||
|
||||
//继续动画
|
||||
- (void)resumeAnimation
|
||||
{
|
||||
if (self.state == WXReaderAnimationStateStoped) {
|
||||
[self startReadingAnimation];
|
||||
}
|
||||
self.state = WXReaderAnimationStateRunning;
|
||||
CFTimeInterval pausedTime = [self.shapeLayer timeOffset];
|
||||
self.shapeLayer.speed = 1.0;
|
||||
self.shapeLayer.timeOffset = 0.0;
|
||||
self.shapeLayer.beginTime = 0.0;
|
||||
CFTimeInterval timeSincePause = [self.shapeLayer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
|
||||
self.shapeLayer.beginTime = timeSincePause;
|
||||
}
|
||||
|
||||
// 停止动画
|
||||
- (void)stopAnimation
|
||||
{
|
||||
self.state = WXReaderAnimationStateStoped;
|
||||
self.pathAnimation = nil;
|
||||
[self.shapeLayer removeAnimationForKey:animationKey];
|
||||
[self.shapeLayer removeFromSuperlayer];
|
||||
self.shapeLayer = nil;
|
||||
}
|
||||
|
||||
// 重启动画
|
||||
- (void)resetAnimation
|
||||
{
|
||||
if (self.state == WXReaderAnimationStateRunning) {
|
||||
[self resetDuration:_animationDuration];
|
||||
}
|
||||
}
|
||||
|
||||
// 重置时间
|
||||
- (void)resetDuration:(CFTimeInterval)animationDuration
|
||||
{
|
||||
_animationDuration = animationDuration;
|
||||
|
||||
if (self.state == WXReaderAnimationStatePausing || self.state == WXReaderAnimationStateStoped) {
|
||||
[self stopAnimation];
|
||||
return;
|
||||
}
|
||||
[self startReadingAnimation];
|
||||
}
|
||||
|
||||
// 动画代理回调
|
||||
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
|
||||
{
|
||||
if (flag == YES) {
|
||||
[self.shapeLayer addAnimation:self.pathAnimation forKey:animationKey];
|
||||
if (self.state == WXReaderAnimationStateRunning && self.readerAutoReadBlock) {
|
||||
self.readerAutoReadBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (UIBezierPath *)layerPath
|
||||
{
|
||||
UIBezierPath *path = [UIBezierPath bezierPath];
|
||||
if (is_iPhoneX) {
|
||||
CGFloat lineWidth = 2;
|
||||
|
||||
// 屏幕左上角圆角半径
|
||||
CGFloat radius1 = 44;
|
||||
// 刘海顶部圆角半径
|
||||
CGFloat radius2 = 6;
|
||||
// 刘海底部圆角半径
|
||||
CGFloat radius3 = 20;
|
||||
// 刘海高度
|
||||
CGFloat hairHeight = 30;
|
||||
// 刘海顶部宽度
|
||||
CGFloat hairTopWidth = 209;
|
||||
// 刘海左侧空隙
|
||||
CGFloat hairLeftWidth = (SCREEN_WIDTH - hairTopWidth) / 2;
|
||||
// 刘海右侧空隙
|
||||
CGFloat hairRightWidth = hairLeftWidth;
|
||||
|
||||
[path moveToPoint:CGPointMake(lineWidth, radius1)];
|
||||
[path addQuadCurveToPoint:CGPointMake(radius1, lineWidth) controlPoint:CGPointMake(lineWidth, lineWidth)];
|
||||
|
||||
[path addLineToPoint:CGPointMake(hairLeftWidth - radius2, lineWidth)];
|
||||
[path addQuadCurveToPoint:CGPointMake(hairLeftWidth - lineWidth, radius2) controlPoint:CGPointMake(hairLeftWidth - lineWidth, lineWidth)];
|
||||
|
||||
[path addLineToPoint:CGPointMake(hairLeftWidth - lineWidth, hairHeight - radius3)];
|
||||
[path addQuadCurveToPoint:CGPointMake(hairLeftWidth + radius3, hairHeight + lineWidth) controlPoint:CGPointMake(hairLeftWidth, hairHeight)];
|
||||
|
||||
[path addLineToPoint:CGPointMake(SCREEN_WIDTH - hairRightWidth - radius3, hairHeight + lineWidth)];
|
||||
[path addQuadCurveToPoint:CGPointMake(SCREEN_WIDTH - hairRightWidth + lineWidth, hairHeight - radius3) controlPoint:CGPointMake(SCREEN_WIDTH - hairRightWidth, hairHeight)];
|
||||
|
||||
[path addLineToPoint:CGPointMake(SCREEN_WIDTH - hairRightWidth + lineWidth, radius2)];
|
||||
[path addQuadCurveToPoint:CGPointMake(SCREEN_WIDTH - hairRightWidth + radius2, lineWidth) controlPoint:CGPointMake(SCREEN_WIDTH - hairRightWidth + lineWidth, lineWidth)];
|
||||
|
||||
[path addLineToPoint:CGPointMake(SCREEN_WIDTH - radius1, lineWidth)];
|
||||
[path addQuadCurveToPoint:CGPointMake(SCREEN_WIDTH - lineWidth, radius1) controlPoint:CGPointMake(SCREEN_WIDTH - lineWidth, lineWidth)];
|
||||
} else {
|
||||
[path moveToPoint:CGPointMake(0, 2)];
|
||||
[path addLineToPoint:CGPointMake(SCREEN_WIDTH,2)];
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
- (CAShapeLayer *)shapeLayer
|
||||
{
|
||||
if (!_shapeLayer) {
|
||||
_shapeLayer = [CAShapeLayer layer];
|
||||
_shapeLayer.lineWidth = 4;
|
||||
_shapeLayer.fillColor = [[UIColor clearColor] CGColor];
|
||||
_shapeLayer.strokeColor = kColorRGBA(255, 102, 0, 1).CGColor;
|
||||
_shapeLayer.path = [self layerPath].CGPath;
|
||||
}
|
||||
return _shapeLayer;
|
||||
}
|
||||
|
||||
- (CABasicAnimation *)pathAnimation
|
||||
{
|
||||
if (!_pathAnimation) {
|
||||
_pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
|
||||
_pathAnimation.duration = _animationDuration;
|
||||
_pathAnimation.repeatCount = 1;
|
||||
_pathAnimation.delegate = self;
|
||||
_pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
|
||||
_pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
|
||||
}
|
||||
return _pathAnimation;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// KeyChainStore.h
|
||||
//
|
||||
// Created by Andrew on 2017/8/5.
|
||||
// Copyright © 2017年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface KeyChainStore : NSObject
|
||||
+ (void)save:(NSString *)service data:(id)data;
|
||||
+ (id)load:(NSString *)service;
|
||||
+ (void)deleteKeyData:(NSString *)service;
|
||||
@end
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// KeyChainStore.m
|
||||
//
|
||||
// Created by Andrew on 2017/8/5.
|
||||
// Copyright © 2017年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "KeyChainStore.h"
|
||||
|
||||
@implementation KeyChainStore
|
||||
+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
|
||||
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||
(id)kSecClassGenericPassword,(id)kSecClass,
|
||||
service, (id)kSecAttrService,
|
||||
service, (id)kSecAttrAccount,
|
||||
(id)kSecAttrAccessibleAfterFirstUnlock,(id)kSecAttrAccessible,
|
||||
nil];
|
||||
}
|
||||
|
||||
+ (void)save:(NSString *)service data:(id)data {
|
||||
//Get search dictionary
|
||||
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
|
||||
//Delete old item before add new item
|
||||
SecItemDelete((CFDictionaryRef)keychainQuery);
|
||||
//Add new object to search dictionary(Attention:the data format)
|
||||
[keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(id)kSecValueData];
|
||||
//Add item to keychain with the search dictionary
|
||||
SecItemAdd((CFDictionaryRef)keychainQuery, NULL);
|
||||
}
|
||||
|
||||
+ (id)load:(NSString *)service {
|
||||
id ret = nil;
|
||||
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
|
||||
//Configure the search setting
|
||||
//Since in our simple case we are expecting only a single attribute to be returned (the password) we can set the attribute kSecReturnData to kCFBooleanTrue
|
||||
[keychainQuery setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
|
||||
[keychainQuery setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
|
||||
CFDataRef keyData = NULL;
|
||||
if (SecItemCopyMatching((CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
|
||||
@try {
|
||||
ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge NSData *)keyData];
|
||||
} @catch (NSException *e) {
|
||||
// TILog(@"Unarchive of %@ failed: %@", service, e);
|
||||
} @finally {
|
||||
}
|
||||
}
|
||||
if (keyData)
|
||||
CFRelease(keyData);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+ (void)deleteKeyData:(NSString *)service {
|
||||
NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
|
||||
SecItemDelete((CFDictionaryRef)keychainQuery);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// UUID.h
|
||||
//
|
||||
// Created by Andrew on 2017/8/5.
|
||||
// Copyright © 2017年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface UUID : NSObject
|
||||
|
||||
+ (NSString *)getUUID;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// UUID.m
|
||||
//
|
||||
// Created by Andrew on 2017/8/5.
|
||||
// Copyright © 2017年 Andrew. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UUID.h"
|
||||
#import "KeyChainStore.h"
|
||||
|
||||
#define UUID_KEY(bunid) [NSString stringWithFormat:@"cn.property.uuidkey%@",bunid]
|
||||
#define Pasteboard(bunid) [NSString stringWithFormat:@"cn.property.PublicPasteBord%@",bunid]
|
||||
|
||||
@implementation UUID
|
||||
+ (NSString *)getUUID
|
||||
{
|
||||
NSString *bunid = [[NSBundle mainBundle]bundleIdentifier];
|
||||
NSString *strUUID;
|
||||
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
|
||||
strUUID = [userDefault objectForKey:UUID_KEY(bunid)];
|
||||
if (!strUUID) {
|
||||
UIPasteboard *pp = [UIPasteboard generalPasteboard];
|
||||
NSData *data = [pp valueForPasteboardType:Pasteboard(bunid)];
|
||||
strUUID = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
if (!strUUID || [strUUID isEqualToString:@""]) {
|
||||
strUUID = (NSString *)[KeyChainStore load:UUID_KEY(bunid)];
|
||||
if (strUUID) {
|
||||
[userDefault setObject:strUUID forKey:UUID_KEY(bunid)];
|
||||
[userDefault synchronize];
|
||||
}
|
||||
} else {
|
||||
[userDefault setObject:strUUID forKey:UUID_KEY(bunid)];
|
||||
[userDefault synchronize];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//首次执行该方法时,uuid为空
|
||||
if ([strUUID isEqualToString:@""] || !strUUID) {
|
||||
|
||||
//生成一个uuid的方法
|
||||
CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
|
||||
strUUID = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault,uuidRef));
|
||||
|
||||
//将该uuid保存到keychain
|
||||
[KeyChainStore save:UUID_KEY(bunid) data:strUUID];
|
||||
[userDefault setObject:strUUID forKey:UUID_KEY(bunid)];
|
||||
[userDefault synchronize];
|
||||
UIPasteboard *p = [UIPasteboard generalPasteboard];
|
||||
[p setValue:strUUID forPasteboardType:Pasteboard(bunid)];
|
||||
|
||||
CFRelease(uuidRef);
|
||||
|
||||
}
|
||||
return strUUID;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user