注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

环信FAQ

环信FAQ

集成常见问题及答案
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

这是一个围绕SQLite的Objective-C封装

FMDB这是一个围绕SQLite的Objective-C的封装安装cocoapodsFMDB可以使用CocoaPods安装。如果尚未执行此操作,则可能需要初始化项目,以使其Podfile为您生成模板:$ pod init然后,编辑Podfile,并添加FMDB...
继续阅读 »

FMDB

这是一个围绕SQLite的Objective-C的封装

安装

cocoapods

FMDB可以使用CocoaPods安装

如果尚未执行此操作,则可能需要初始化项目,以使其Podfile为您生成模板:

$ pod init

然后,编辑Podfile,并添加FMDB


# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'MyApp' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!

# Pods for MyApp2

pod 'FMDB'
# pod 'FMDB/FTS' # FMDB with FTS
# pod 'FMDB/standalone' # FMDB with latest SQLite amalgamation source
# pod 'FMDB/standalone/FTS' # FMDB with latest SQLite amalgamation source and FTS
# pod 'FMDB/SQLCipher' # FMDB with SQLCipher
end

$ pod install

然后打开.xcworkspace而不是.xcodeproj


Carthage 安装

$ echo ' github "ccgus/fmdb" ' > ./Cartfile
$ carthage update

您可以在Cocoa项目中使用任何一种样式。FMDB会在编译时确定您正在使用哪个,并做正确的事。

自定义功能

过去,编写自定义函数时,通常必须包含自己的@autoreleasepool块,以避免在编写通过大表扫描的函数时出现问题。现在,FMDB将自动将其包装在自动释放池中,因此您不必这样做。

另外,过去,在检索传递给函数的值时,您必须下拉至SQLite C API并包含您自己的sqlite3_value_XXX调用。现在有FMDatabase方法valueIntvalueString等等,这样你就可以留内斯威夫特和/或Objective-C中,而无需自行调用C函数。同样,指定的返回值时,你不再需要调用sqlite3_result_XXXC API,而是你可以使用FMDatabase方法resultIntresultString等有一个新enumvalueTypeSqliteValueType,可用于检查传递给自定义函数参数的类型。

queue.inTransaction { db, rollback in
do {
guard let db == db else {
// handle error here
return
}

try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [1])
try db.executeUpdate("INSERT INTO foo (bar) VALUES (?)", values: [2])
} catch {
rollback?.pointee = true
}
}


然后,您可以在SQL中使用该函数(在这种情况下,匹配“ Jose”和“José”):

SELECT * FROM employees WHERE RemoveDiacritics(first_name) LIKE 'jose'

API变更

除了makeFunctionNamed上面提到的以外,还有一些其他的API更改。具体来说,

  • 为了与API的其余部分保持一致,方法objectForColumnNameUTF8StringForColumnName已重命名为objectForColumnUTF8StringForColumn

  • 注意,如果将无效的列名/索引传递给它,则objectForColumn(和相关的下标运算符)现在返回nil它曾经返回NSNull

  • 为了避免与混乱FMDatabaseQueue的方法inTransaction,其中执行交易,该FMDatabase方法以确定是否是在交易与否,inTransaction已被替换为只读属性,isInTransaction

  • 几种功能都被转换为性能,即,databasePathmaxBusyRetryTimeIntervalshouldCacheStatementssqliteHandlehasOpenResultSetslastInsertRowIdchangesgoodConnectioncolumnCountresultDictionaryapplicationIDapplicationIDStringuserVersioncountOfCheckedInDatabasescountOfCheckedOutDatabases,和countOfOpenDatabases对于Objective-C用户而言,这几乎没有实质性影响,但是对于Swift用户而言,它带来了更为自然的界面。注意:对于Objective-C开发人员,以前版本的FMDB公开了许多ivars(但是我们希望您无论如何都不要直接使用它们!),但是这些实现的详细信息不再公开。

URL方法

为了适应Apple从路径到URL的转变,现在存在NSURL各种init方法的再现形式,以前只接受路径。


用法

FMDB中有三个主要类:

  1. FMDatabase-表示单个SQLite数据库。用于执行SQL语句。
  2. FMResultSet-表示在上执行查询的结果FMDatabase
  3. FMDatabaseQueue-如果要在多个线程上执行查询和更新,则需要使用此类。在下面的“线程安全”部分中对此进行了描述。

数据库创建

使用FMDatabase指向SQLite数据库文件的路径创建。此路径可以是以下三个路径之一:

  1. 文件系统路径。该文件不必在磁盘上存在。如果它不存在,则会为您创建。
  2. 空字符串(@"")。在临时位置创建一个空数据库。FMDatabase关闭连接后,将删除该数据库
  3. NULL创建一个内存数据库。FMDatabase关闭连接后,该数据库将被销毁

(有关临时和内存数据库的更多信息,请阅读有关此主题的sqlite文档:https : //www.sqlite.org/inmemorydb.html

NSString * path = [ NSTemporaryDirectory()stringByAppendingPathComponent:@“ tmp.db  ];
FMDatabase * db = [FMDatabase
databaseWithPath: path];

与数据库进行交互之前,必须先将其打开。如果没有足够的资源或权限打开和/或创建数据库,则打开失败。

if(![db open ]){
db = nil
}

执行更新

任何不是该SELECT语句的SQL语句都可以视为更新。这包括CREATEUPDATEINSERTALTERCOMMITBEGINDETACHDELETEDROPENDEXPLAINVACUUM,和REPLACE语句(以及许多其他)。基本上,如果您的SQL语句不是以开头SELECT,则它是一条更新语句。

执行更新将返回单个值a BOOL返回值YES表示更新已成功执行,返回值NO表示遇到某些错误。您可以调用-lastErrorMessage-lastErrorCode方法来检索更多信息。

执行查询

一个SELECT语句是一个查询和通过的一个执行-executeQuery...方法。

FMResultSet如果成功,则执行查询将返回一个对象,如果nil失败,则返回一个对象您应该使用-lastErrorMessage-lastErrorCode方法来确定查询失败的原因。

为了遍历查询结果,可以使用while()循环。您还需要从一条记录“步入”到另一条记录。使用FMDB,最简单的方法是这样的:

FMResultSet *s = [db executeQuery:@"SELECT * FROM myTable"];
while ([s next]) {
//retrieve values for each record
}

-[FMResultSet next]在尝试访问查询中返回的值之前,必须始终调用它,即使您只希望得到一个值:

FMResultSet *s = [db executeQuery:@"SELECT COUNT(*) FROM myTable"];
if ([s next]) {
int totalCount = [s intForColumnIndex:0];
}
[s close]; // Call the -close method on the FMResultSet if you cannot confirm whether the result set is exhausted.

FMResultSet 有许多方法可以检索适当格式的数据:

  • intForColumn:
  • longForColumn:
  • longLongIntForColumn:
  • boolForColumn:
  • doubleForColumn:
  • stringForColumn:
  • dateForColumn:
  • dataForColumn:
  • dataNoCopyForColumn:
  • UTF8StringForColumn:
  • objectForColumn:

这些方法中的每一个都还具有一个{type}ForColumnIndex:变体,用于根据结果中列的位置而不是列名来检索数据。

通常情况下,有没有必要-closeFMResultSet自己,因为当任一结果集耗尽出现这种情况。但是,如果仅提取单个请求或其他没有耗尽结果集的请求,则需要在上调用-close方法FMResultSet

在数据库上执行完查询和更新后,应-close建立FMDatabase连接,以便SQLite放弃其在操作过程中获取的任何资源。

[db close];

更多问题与demo下载:https://github.com/ccgus/fmdb

源码下载:fmdb-master.zip







收起阅读 »

iOS 方便操作 CoreData 的快捷方式

MagicalRecordMagicalRecord的灵感来自Ruby on Rails的Active Record获取。该代码的目标是:清理我的核心数据相关代码允许清晰,简单的单行读取当需要优化请求时,仍允许修改NSFetchRequest项目状况该项目的活...
继续阅读 »


MagicalRecord

MagicalRecord的灵感来自Ruby on Rails的Active Record获取。该代码的目标是:

  • 清理我的核心数据相关代码
  • 允许清晰,简单的单行读取
  • 当需要优化请求时,仍允许修改NSFetchRequest

项目状况

该项目的活动已停止,已由Core Data本身取代。我们提供的最新版本是:

  • MagicalRecord 2.4.0是一个稳定的版本,可从标签'2.4.0'或中获得pod 'MagicalRecord', :git => 'https://github.com/magicalpanda/MagicalRecord'
  • 实验版本MagicalRecord 3.0.0,有两种版本,一种是branch release/3.0,另一种是branch maintenance/3.0

使用CocoaPods

在您的项目中集成MagicalRecord的最简单方法之一是使用CocoaPods

  1. 将以下行添加到您的Podfile

    一种。清楚的

     'MagicalRecord'   :GIT中 =>  'https://github.com/magicalpanda/MagicalRecord'

    b。使用CocoaLumberjack作为记录器

     'MagicalRecord / CocoaLumberjack'   :GIT中 =>  'https://github.com/magicalpanda/MagicalRecord'
  2. 在您的项目目录中,运行 pod update

  3. 现在,您应该能够添加#import 到目标的任何源文件中,并开始使用MagicalRecord!

使用Xcode

  1. 作为Git子模块将MagicalRecord添加到您的项目中:

    $ cd MyXcodeProjectFolder
    $ git submodule add https://github.com/magicalpanda/MagicalRecord.git Vendor/MagicalRecord
    $ git commit -m "Add MagicalRecord submodule"
  2. 拖动Vendor/MagicalRecord/MagicalRecord.xcproj到您现有的Xcode项目

  3. 导航到项目的设置,然后选择要将MagicalRecord添加到的目标

  4. 导航到“构建阶段”,然后展开“使用库链接二进制文件”部分

  5. 单击+,然后找到适合您目标平台的MagicalRecord框架版本

  6. 现在,您应该能够添加#import 到目标的任何源文件中,并开始使用MagicalRecord!

注意请注意,如果将Xcode的链接框架自动设置为“否”,则可能需要将CoreData.framework添加到iOS上的项目中,因为UIKit默认情况下不包括Core Data。在OS X上,Cocoa包含核心数据。

类别方法

//目标C 
进口 < MagicalRecord / MagicalRecord.h >
进口 < MagicalRecord / MagicalRecord + ShorthandMethods.h >
进口 < MagicalRecord / MagicalRecordShorthandMethodAliases.h >

如果您使用的是Swift,则需要将这些导入添加到目标的Objective-C桥接标头中。

一旦包含了标题,就应该设置/使用MagicalRecord之前调用+[MagicalRecord enableShorthandMethods]class方法

// Objective-C- 
void)theMethodWhereYouSetupMagicalRecord
{

[MagicalRecord
enableShorthandMethods ];

//按照常规设置MagicalRecord
}
//斯威夫特
func theMethodWhereYouSetupMagicalRecord(){
MagicalRecord
enableShorthandMethods()

//照常设置MagicalRecord

源码下载:MagicalRecord-master.zip


收起阅读 »

iOS超方便的多样式提示框

MBProgressHUDMBProgressHUD是一个iOS嵌入式类,在后台线程中完成工作时显示带有指示符和/或标签的半透明HUD。HUD旨在代替未记录的,UIKit UIProgressHUD具有某些附加功能的专用显示器。要求MBProgres...
继续阅读 »

MBProgressHUD

MBProgressHUD是一个iOS嵌入式类,在后台线程中完成工作时显示带有指示符和/或标签的半透明HUD。HUD旨在代替未记录的,UIKit UIProgressHUD具有某些附加功能的专用显示器

要求

MBProgressHUD适用于iOS 9.0+。它取决于以下Apple框架,大多数Xcode模板应已包含以下框架:

  • Foundation.framework
  • UIKit.framework
  • CoreGraphics.framework

您将需要最新的开发人员工具才能进行构建MBProgressHUD较旧的Xcode版本可能会起作用,但不会明确维护兼容性。

将MBProgressHUD添加到您的项目

CocoaPods

  1. pod 'MBProgressHUD', '~> 1.2.0'
  2. 通过运行安装pod pod install
  3. 随需包含MBProgressHUD #import "MBProgressHUD.h"

Carthage

  1. MBProgressHUD添加到您的Cartfile。例如,github "jdg/MBProgressHUD" ~> 1.2.0
  2. run carthage update
  3. 将MBProgressHUD添加到您的项目中。

SwiftPM / Accio

.package(url: "https://github.com/jdg/MBProgressHUD.git", .upToNextMajor(from: "1.2.0")),

.target(name: "App", dependencies: ["MBProgressHUD"]),

然后在Xcode 11+(SwiftPM)中打开您的项目或运行accio update(Accio)。

源文件

或者,您可以直接将MBProgressHUD.hMBProgressHUD.m源文件添加到您的项目中。

  1. 下载最新的代码版本,或将存储库作为git子模块添加到git跟踪的项目中。
  2. 打开Xcode中的项目,然后拖放MBProgressHUD.hMBProgressHUD.m到您的项目(使用“产品导航视图”)。当询问是否从项目外部提取代码存档时,请确保选择复制项目。
  3. 随需包含MBProgressHUD #import "MBProgressHUD.h"

在运行长时间运行的任务时处理MBProgressHUD时需要遵循的主要原则是使主线程保持无工作状态,因此可以及时更新UI。因此,建议使用MBProgressHUD的方法是在主线程上进行设置,然后将要执行的任务旋转到新线程上。

[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});

MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeAnnularDeterminate;
hud.label.text = @"Loading";
[self doSomethingInBackgroundWithProgressCallback:^(float progress) {
hud.progress = progress;
} completionCallback:^{
[hud hideAnimated:YES];
}];

MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeAnnularDeterminate;
hud.label.text = @"Loading";
NSProgress *progress = [self doSomethingInBackgroundCompletion:^{
[hud hideAnimated:YES];
}];
hud.progressObject = progress;

[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// Do something...
[MBProgressHUD hideHUDForView:self.view animated:YES];
});

您应该注意,在该块内完成之前,不会显示任何在上述块内发出的HUD更新。


更多常见问题:https://github.com/jdg/MBProgressHUD
源码下载:MBProgressHUD-master.zip



收起阅读 »

无敌的 iOS 网络通信库

RestKit是一个现代的Objective-C框架,用于在iOS和Mac OS X上实现RESTful Web服务客户端。它提供了一个强大的对象映射引擎,该引擎与Core Data无缝集成,并提供了一组简单的网络原语,用于映射建立在顶部的HTTP请求和响应。...
继续阅读 »

RestKit是一个现代的Objective-C框架,用于在iOS和Mac OS X上实现RESTful Web服务客户端。它提供了一个强大的对象映射引擎,该引擎与Core Data无缝集成,并提供了一组简单的网络原语,用于映射建立在顶部的HTTP请求和响应。的AFNetworking它具有一组经过精心设计的优雅API,这些API使访问和建模RESTful资源感到不可思议。例如,以下是访问Twitter公共时间轴并将JSON内容转换为Tweet对象数组的方法:

@interface RKTweet : NSObject
@property (nonatomic, copy) NSNumber *userID;
@property (nonatomic, copy) NSString *username;
@property (nonatomic, copy) NSString *text;
@end

RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[RKTweet class]];
[mapping addAttributeMappingsFromDictionary:@{
@"user.name": @"username",
@"user.id": @"userID",
@"text": @"text"
}];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodAny pathPattern:nil keyPath:nil statusCodes:nil];
NSURL *url = [NSURL URLWithString:@"http://api.twitter.com/1/statuses/public_timeline.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
NSLog(@"The public timeline Tweets: %@", [result array]);
} failure:nil];
[operation start];


概述

RestKit被设计为模块化的,每个模块都致力于在整个框架以及与主机平台之间维护最少的依赖关系集。库的核心是对象映射引擎,该引擎负责在表示形式之间转换对象(例如JSON / XML <->本地域对象)。

API快速入门

RestKit分为几个模块,这些模块将映射引擎与HTTP和Core Data集成完全分开,以提供最大的灵活性。每个模块中的键类在下面突出显示,并且每个模块都超链接到源代码中包含的README.md。

对象映射
RKObjectMapping封装用于转换由键-值编码键路径表示的对象表示的配置。
RKAttributeMapping根据源和目标键路径指定对象或实体映射中的属性之间的所需转换。
RKRelationshipMapping根据源和目标键路径以及用来映射子对象属性RKObjectMapping指定嵌套的一个或多个子对象的所需映射
RK动态映射指定一个灵活的映射,在该映射中,关于要使用哪个RKObjectMapping来处理给定文档的决策将推迟到运行时。
RKMapperOperation提供用于将反序列化文档映射到一组本地域对象的接口。
RKMappingOperation一个的NSOperation执行使用对象表示之间的映射RKObjectMapping
联网
RKRequestDescriptor描述可以针对给定对象类型从应用程序发送到远程Web应用程序的请求。
RKResponseDescriptor根据对象映射,键路径,用于匹配URLSOCKit模式以及一组状态码(它们定义了适合映射的情况)描述了可以从远程Web应用程序返回的对象可映射响应。给定的响应。
RKObjectParameterization执行给定对象到NSDictionary表示形式的映射,该表示形式适合用作HTTP请求的参数。
RKObjectRequestOperation一个NSOperation,它使用一组RKResponseDescriptor对象中表示的配置来发送HTTP请求并在已解析的响应主体上执行对象映射
RKResponseMapperOperation一个的NSOperation提供用于对象映射的支撑NSHTTPURLResponse使用一组RKResponseDescriptor对象。
RKObjectManager捕获使用对象映射通过HTTP与RESTful Web应用程序通信的常见模式,包括:
  • 集中化RKRequestDescriptorRKResponseDescriptor配置
  • RKRouter描述URL配置
  • 序列化对象并使用序列化的表示形式发送请求
  • 发送请求以加载远程资源并映射响应主体的对象
  • 构建对象的多部分表单请求
路由器从基本URL和一组RKRoute对象生成NSURL对象,这些对象描述了应用程序使用的相对路径。
RKRoute描述给定对象类型和HTTP方法的单个相对路径,对象的关系或符号名。
核心数据
RKManagedObjectStore封装核心数据配置,包括NSManagedObjectModelNSPersistentStoreCoordinator和一对NSManagedObjectContext对象。
RKEntityMapping为映射建模,以将对象表示形式转换为给定NSEntityDescriptionNSManagedObject实例
RKConnectionDescription描述用于使用外键属性在Core Data实体之间建立关系的映射。
RKManagedObjectRequestOperation一个的NSOperation子类发送所解析响应身体的HTTP请求和执行对象映射来创建NSManagedObject情况下,使用建立对象之间的关系RKConnectionDescription了孤立的对象的对象清洗,并且在远程后端系统不再存在。
RKManagedObjectImporter针对以下两种情况,使用RKEntityMapping对象提供对托管对象的批量映射的支持
  1. 将已解析的文档批量导入到NSPersistentStore中。
  2. 生成种子数据库,以在安装时使用初始数据集初始化应用程序的核心数据存储。
搜索
RKSearchIndexer提供对在Core Data中为应用程序中实体的字符串属性生成全文可搜索索引的支持。
RKSearchPredicate生成给定文本字符串NSCompoundPredicate,该字符串将搜索通过RKSearchIndexer跨任何索引实体建立的索引。
测验
RKMappingTest给定已解析的文档以及对象或实体映射,为单元测试对象映射配置提供支持。根据预期的关键路径映射和/或预期的转换结果来配置预期。
RKTestFixture提供一个接口,可轻松生成用于单元测试的测试夹具数据。
RKTestFactory提供对创建用于测试的对象的支持。


例子

对象请求

//从/articles/1234.json获取单个Article并将其映射到一个对象
// JSON看起来像{“ article”:{“ title”:“ My Article”,“ author”:“ Blake”,“ body” :“非常酷!”}}
RKObjectMapping *映射= [RKObjectMapping
mappingForClass: [Article class ]];
[映射
addAttributeMappingsFromArray: @ [ @“标题@“作者@“正文 ]];
NSIndexSet * statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // 2xx中的任何内容
RKResponseDescriptor * responseDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping:映射方法: RKRequestMethodAny pathPattern:@“ / articles /:articleID keyPath:@“ article statusCodes: statusCodes];

NSURLRequest * request = [ NSURLRequest requestWithURL: [ NSURL URLWithString:@“ http://restkit.org/articles/1234.json ]];
RKObjectRequestOperation * operation = [[RKObjectRequestOperation
alloc ] initWithRequest:请求responseDescriptors: @ [responseDescriptor]];
[操作
setCompletionBlockWithSuccess: ^(RKObjectRequestOperation *操作,RKMappingResult *结果){
Article *文章= [结果
firstObject ];
NSLog@“映射文章:%@ ,文章);
}
错误: ^(RKObjectRequestOperation * operation,NSError * error){
NSLog@“失败,错误:%@ ,[error localizedDescription ]);
}];

[操作
开始];

托管对象请求

//从/articles/888.json获取文章及其类别,并映射到Core Data实体
// // JSON类似于{“ article”:{“ title”:“ My Article”,“ author”:“ Blake”,“正文”:“非常酷!”,“类别”:[{“ id”:1,“名称”:“核心数据”]}
NSManagedObjectModel * managedObjectModel = [ NSManagedObjectModel mergedModelFromBundles:nil ];
RKManagedObjectStore * managedObjectStore = [[RKManagedObjectStore
alloc ] initWithManagedObjectModel: managedObjectModel];
NSError *错误= nil ;
布尔成功= RKEnsureDirectoryExistsAtPath(RKApplicationDataDirectory(),&error);
如果(!成功){
RKLogError@“无法在路径' %@ '%@ 上创建应用程序数据目录RKApplicationDataDirectory(),错误);
}

NSString * path = [ RKApplicationDataDirectory()stringByAppendingPathComponent:@“ Store.sqlite ];
NSPersistentStore * persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:SeedDatabaseAtPath的路径nil withConfiguration:nil 选项:nil 错误:&error];
if(!persistentStore){
RKLogError@“无法在路径' %@ '%@ ”上添加持久性存储,路径错误);
}

[managedObjectStore
createManagedObjectContexts ];

RKEntityMapping * categoryMapping = [RKEntityMapping
mappingForEntityForName:@“ Category inManagedObjectStore: managedObjectStore];
[categoryMapping
addAttributeMappingsFromDictionary: @ { id categoryID @“ name name }]];
RKEntityMapping * articleMapping = [RKEntityMapping
mappingForEntityForName:@“ Article inManagedObjectStore: managedObjectStore];
[articleMapping
addAttributeMappingsFromArray: @ [ @“标题@“作者@“正文 ]];
[articleMapping
addPropertyMapping: [RKRelationshipMapping relationshipMappingFromKeyPath:@“类别 toKeyPath:@“类别 withMapping: categoryMapping]];

NSIndexSet * statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // 2xx中的任何内容
RKResponseDescriptor * responseDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping: articleMapping方法: RKRequestMethodAny pathPattern:@“ / articles /:articleID keyPath:@“ article statusCodes: statusCodes];

NSURLRequest * request = [ NSURLRequest requestWithURL: [ NSURL URLWithString:@“ http://restkit.org/articles/888.json ]];
RKManagedObjectRequestOperation * operation = [[RKManagedObjectRequestOperation
alloc ] initWithRequest:请求responseDescriptors: @ [responseDescriptor]];
operation.managedObjectContext = managedObjectStore.mainQueueManagedObjectContext;

operation.managedObjectCache = managedObjectStore.managedObjectCache;

[操作
setCompletionBlockWithSuccess: ^(RKObjectRequestOperation *操作,RKMappingResult *结果){
Article *文章= [结果
firstObject ];
NSLog@“映射文章:%@ ,文章);
NSLog@“映射类别:%@ ,[article.categories anyObject ]);
}
错误: ^(RKObjectRequestOperation * operation,NSError * error){
NSLog@“失败,错误:%@ ,[error localizedDescription ]);
}];

NSOperationQueue * operationQueue = [ NSOperationQueue new ];
[operationQueue
addOperation:操作];

将客户端错误响应映射到NSError

// GET /articles/error.json返回422(不可处理的实体)
// JSON看起来像{“ errors”:“发生某些错误”}

//您可以将错误映射到任何类,但是会免费提供`RKErrorMessage`
RKObjectMapping * errorMapping = [RKObjectMapping
mappingForClass: [RKErrorMessage]];
//源键路径上包含错误的整个值映射到消息
[errorMapping
addPropertyMapping: [RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@“ errorMessage ]];

NSIndexSet * statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError);
//在4xx状态码范围内的任意响应于与“错误”键路径使用该映射
RKResponseDescriptor * errorDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping: errorMapping方法: RKRequestMethodAny pathPattern: 的keyPath:@ “错误 statusCodes: statusCodes];

NSURLRequest * request = [ NSURLRequest requestWithURL: [ NSURL URLWithString:@“ http://restkit.org/articles/error.json ]];
RKObjectRequestOperation * operation = [[RKObjectRequestOperation
alloc ] initWithRequest:请求responseDescriptors: @ [errorDescriptor]];
[operation
setCompletionBlockWithSuccess: 故障: ^(RKObjectRequestOperation * operation,NSError * error){
//错误所映射到的类的“ description”方法用于构造localizedDescription
NSLog的值@“加载了此错误:%@ ,[错误localizedDescription ]);

//您可以通过`userInfo`
RKErrorMessage * errorMessage = [[error.userInfo
objectForKey: RKObjectMapperErrorObjectsKey] firstObject ];访问用于构造NSError的模型对象
}];

在对象管理器中集中配置

//设置文章和错误响应描述符
//成功的JSON类似于{“ article”:{“ title”:“ My Article”,“ author”:“ Blake”,“ body”:“非常酷!”}}
RKObjectMapping *映射= [RKObjectMapping
mappingForClass: [Article class ]];
[映射
addAttributeMappingsFromArray: @ [ @“标题@“作者@“正文 ]];
NSIndexSet * statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful); // 2xx中的任何内容
RKResponseDescriptor * articleDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping:映射方法: RKRequestMethodAny pathPattern:@“ / articles keyPath:@“ article statusCodes: statusCodes];

//错误JSON看起来像{“错误”:“发生某些错误”}
RKObjectMapping * errorMapping = [RKObjectMapping
mappingForClass: [RKErrorMessage]];
//源键路径上包含错误的整个值映射到消息
[errorMapping
addPropertyMapping: [RKAttributeMapping attributeMappingFromKeyPath:nil toKeyPath:@“ errorMessage ]];
NSIndexSet * statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassClientError);
// 4xx状态码范围内带有“错误”键路径的任何响应都使用此映射
RKResponseDescriptor * errorDescriptor = [RKResponseDescriptor
responseDescriptorWithMapping: errorMapping方法: RKRequestMethodAny pathPattern:nil keyPath:@“ errors statusCodes: statusCodes];

//将描述符添加到管理器
RKObjectManager * manager = [RKObjectManager
managerWithBaseURL: [ NSURL URLWithString:@“ http://restkit.org ]];
[manager
addResponseDescriptorsFromArray: @ [articleDescriptor,errorDescriptor]];

[manager
getObjectsAtPath:@“ /articles/555.json 参数: 成功: ^(RKObjectRequestOperation * operation,RKMappingResult * mappingResult){
//处理articleDescriptor
}
失败: ^(RKObjectRequestOperation * operation,NSError * error){
//传输错误或服务器错误,由errorDescriptor
}]
处理


要求

RestKit需要iOS 8.0或更高版本或Mac OS X 10.9或更高版本。

RestKit中使用了多个第三方开源库,包括:

  1. AFNetworking-网络支持
  2. LibComponentLogging-日志支持
  3. SOCKit-字符串<->对象编码
  4. iso8601parser-支持解析和生成ISO- 8601日期

必须将以下Cocoa框架链接到应用程序目标中,以进行正确的编译:

  1. iOS上的CFNetwork.framework
  2. CoreData.framework
  3. Security.framework
  4. iOS上的MobileCoreServices.frameworkOS X上的CoreServices.framework

并且必须设置以下链接器标志:

  1. -ObjC
  2. -all_load

pods安装

$ cd /path/to/MyProject
$ touch Podfile
$ edit Podfile
target "YOUR PROJECT" do
platform :ios, '7.0'
# Or platform :osx, '10.7'
pod 'RestKit', '~> 0.24.0'
end
# Testing and Search are optional components
pod 'RestKit/Testing', '~> 0.24.0'
pod 'RestKit/Search', '~> 0.24.0'


更多实例及常见问题:https://github.com/RestKit/RestKit

源码下载:RestKit-development.zip


收起阅读 »

一个开源的iOS框架,用于基于GPU的图像和视频处理

概述GPUImage框架是BSD许可的iOS库,可让您将GPU加速的滤镜和其他效果应用于图像,实时摄像机视频和电影。与Core Image(iOS 5.0的一部分)相比,GPUImage允许您编写自己的自定义过滤器,支持部署到iOS 4.0并具有更简单的界面。...
继续阅读 »

概述

GPUImage框架是BSD许可的iOS库,可让您将GPU加速的滤镜和其他效果应用于图像,实时摄像机视频和电影。与Core Image(iOS 5.0的一部分)相比,GPUImage允许您编写自己的自定义过滤器,支持部署到iOS 4.0并具有更简单的界面。但是,它目前缺少Core Image的一些更高级的功能,例如面部检测。

对于诸如处理图像或实时视频帧之类的大规模并行操作,GPU比CPU具有一些明显的性能优势。在iPhone 4上,简单的图像过滤器在GPU上的执行速度比基于CPU的等效过滤器快100倍以上。

但是,在GPU上运行自定义滤镜需要大量代码才能为这些滤镜设置和维护OpenGL ES 2.0渲染目标。我创建了一个示例项目来做到这一点:

http://www.sunsetlakesoftware.com/2010/10/22/gpu-accelerated-video-processing-mac-and-ios

并发现我在创建过程中必须编写许多样板代码。因此,我将这个框架整合在一起,该框架封装了处理图像和视频时遇到的许多常见任务,并且使您不必担心OpenGL ES 2.0的基础。

在处理视频时,此框架与Core Image相比具有优势,在iPhone 4上仅花费2.5 ms即可从相机上传帧,应用伽玛滤镜和显示,而使用Core Image进行相同操作则需要106 ms。基于CPU的处理需要460毫秒,因此使GPUImage在此硬件上进行此操作的速度比Core Image快40倍,比与CPU绑定的处理快184倍。在iPhone 4S上,在这种情况下,GPUImage仅比Core Image快4倍,比CPU绑定处理快102倍。但是,对于更高半径的高斯模糊等更复杂的操作,Core Image当前超过GPUImage。


技术要求

  • OpenGL ES 2.0:使用此功能的应用程序将无法在原始iPhone,iPhone 3G以及第一代和第二代iPod touch上运行
  • iOS 4.1作为部署目标(4.0没有电影阅读所需的某些扩展)。如果您希望在拍摄静态照片时显示实时视频预览,则需要iOS 4.3作为部署目标。
  • iOS 5.0 SDK构建
  • 设备必须具有摄像头才能使用与摄像头相关的功能(显然)
  • 该框架使用自动引用计数(ARC),但如果将其添加为子项目,则应同时支持使用ARC和手动引用计数的项目,如下所述。对于针对iOS 4.x的手动引用计数应用程序,您需要在应用程序项目的“其他链接器标志”中添加-fobjc-arc。

通用架构

GPUImage使用OpenGL ES 2.0着色器以比在CPU绑定例程中更快的速度执行图像和视频操作。但是,它在简化的Objective-C界面中隐藏了与OpenGL ES API交互的复杂性。通过此接口,您可以定义图像和视频的输入源,将过滤器成串连接,并将处理后的图像或视频发送到屏幕,UIImage或磁盘上的电影。

视频图像或视频帧是从源对象上传的,源对象是GPUImageOutput的子类。其中包括GPUImageVideoCamera(用于来自iOS相机的实时视频),GPUImageStillCamera(用于使用相机拍摄照片),GPUImagePicture(用于静止图像)和GPUImageMovie(用于电影)。源对象将静止图像帧作为纹理上传到OpenGL ES,然后将这些纹理移交给处理链中的下一个对象。

链中的滤镜和其他后续元素符合GPUImageInput协议,该协议使它们可以从链中的上一个链接中获取或处理纹理,并对它们进行处理。距离目标更远一步的对象被视为目标,可以通过将多个目标添加到单个输出或过滤器来分支处理。

例如,如果某个应用程序从摄像机中获取实时视频,然后将该视频转换为棕褐色调,然后在屏幕上显示该视频,则会建立一个类似于以下内容的链:

GPUImageVideoCamera -> GPUImageSepiaFilter -> GPUImageView            

将静态库添加到您的iOS项目

注意:如果要在Swift项目中使用它,则需要使用“将其添加为框架”部分中的步骤,而不要使用以下步骤。Swift需要第三方代码模块。

一旦有了该框架的最新源代码,就可以很容易地将其添加到您的应用程序中。首先将GPUImage.xcodeproj文件拖动到应用程序的Xcode项目中,以将框架嵌入项目中。接下来,转到应用程序的目标,然后将GPUImage添加为目标依赖项。最后,您需要将libGPUImage.a库从GPUImage框架的Products文件夹拖到应用程序目标的Link Binary With Libraries构建阶段。

GPUImage需要将其他一些框架链接到您的应用程序,因此您需要在应用程序目标中添加以下内容作为链接库:

  • CoreMedia
  • CoreVideo
  • OpenGLES
  • AVFoundation
  • QuartzCore


您还需要找到框架标头,因此在项目的构建设置中,将“标头搜索路径”设置为从应用程序到GPUImage源目录内的framework /子目录的相对路径。使此标头搜索路径是递归的。

要在您的应用程序中使用GPUImage类,只需使用以下内容包括核心框架头即可:

#import "GPUImage.h"

注意:如果尝试使用Interface Builder构建接口时,如果遇到错误“ Interface Builder中的未知类GPUImageView”或类似错误,则可能需要在项目的构建设置中将-ObjC添加到Other Linker Flags。

另外,如果您需要将其部署到iOS 4.x,则似乎当前版本的Xcode(4.3)要求您在最终应用程序中弱链接Core Video框架,否则会崩溃并显示消息“找不到符号” :_CVOpenGLESTextureCacheCreate”,当您创建要上传到App Store或临时分发的归档文件时。为此,请转到项目的“构建阶段”选项卡,展开“使用库链接二进制文件”组,然后在列表中找到CoreVideo.framework。将列表最右边的设置从“必需”更改为“可选”。

此外,这是一个支持ARC的框架,因此,如果要在针对iOS 4.x的手动引用计数应用程序中使用此框架,则还需要将-fobjc-arc添加到“其他链接器标志”中。

在命令行上构建静态库

如果您不想将项目作为依赖项包含在应用程序的Xcode项目中,则可以为iOS Simulator或设备构建通用静态库。为此,请build.sh在命令行上运行生成的库和头文件将位于build/Release-iphone您也可以通过更改中的IOSSDK_VER变量来更改iOS SDK的build.sh版本(可以使用找到所有可用版本xcodebuild -showsdks)。

将此作为框架(模块)添加到Mac或iOS项目

Xcode 6和iOS 8和Mac一样,都支持使用完整的框架,从而简化了将其添加到应用程序中的过程。要将其添加到您的应用程序中,建议将.xcodeproj项目文件拖到您的应用程序项目中(就像在静态库目标中一样)。

对于您的应用程序,转到其目标构建设置,然后选择“构建阶段”选项卡。在“目标依赖项”分组下,在iOS上添加GPUImageFramework(而不是在构建静态库的GPUImage上),或者在Mac上添加GPUImage。在“使用库链接二进制文件”部分下,添加GPUImage.framework。

这将导致GPUImage构建为框架。在Xcode 6下,它也将作为模块构建,从而允许您在Swift项目中使用它。如上设置时,您只需要使用

import GPUImage

把它拉进去。

然后,您需要添加一个新的“复制文件”构建阶段,将“目标”设置为“框架”,然后向其添加GPUImage.framework构建产品。这将使框架与您的应用程序捆绑在一起(否则,在执行时,您将看到神秘的“ dyld:库未加载:@ rpath / GPUImage.framework / GPUImage”错误)。


过滤实况视频

要从iOS设备的摄像头过滤实时视频,可以使用以下代码:

GPUImageVideoCamera *videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionBack];
videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;

GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];
GPUImageView *filteredVideoView = [[GPUImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, viewWidth, viewHeight)];

// Add the view somewhere so it's visible

[videoCamera addTarget:customFilter];
[customFilter addTarget:filteredVideoView];

[videoCamera startCameraCapture];

这会使用尝试以640x480捕获的预设来设置来自iOS设备的背面摄像头的视频源。该视频是通过处于纵向模式的界面捕获的,在该模式下,横向安装的摄像机需要在显示之前旋转其视频帧。然后,使用文件CustomShader.fsh中的代码将自定义滤镜设置为来自摄像机的视频帧的目标。最终,这些过滤后的视频帧将在UIView子类的帮助下显示在屏幕上,该子类可以呈现此管道产生的过滤后的OpenGL ES纹理。

可以通过设置GPUImageView的fillMode属性来更改其填充模式,这样,如果源视频的长宽比与视图的长宽比不同,则视频将被拉伸,以黑条居中或缩放以填充。

对于混合滤镜和其他吸收多个图像的滤镜,您可以创建多个输出并将单个滤镜添加为这两个输出的目标。将输出添加为目标的顺序将影响混合或以其他方式处理输入图像的顺序。

另外,如果您希望启用麦克风音频捕获以录制到电影中,则需要将相机的audioEncodingTarget设置为您的电影编写者,如下所示:

videoCamera.audioEncodingTarget = movieWriter;

捕获和过滤静止照片

要捕获和过滤静态照片,可以使用与过滤视频类似的过程。您可以使用GPUImageStillCamera代替GPUImageVideoCamera:

stillCamera = [[GPUImageStillCamera alloc] init];
stillCamera.outputImageOrientation = UIInterfaceOrientationPortrait;

filter = [[GPUImageGammaFilter alloc] init];
[stillCamera addTarget:filter];
GPUImageView *filterView = (GPUImageView *)self.view;
[filter addTarget:filterView];

[stillCamera startCameraCapture];

这将为您提供静态相机预览视频的实时,经过过滤的提要。请注意,此预览视频仅在iOS 4.3及更高版本上提供,因此,如果您希望具有此功能,则可能需要将其设置为部署目标。

一旦要捕获照片,就可以使用如下所示的回调块:

[stillCamera capturePhotoProcessedUpToFilter:filter withCompletionHandler:^(UIImage *processedImage, NSError *error){
NSData *dataForJPEGFile = UIImageJPEGRepresentation(processedImage, 0.8);

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

NSError *error2 = nil;
if (![dataForJPEGFile writeToFile:[documentsDirectory stringByAppendingPathComponent:@"FilteredPhoto.jpg"] options:NSAtomicWrite error:&error2])
{
return;
}
}];

上面的代码捕获由预览视图中使用的相同滤镜链处理的原尺寸照片,并将该照片以JPEG格式保存到磁盘中,位于应用程序的文档目录中。

请注意,由于纹理大小的限制,该框架目前无法在较旧的设备(iPhone 4S,iPad 2或Retina iPad之前的设备)上处理宽度大于2048像素或高的图像。这意味着其相机输出的静态照片大于该尺寸的iPhone 4将无法捕获此类照片。正在实施一种切片机制来解决此问题。所有其他设备都应该能够使用此方法捕获和过滤照片。

处理静止图像

有两种方法可以处理静止图像并创建结果。实现此目的的第一种方法是创建一个静止图像源对象并手动创建一个滤镜链:

UIImage *inputImage = [UIImage imageNamed:@"Lambeau.jpg"];

GPUImagePicture *stillImageSource = [[GPUImagePicture alloc] initWithImage:inputImage];
GPUImageSepiaFilter *stillImageFilter = [[GPUImageSepiaFilter alloc] init];

[stillImageSource addTarget:stillImageFilter];
[stillImageFilter useNextFrameForImageCapture];
[stillImageSource processImage];

UIImage *currentFilteredVideoFrame = [stillImageFilter imageFromCurrentFramebuffer];

请注意,要从过滤器手动捕获图像,需要设置-useNextFrameForImageCapture以便告诉过滤器以后需要从其捕获。默认情况下,GPUImage重用过滤器中的帧缓冲区以节省内存,因此,如果您需要保留过滤器的帧缓冲区以进行手动图像捕获,则需要提前告知它。

对于要应用于图像的单个滤镜,只需执行以下操作:

GPUImageSepiaFilter *stillImageFilter2 = [[GPUImageSepiaFilter alloc] init];
UIImage *quickFilteredImage = [stillImageFilter2 imageByFilteringImage:inputImage];

编写自定义过滤器

与iOS(从iOS 5.0开始)上的Core Image相比,此框架的一大优势是能够编写自己的自定义图像和视频处理过滤器。这些过滤器作为OpenGL ES 2.0片段着色器提供,以类似C的OpenGL着色语言编写。

自定义过滤器使用以下代码初始化

GPUImageFilter *customFilter = [[GPUImageFilter alloc] initWithFragmentShaderFromFile:@"CustomShader"];

其中片段着色器使用的扩展名是.fsh。此外,如果您不希望将片段着色器发送到应用程序包中,则可以使用-initWithFragmentShaderFromString:初始值设定项将片段着色器提供为字符串。

片段着色器对在该滤镜阶段要渲染的每个像素执行其计算。他们使用OpenGL阴影语言(GLSL)进行此操作,这是一种类似于C的语言,并附加了2-D和3-D图形。以下棕褐色调滤镜是片段着色器的一个示例:

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;

void main()
{
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
lowp vec4 outputColor;
outputColor.r = (textureColor.r * 0.393) + (textureColor.g * 0.769) + (textureColor.b * 0.189);
outputColor.g = (textureColor.r * 0.349) + (textureColor.g * 0.686) + (textureColor.b * 0.168);
outputColor.b = (textureColor.r * 0.272) + (textureColor.g * 0.534) + (textureColor.b * 0.131);
outputColor.a = 1.0;

gl_FragColor = outputColor;
}

为了使图像过滤器可在GPUImage框架中使用,需要前两行采用textureCoordinate变化(对于纹理中的当前坐标,归一化为1.0)和inputImageTexture统一(对于实际的输入图像帧纹理) 。

着色器的其余部分在传入的纹理中的该位置捕获像素的颜色,以产生棕褐色调的方式对其进行操作,并将该像素颜色写出以用于下一步处理管道。

将片段着色器添加到Xcode项目时要注意的一件事是Xcode认为它们是源代码文件。要解决此问题,您需要将着色器从“编译源”构建阶段手动移至“复制捆绑包资源”,以便使着色器包含在应用程序捆绑包中。

过滤和重新编码电影

可以通过GPUImageMovie类将电影加载到框架中,进行过滤,然后使用GPUImageMovieWriter将其写出。GPUImageMovieWriter的速度也足够快,可以以640x480的速度从iPhone 4的摄像头实时录制视频,因此可以将直接过滤的视频源输入其中。目前,GPUImageMovieWriter的速度足以在iPhone 4上以高达20 FPS的速度录制实时720p视频,而在iPhone 4S(以及新iPad)上以30 FPS的速度录制720p和1080p的视频。

以下是如何加载示例电影,将其通过像素化滤镜传递,然后将结果记录为480 x 640 h.264电影的示例:

movieFile = [[GPUImageMovie alloc] initWithURL:sampleURL];
pixellateFilter = [[GPUImagePixellateFilter alloc] init];

[movieFile addTarget:pixellateFilter];

NSString *pathToMovie = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/Movie.m4v"];
unlink([pathToMovie UTF8String]);
NSURL *movieURL = [NSURL fileURLWithPath:pathToMovie];

movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(480.0, 640.0)];
[pixellateFilter addTarget:movieWriter];

movieWriter.shouldPassthroughAudio = YES;
movieFile.audioEncodingTarget = movieWriter;
[movieFile enableSynchronizedEncodingUsingMovieWriter:movieWriter];

[movieWriter startRecording];
[movieFile startProcessing];

录制完成后,您需要从筛选器链中删除电影录像机,并使用如下代码关闭录制:

[pixellateFilter removeTarget:movieWriter];
[movieWriter finishRecording];

电影必须先完成,然后才能使用,因此,如果在此之前将其中断,则录制将丢失。

与OpenGL ES交互

GPUImage可以分别通过使用其GPUImageTextureOutput和GPUImageTextureInput类从OpenGL ES导出和导入纹理。这样,您就可以记录来自OpenGL ES场景的电影,该电影使用绑定的纹理渲染到帧缓冲区对象,或者过滤视频或图像,然后将它们作为纹理显示在OpenGL ES中,以在场景中显示。

使用此方法的一个警告是,必须通过共享组或类似方式在GPUImage的OpenGL ES上下文和任何其他上下文之间共享这些过程中使用的纹理。

内置过滤器

当前有125个内置过滤器,分为以下类别:

色彩调整

  • GPUImageBrightnessFilter:调整图像的亮度

    • 亮度:调整后的亮度(-1.0-1.0,默认值为0.0)
  • GPUImageExposureFilter:调整图像的曝光

    • 曝光:调整后的曝光(-10.0-10.0,默认值为0.0)
  • GPUImageContrastFilter:调整图像的对比度

    • 对比度:调整后的对比度(0.0-4.0,默认值为1.0)
  • GPUImageSaturationFilter:调整图像的饱和度

    • 饱和度:应用于图像的饱和度或去饱和度(0.0-2.0,默认值为1.0)
  • GPUImageGammaFilter:调整图像的灰度系数

    • gamma:要应用的gamma调整(0.0-3.0,默认值为1.0)
  • GPUImageLevelsFilter:类似Photoshop的色阶调整。min,max,minOut和maxOut参数在[0,1]范围内浮动。如果来自Photoshop的参数在[0,255]范围内,则必须首先将它们转换为[0,1]。gamma / mid参数是一个float> =0。它与Photoshop中的值匹配。如果要对RGB以及各个通道应用级别,则需要两次使用此滤镜-首先用于单个通道,然后用于所有通道。

  • GPUImageColorMatrixFilter:通过将矩阵应用于图像来变换图像的颜色

    • colorMatrix:一个4x4矩阵,用于转换图像中的每种颜色
    • 强度:新转换的颜色替换每个像素的原始颜色的程度
  • GPUImageRGBFilter:调整图像的各个RGB通道

    • 红色:每个色彩通道相乘的归一化值。范围从0.0开始,默认值为1.0。
    • 绿色
    • 蓝色
  • GPUImageHueFilter:调整图像的色调

    • 色调:色调角度,以度为单位。默认为90度
  • GPUImageVibranceFilter:调整图像的鲜艳度

    • vibrance:要应用的vibrance调整,默认设置为0.0,建议的最小/最大值分别为-1.2和1.2。
  • GPUImageWhiteBalanceFilter:调整图像的白平衡。

    • temperature:调整图像所用的温度,以ºK为单位。值4000非常凉爽,而7000非常温暖。默认值为5000。请注意,在4000和5000之间的比例在视觉上与在5000和7000之间的比例几乎相同。
    • 色调:用于调整图像的色调。值-200表示非常绿色,而200表示非常粉红色。默认值为0。
  • GPUImageToneCurveFilter:基于样条曲线为每个颜色通道调整图像的颜色。

    • redControlPoints
    • greenControlPoints
    • blueControlPoints
    • rgbCompositeControlPoints:色调曲线采用一系列控制点,这些控制点定义了每个颜色分量或复合物中所有三个分量的样条曲线。这些以NSValue包裹的CGPoints的形式存储在NSArray中,其标准化的X和Y坐标为0-1。默认值为(0,0),(0.5,0.5),(1,1)。
  • GPUImageHighlightShadowFilter:调整图像的阴影和高光

    • 阴影:增加阴影以使阴影变淡,从0.0到1.0,默认值为0.0。
    • Highlights:从1.0降低到0.0,以1.0为默认值将高光变暗。
  • GPUImageHighlightShadowTintFilter:允许您使用颜色和强度独立地着色图像的阴影和高光

    • shadowTintColor:阴影色调RGB颜色(GPUVector4)。默认值:({1.0f, 0.0f, 0.0f, 1.0f}红色)。
    • highlightTintColor:高亮色调RGB颜色(GPUVector4)。默认值:({0.0f, 0.0f, 1.0f, 1.0f}蓝色)。
    • shadowTintIntensity:阴影着色强度,从0.0到1.0。默认值:0.0
    • highlightTintIntensity:突出显示色调强度,从0.0到1.0,默认值为0.0。
  • GPUImageLookupFilter:使用RGB颜色查找图像来重新映射图像中的颜色。首先,使用您最喜欢的照片编辑应用程序将滤镜应用于GPUImage / framework / Resources中的lookup.png。为了使其正常工作,每个像素的颜色都不得依赖于其他像素(例如,模糊将不起作用)。如果需要更复杂的过滤器,则可以根据需要创建任意多个查找表。准备就绪后,将新的lookup.png文件用作GPUImageLookupFilter的第二个输入。

  • GPUImageAmatorkaFilter:基于Amatorka的Photoshop动作的照片滤镜:http ://amatorka.deviantart.com/art/Amatorka-Action-2-121069631 如果要使用此效果,则必须将GPUImage Resources文件夹中的lookup_amatorka.png添加到应用程序包中。

  • GPUImageMissEtikateFilter:基于Etikate小姐的Photoshop动作的照片滤镜:http ://miss-etikate.deviantart.com/art/Photoshop-Action-15-120151961 如果要使用此效果,则必须将GPUImage Resources文件夹中的lookup_miss_etikate.png添加到应用程序包中。

  • GPUImageSoftEleganceFilter:另一个基于查找的颜色重新映射滤镜。如果要使用此效果,则必须将GPUImage Resources文件夹中的lookup_soft_elegance_1.png和lookup_soft_elegance_2.png添加到应用程序包中。

  • GPUImageSkinToneFilter:肤色调整滤镜,可影响浅色肤色的唯一范围,并相应地调整粉红色/绿色或粉红色/橙色范围。默认值针对白皙的皮肤,但可以根据需要进行调整。

    • skinToneAdjust:调整肤色的量。默认值:0.0,建议的最小/最大值:分别为-0.3和0.3。
    • skinHue:要检测的皮肤色调。默认值:0.05(白皙至泛红皮肤)。
    • skinHueThreshold:皮肤色调的变化量。默认值:40.0。
    • maxHueShift:允许的最大色相偏移量。默认值:0.25
    • maxSaturationShift =要移动的最大饱和量(使用橙色时)。默认值:0.4
    • upperSkinToneColor =GPUImageSkinToneUpperColorGreenGPUImageSkinToneUpperColorOrange
  • GPUImageColorInvertFilter:反转图像的颜色

  • GPUImageGrayscaleFilter:将图像转换为灰度(饱和度滤镜的实现速度稍快,但无法改变颜色的贡献)

  • GPUImageMonochromeFilter:根据每个像素的亮度,将图像转换为单色版本

    • strength:特定颜色替换正常图像颜色的程度(0.0-1.0,默认值为1.0)
    • color:用作效果基础的颜色,默认为(0.6,0.45,0.3,1.0)。
  • GPUImageFalseColorFilter:使用图像的亮度在两种用户指定的颜色之间进行混合

    • firstColor:第一种和第二种颜色分别指定什么颜色替换图像的暗区和亮区。默认值为(0.0,0.0,0.5)amd(1.0,0.0,0.0)。
    • secondColor
  • GPUImageHazeFilter:用于添加或删除雾度(类似于UV滤镜)

    • distance:所应用颜色的强度。默认值为0。最佳值为-.3到.3。
    • 斜率:颜色变化的量。默认值为0。最佳值为-.3到.3。
  • GPUImageSepiaFilter:简单的棕褐色调滤镜

    • 强度:棕褐色替换正常图像颜色的程度(0.0-1.0,默认值为1.0)
  • GPUImageOpacityFilter:调整传入图像的Alpha通道

    • opacity:将每个像素的输入Alpha通道乘以(0.0-1.0,默认值为1.0)的值
  • GPUImageSolidColorGenerator:这会输出生成的纯色图像。您需要使用-forceProcessingAtSize定义图像大小:

    • color:用于填充图像的颜色,采用四部分格式。
  • GPUImageLuminanceThresholdFilter:亮度高于阈值的像素将显示为白色,而低于阈值的像素将为黑色

    • threshold:亮度阈值,从0.0到1.0,默认值为0.5
  • GPUImageAdaptiveThresholdFilter:确定像素周围的局部亮度,如果像素低于该局部亮度,则将其变为黑色,如果高于该像素,则将其变为白色。这对于在变化的光照条件下挑选文本很有用。

    • blurRadiusInPixels:背景平均模糊半径(以像素为单位)的倍数,默认为4。
  • GPUImageAverageLuminanceThresholdFilter:这将应用阈值操作,其中将根据场景的平均亮度连续调整阈值。

    • thresholdMultiplier:这是平均亮度将被乘以达到最终使用阈值的一个因素。默认情况下,该值为1.0。
  • GPUImageHistogramFilter:这将分析传入的图像并创建一个输出直方图,其输出每个颜色值的频率。该滤波器的输出是一个3像素高,256像素宽的图像,其中心(垂直)像素包含与发生各种颜色值的频率相对应的像素。每个颜色值占据256个宽度位置之一,从左侧的0到右侧的255。可以为单个颜色通道(kGPUImageHistogramRed,kGPUImageHistogramGreen,kGPUImageHistogramBlue),图像亮度(kGPUImageHistogramLuminance)或一次为所有三个颜色通道(kGPUImageHistogramRGB)生成此直方图。

    • downsamplingFactor:不是决定对每个像素进行采样,而是决定要对图像的哪个部分进行采样。默认情况下,它是16,最小值为1。这是防止饱和直方图的必要条件,直方图在每种颜色值变得过载之前只能记录256个像素。
  • GPUImageHistogramGenerator:这是一个特殊的过滤器,主要用于与GPUImageHistogramFilter一起使用。它生成由GPUImageHistogramFilter生成的颜色直方图的输出表示形式,但可以重新用于显示其他类型的值。它获取图像并查看中心(垂直)像素。然后,它将RGB分量的数值绘制在输出纹理的单独的彩色图形中。您可能需要为此过滤器设置一个大小,以使其输出可见。

  • GPUImageAverageColor:通过对图像中每个像素的RGBA分量求平均值,此处理输入图像并确定场景的平均颜色。约简过程用于在GPU上逐步对源图像进行下采样,然后在CPU上进行短暂的平均计算。该过滤器的输出毫无意义,但是您需要将colorAverageProcessingFinishedBlock属性设置为一个包含四个颜色分量和一个帧时间并对它们执行某些操作的块。

  • GPUImageLuminosity:与GPUImageAverageColor一样,这会将图像降低到其平均亮度。您需要设置luminosityProcessingFinishedBlock来处理此过滤器的输出,该过滤器仅返回一个发光度值和一个帧时间。

  • GPUImageChromaKeyFilter:对于图像中的给定颜色,将alpha通道设置为0。这与GPUImageChromaKeyBlendFilter相似,只是不将第二个图像混合为匹配的颜色,而不会在第二个图像中取而代之颜色透明。

    • thresholdSensitivity:颜色匹配需要与要替换的目标颜色之间的接近程度(默认值为0.4)
    • 平滑:混合颜色时的平滑程度(默认值为0.1)

图像处理

  • GPUImageTransformFilter:这会将任意2D或3D变换应用于图像

    • affineTransform:这需要一个CGAffineTransform来调整二维图像
    • transform3D:这需要一个CATransform3D来处理3-D图像
    • ignoreAspectRatio:默认情况下,将保持变换图像的纵横比,但是可以将其设置为YES,以使变换与纵横比无关
  • GPUImageCropFilter:将图像裁剪到特定区域,然后仅将该区域传递到滤镜的下一个阶段

    • cropRegion:要裁剪出图像的矩形区域,将其标准化为0.0-1.0的坐标。(0.0,0.0)位置在图像的左上方。
  • GPUImageLanczosResamplingFilter:这使您可以使用Lanczos重采样对图像进行上采样或下采样,其质量明显优于标准线性或三线性插值。只需使用-forceProcessingAtSize:即可设置过滤器的目标输出分辨率,然后将针对该新大小重新采样图像。

  • GPUImageSharpenFilter:锐化图像

    • 清晰度:要应用的清晰度调整(-4.0-4.0,默认值为0.0)
  • GPUImageUnsharpMaskFilter:应用不清晰的蒙版

    • blurRadiusInPixels:基础高斯模糊的模糊半径。默认值为4.0。
    • 强度:锐化的强度,从在多达0.0,与1.0的默认
  • GPUImageGaussianBlurFilter:硬件优化的可变半径高斯模糊

    • texelSpacingMultiplier:像素间距的乘数,范围从0.0开始,默认值为1.0。进行调整可能会稍微增加模糊强度,但会在结果中引入伪影。强烈建议您先使用其他参数,然后再触摸此参数。
    • blurRadiusInPixels:用于模糊的半径(以像素为单位),默认值为2.0。这将调整高斯分布函数中的sigma变量。
    • blurRadiusAsFractionOfImageWidth
    • blurRadiusAsFractionOfImageHeight:设置这些属性将允许模糊半径随图像的大小缩放
    • blurPasses:顺序模糊传入图像的次数。通过的次数越多,过滤器的速度就越慢。
  • GPUImageBoxBlurFilter:硬件优化的可变半径框模糊

    • texelSpacingMultiplier:像素间距的乘数,范围从0.0开始,默认值为1.0。进行调整可能会稍微增加模糊强度,但会在结果中引入伪影。强烈建议您先使用其他参数,然后再触摸此参数。
    • blurRadiusInPixels:用于模糊的半径(以像素为单位),默认值为2.0。这将调整高斯分布函数中的sigma变量。
    • blurRadiusAsFractionOfImageWidth
    • blurRadiusAsFractionOfImageHeight:设置这些属性将允许模糊半径随图像的大小缩放
    • blurPasses:顺序模糊传入图像的次数。通过的次数越多,过滤器的速度就越慢。
  • GPUImageSingleComponentGaussianBlurFilter:对GPUImageGaussianBlurFilter的修改,仅对红色组件起作用

    • texelSpacingMultiplier:像素间距的乘数,范围从0.0开始,默认值为1.0。进行调整可能会稍微增加模糊强度,但会在结果中引入伪影。强烈建议您先使用其他参数,然后再触摸此参数。
    • blurRadiusInPixels:用于模糊的半径(以像素为单位),默认值为2.0。这将调整高斯分布函数中的sigma变量。
    • blurRadiusAsFractionOfImageWidth
    • blurRadiusAsFractionOfImageHeight:设置这些属性将允许模糊半径随图像的大小缩放
    • blurPasses:顺序模糊传入图像的次数。通过的次数越多,过滤器的速度就越慢。
  • GPUImageGaussianSelectiveBlurFilter:高斯模糊,可将焦点保留在圆形区域内

    • blurRadiusInPixels:用于模糊的半径(以像素为单位),默认值为5.0。这将调整高斯分布函数中的sigma变量。
    • excludeCircleRadius:从模糊中排除的圆形区域的半径
    • excludeCirclePoint:从模糊中排除的圆形区域的中心
    • excludeBlurSize:模糊部分和透明圆之间的区域大小
    • AspectRatio:图像的纵横比,用于调整对焦区域的圆度。默认情况下,它与图像的宽高比匹配,但是您可以覆盖此值。
  • GPUImageGaussianBlurPositionFilterGPUImageGaussianSelectiveBlurFilter的反函数,仅在特定圆圈内应用模糊

    • blurSize:模糊大小的乘数,范围从0.0开始,默认为1.0
    • blurCenter:模糊的中心,默认为0.5、0.5
    • blurRadius:模糊的半径,默认为1.0
  • GPUImageiOSBlurFilter:尝试在控制中心等地方复制iOS 7上使用的背景模糊。

    • blurRadiusInPixels:用于模糊的半径(以像素为单位),默认值为12.0。这将调整高斯分布函数中的sigma变量。
    • 饱和度:饱和度范围从0.0(完全饱和)到2.0(最大饱和度),正常水平为0.8
    • 下采样:下采样然后上采样传入图像的程度,以最小化高斯模糊内的计算,默认值为4.0。
  • GPUImageMedianFilter:获取3x3区域中三个颜色分量的中值

  • GPUImageBilateralFilter:双向模糊,它尝试在保留锐利边缘的同时模糊相似的颜色值

    • texelSpacingMultiplier:texel读取之间的间隔的乘数,范围从0.0开始,默认为4.0
    • distanceNormalizationFactor:中心颜色和样本颜色之间的距离的归一化因子,默认值为8.0。
  • GPUImageTiltShiftFilter:模拟的倾斜移位镜头效果

    • blurRadiusInPixels:基础模糊的半径,以像素为单位。默认情况下是7.0。
    • topFocusLevel:图像中对焦区域顶部的标准化位置,此值应小于bottomFocusLevel,默认值为0.4
    • bottomFocusLevel:图像中对焦区域底部的标准化位置,此值应高于topFocusLevel,默认值为0.6
    • focusFallOffRate:图像从对焦区域模糊的速率,默认为0.2
  • GPUImage3x3ConvolutionFilter:对图像运行3x3卷积内核

    • convolutionKernel:卷积内核是一个3x3的值矩阵,适用于该像素及其周围的8个像素。矩阵以行优先顺序指定,左上像素为one.one,右下像素为three.three。如果矩阵中的值之和不等于1.0,则图像可能变亮或变暗。
  • GPUImageSobelEdgeDetectionFilter:Sobel边缘检测,边缘以白色突出显示

    • texelWidth
    • texelHeight:这些参数影响检测到的边缘的可见性
    • edgeStrength:调整滤镜的动态范围。较高的值会导致边缘更牢固,但会饱和强度颜色空间。默认值为1.0。
  • GPUImagePrewittEdgeDetectionFilter:Prewitt边缘检测,边缘以白色突出显示

    • texelWidth
    • texelHeight:这些参数影响检测到的边缘的可见性
    • edgeStrength:调整滤镜的动态范围。较高的值会导致边缘更牢固,但会饱和强度颜色空间。默认值为1.0。
  • GPUImageThresholdEdgeDetectionFilter:执行Sobel边缘检测,但是应用阈值而不是给出逐渐的强度值

    • texelWidth
    • texelHeight:这些参数影响检测到的边缘的可见性
    • edgeStrength:调整滤镜的动态范围。较高的值会导致边缘更牢固,但会饱和强度颜色空间。默认值为1.0。
    • threshold:高于此阈值的任何边缘将为黑色,低于白色的任何边缘。范围从0.0到1.0,默认值为0.8
  • GPUImageCannyEdgeDetectionFilter:这使用完整的Canny流程突出显示一像素宽的边缘

    • texelWidth
    • texelHeight:这些参数影响检测到的边缘的可见性
    • blurRadiusInPixels:高斯模糊的基础模糊半径。默认值为2.0。
    • blurTexelSpacingMultiplier:基础的模糊纹理像素间距乘数。默认值为1.0。
    • upperThreshold:任何梯度幅度大于此阈值的边都将通过并显示在最终结果中。默认值为0.4。
    • lowerThreshold:任何梯度幅度低于此阈值的边将失败,并从最终结果中删除。默认值为0.1。
  • GPUImageHarrisCornerDetectionFilter:在输入图像上运行哈里斯角点检测算法,并生成一个图像,这些角点为白色像素,其他所有颜色均为黑色。可以设置cornersDetectedBlock,并且在回调中将为您提供您要执行的所有其他操作的角列表(以标准化的0..1 X,Y坐标表示)。

    • blurRadiusInPixels:基础高斯模糊的半径。默认值为2.0。
    • 敏感度:一个内部比例因子,用于调整在滤镜中生成的边角图的动态范围。默认值为5.0。
    • threshold:检测到一个点作为拐角的阈值。根据尺寸,光线条件和iOS设备摄像头类型的不同,此方法可能会有很大的不同,因此可能需要一些试验才能确定适合您的情况。默认值为0.20。
附件下载及详细介绍及常见问题:https://github.com/BradLarson/GPUImage


收起阅读 »

iOS仿高德路线规划滑动效果

因为项目有个界面要模仿高德地图路径规划滑动效果,因此写了demo,并简单说下分析过程高德地图效果演示:demo效果演示:Demo地址:https://github.com/fangjinfeng/MySampleCode/tree/master/FJFBlog...
继续阅读 »

因为项目有个界面要模仿高德地图路径规划滑动效果,因此写了demo,并简单说下分析过程

高德地图效果演示:


demo效果演示:


Demo地址:https://github.com/fangjinfeng/MySampleCode/tree/master/FJFBlogProjectDemo

一. 分析

  • 首先,我们可以看出这个滚动的视图应该是UIScrollView或者UIScrollView的子类(比如:UITableView);

  • 其次,从高德地图里的视图一开始的滑动,可以看出这个滑动是平稳的滑动,没有加速和减速,因此这里不可能是UIScrollView的滚动效果,因为UIScrollView的滚动效果是由一个加减速的过程,因此一开始滑动,应该是通过滑动手势UIPanGestureRecognizer,来移动UIScrollView的y值来移动

  • 接着滑动到指定位置之后,UIScrollView的y值固定不动,然后UIScrollView的内容进行滚动。这里就涉及到滑动手势UIPanGestureRecognizer的滑动,还有UIScrollView内部的滚动的处理。高德地图的演示效果里面,一开始滑动视图向上移动,移动到指定的点之后,立马就变成视图的滚动,这里可以分析,UIScrollView既支持手势的滑动又支持视图的滚动,只是通过条件来判断限制两者的执行逻辑。

  • 同时我们可以看到,如果一开始向上拉动视图力度大一点,视图会直接滚动到指定位置,如果力度小,就恢复到原来位置,因此这里需要依据手势滑动的加速度来进行判断处理。

  • 而当你滑动到中间位置的时候,也需要依据最后滑动的位置来判断应该动画滚动到上方还是下方。

  • 最后滑动的时候上方的视图和滑动视图本身有背景颜色的渐变效果,这里需要依据滑动距离来判断。

二.代码分析:

首先由于滚动视图(demo里面是UITableView)需要支持手势滑动和内部滚动,因此需要写一个类FJBaseTableView继承自UITableView,然后在FJBaseTableView的实现里面重写如下方法:

// 当有 多个手势 都可以 响应
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {

return YES;
}

来支持响应多个手势。

  • 然后给滚动视图tableView添加滑动手势,当tableView从底部滑动到顶部指定位置时,应该限制tableView内部的视图滚动。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (self.tableView.frame.origin.y > _scrollViewStartPositionY) {
[scrollView setContentOffset:CGPointMake(0, 0)];
}
}

这里的_scrollViewStartPositionY是顶部指定位置。

  • 接着看下手势滑动的处理逻辑:

#pragma mark - 手势处理
- (void)handlePanGesture:(UIPanGestureRecognizer *)sender {

if (sender.state == UIGestureRecognizerStateBegan) {

_beganPoint = [sender locationInView:sender.view.superview];
_curPoint = sender.view.center;
_topTipContainerViewCurrentY = _topContainerView.frame.origin.y;
_previousOffsetY = self.tableView.contentOffset.y;

} else if(sender.state == UIGestureRecognizerStateChanged) {

CGPoint point = [sender locationInView:sender.view.superview];

CGFloat offsetY = _previousOffsetY - self.tableView.contentOffset.y;
NSInteger y_offset = point.y - _beganPoint.y - offsetY;

if (sender.view.frame.origin.y >= _scrollViewStartPositionY || (self.tableView.contentOffset.y == 0 && self.tableView.contentSize.height > self.tableView.frame.size.height)) {
sender.view.center = CGPointMake(_curPoint.x, _curPoint.y + y_offset);
[self updateViewControlsWithSlideOffset:y_offset];
}

if (sender.view.frame.origin.y > _scrollViewLimitMaxY) {
sender.view.y = _scrollViewLimitMaxY;
[self updateViewControlsWithSlideUp:NO];
}
else if(sender.view.frame.origin.y < _scrollViewStartPositionY) {

sender.view.y = _scrollViewStartPositionY;
[self updateViewControlsWithSlideUp:YES];
}
} else if(sender.state == UIGestureRecognizerStateEnded) {

if (sender.view.frame.origin.y <= _scrollViewStartPositionY || sender.view.frame.origin.y > _scrollViewLimitMaxY) {
if (sender.view.frame.origin.y <= _scrollViewStartPositionY) {
[self updateViewControlsWithSlideUp:YES];
}
if (sender.view.frame.origin.y > _scrollViewLimitMaxY) {
[self updateViewControlsWithSlideUp:NO];
}
return;
}
// 滑动速度处理
CGPoint velocity = [sender velocityInView:self.view];
CGFloat speed = 350;
if (velocity.y < - speed) {
// 快速向上
[self tableViewMoveToTop];
return;
} else if (velocity.y > speed) {
// 快速向下
[self tableViewMoveToBottom];
return;
}

// 滑动临界值
CGFloat criticalValue = _scrollViewLimitMaxY/2.0;
if (sender.view.frame.origin.y <= criticalValue) {
[self tableViewMoveToTop];
} else {
[self tableViewMoveToBottom];
}
}
}

这里几个点需要注意:

1. _beganPoint、_curPoint两个参数是用来计算手势滑动距离然后调整scrollView的滑动距离。而_previousOffsetY是用来记录滑动之前tableView的内部视图的偏移距离,因为当tableView滑动到顶部指定位置后,tableView开始滚动,这时候tableView向下滑动是先移动了tableView内部的滚动距离,然后才是滑动距离,因此需要将这部分值先记录,然后去除掉,才是tableView向下真正需要滑动的距离。

CGFloat offsetY = _previousOffsetY - self.tableView.contentOffset.y;
NSInteger y_offset = point.y - _beganPoint.y - offsetY;

2.滑动过程中,顶部视图的移动和渐变处理,这里先依据滑动的距离算出tableView滑动距离与tableView最大滑动距离的比值,然后再算出顶部视图需要移动的距离和背景的透明度。

- (void)updateViewControlsWhenSliding {
if (self.tableView.frame.origin.y > _scrollViewStartPositionY && self.tableView.frame.origin.y < _scrollViewLimitMaxY) {

CGFloat offsetLimitDistance = _scrollViewLimitMaxY - _scrollViewStartPositionY;
CGFloat offsetDistance = self.tableView.frame.origin.y - _scrollViewStartPositionY;
if (offsetDistance > 0 && offsetDistance < offsetLimitDistance) {
CGFloat topViewHeight = [FJFTopContainerView viewHeight];
CGFloat topViewHeightOffset = offsetDistance * (topViewHeight / offsetLimitDistance);
CGFloat viewAlpha = offsetDistance / offsetLimitDistance;
_topContainerView.y = topViewHeightOffset - topViewHeight;
_topContainerView.alpha = viewAlpha;
}
}
}

3.滑动速度处理,依据velocityInView函数获取速度值,然后依据当前速度值大小和正负和设定的速度值比较来判断是否需要向上或向下移动。

// 滑动速度处理
CGPoint velocity = [sender velocityInView:self.view];
CGFloat speed = 350;
if (velocity.y < - speed) {
// 快速向上
[self tableViewMoveToTop];
return;
} else if (velocity.y > speed) {
// 快速向下
[self tableViewMoveToBottom];
return;
}

4.滑动临界值处理,判断最后滑动位置与底部指定位置一半,两个值的大小来判断滑动的方向。

// 滑动临界值
CGFloat criticalValue = _scrollViewLimitMaxY/2.0;
if (sender.view.frame.origin.y <= criticalValue) {
[self tableViewMoveToTop];
} else {
[self tableViewMoveToBottom];
}

三.总结

这里最主要就是介绍了分析的思路,来找出可靠的实现方法,具体逻辑,详见demo

转自:https://www.jianshu.com/p/14dd820393fa

收起阅读 »

iOS-网络图片预览器(缩放,拖拽等手势)

预览效果(原位置启动,放大缩小,拖拽关闭,支持长图,跳转其他界面):视图结构:present跳转一个UINavigationController,UINavigationController的根跟控制是UIViewController,在viewcontrol...
继续阅读 »

预览效果(原位置启动,放大缩小,拖拽关闭,支持长图,跳转其他界面):





视图结构:present跳转一个UINavigationController,UINavigationController的根跟控制是UIViewController,在viewcontroller上添加预览器。

功能实现:
1.使用UICollectionView,在UICollectionViewCell上有个一UIScrollView的容器,在上面使用UIImageView展示图片,使用UIScrollView是为了方便实现长图预览,图片放大缩小的功能。

2.网络图片大小的处理:
第一种情况,一般来说在网络较好的情况下都会在列表页(如图列表)时已经加载完图片,在cell中直接拿到图片对象获得大小,根据显示区域的比例计算宽高。

第二种情况,在显示预览时,图片还没有加载完成(图片较大,网络较卡),我会给一个默认的宽高,在网络图片加载完成时,重新刷新他的宽高。(Data形式获取宽高有性能问题,放弃了)

3.拖拽动画和手势的处理:
在UIScrollView上添加了一个UIPanGestureRecognizer拖动手势,开启手势共享。然后根据触摸屏幕时水平方向和垂直方向的速度来判断是进行UICollectionView的左右滑动还是拖拽。在拖拽手势中,根据移动的Y轴距离的和整个区的的高度比例来缩放UIScrollView(使用transform来缩放)

总结:基本能满足一些普通要求的项目,如果想添加更多功能,可以在viewcontroller上添加,改变预览器的显示区域等。

Demo地址

作者:约德尔人郭敬明
链接:https://www.jianshu.com/p/7dda7add67e6
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

iOS 功能丰富的 Category 类型工具库

YYCategories安装CocoaPods在 Podfile 中添加  pod 'YYCategories'。执行 pod install 或 pod update。导入 <YYCategories/...
继续阅读 »

YYCategories

安装

CocoaPods

  1. 在 Podfile 中添加  pod 'YYCategories'
  2. 执行 pod install 或 pod update
  3. 导入 <YYCategories/YYCategories.h>。

Carthage

  1. 在 Cartfile 中添加 github "ibireme/YYCategories"
  2. 执行 carthage update --platform ios 并将生成的 framework 添加到你的工程。
  3. 导入 <YYCategories/YYCategories.h>。

手动安装

  1. 下载 YYCategories 文件夹内的所有内容。
  2. 将 YYCategories 内的源文件添加(拖放)到你的工程。
  3. 为 NSObject+YYAddForARC.m 和 NSThread+YYAdd.m 添加编译参数 -fno-objc-arc
  4. 链接以下 frameworks:
    • UIKit
    • CoreGraphics
    • QuartzCore
    • Accelerate
    • ImageIO
    • CoreText
    • CoreFoundation
    • libz
  5. 导入 YYCategories.h

注意

我希望调用 API 时,有着和调用系统自带 API 一样的体验,所以我并没有为 Category 方法添加前缀。我已经用工具扫描过这个项目中的 API,确保没有对系统 API 产生影响。我知道没有前缀的 Category 可能会带来麻烦(比如可能和其他某些类库产生冲突),所以如果你只需要其中少量代码,那最好将那段代码取出来,而不是导入整个库。


常见问题及demo下载:https://github.com/ibireme/YYCategories

源码下载:YYCategories.zip




收起阅读 »

iOS 异步绘制与显示的工具类

YYAsyncLayeriOS 异步绘制与显示的工具类。简单用法@interface YYLabel : UIView @property NSString *text; @property UIFont *font; @end @implementatio...
继续阅读 »

YYAsyncLayer

iOS 异步绘制与显示的工具类。

简单用法

@interface YYLabel : UIView
@property NSString *text;
@property UIFont *font;
@end

@implementation YYLabel

- (void)setText:(NSString *)text {
_text = text.copy;
[[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit];
}

- (void)setFont:(UIFont *)font {
_font = font;
[[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit];
}

- (void)layoutSubviews {
[super layoutSubviews];
[[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit];
}

- (void)contentsNeedUpdated {
// do update
[self.layer setNeedsDisplay];
}

#pragma mark - YYAsyncLayer

+ (Class)layerClass {
return YYAsyncLayer.class;
}

- (YYAsyncLayerDisplayTask *)newAsyncDisplayTask {

// capture current state to display task
NSString *text = _text;
UIFont *font = _font;

YYAsyncLayerDisplayTask *task = [YYAsyncLayerDisplayTask new];
task.willDisplay = ^(CALayer *layer) {
//...
};

task.display = ^(CGContextRef context, CGSize size, BOOL(^isCancelled)(void)) {
if (isCancelled()) return;
NSArray *lines = CreateCTLines(text, font, size.width);
if (isCancelled()) return;

for (int i = 0; i < lines.count; i++) {
CTLineRef line = line[i];
CGContextSetTextPosition(context, 0, i * font.pointSize * 1.5);
CTLineDraw(line, context);
if (isCancelled()) return;
}
};

task.didDisplay = ^(CALayer *layer, BOOL finished) {
if (finished) {
// finished
} else {
// cancelled
}
};

return task;
}
@end

安装

CocoaPods

  1. 在 Podfile 中添加 pod 'YYAsyncLayer'
  2. 执行 pod install 或 pod update
  3. 导入

Carthage

  1. 在 Cartfile 中添加 github "ibireme/YYAsyncLayer"
  2. 执行 carthage update --platform ios 并将生成的 framework 添加到你的工程。
  3. 导入

手动安装

  1. 下载 YYAsyncLayer 文件夹内的所有内容。
  2. 将 YYAsyncLayer 内的源文件添加(拖放)到你的工程。
  3. 导入 YYAsyncLayer.h


系统要求

该项目最低支持 iOS 6.0 和 Xcode 8.0






收起阅读 »

iOS 全局并发队列管理工具

YYDispatchQueuePooliOS 全局并发队列管理工具。当某个 block 所在线程被锁住时,concurrent queue 会创建大量线程以至于占用了过多资源而影响到主线程。这里可以用一个全局的 serial queue pool 来尽量控制全...
继续阅读 »

YYDispatchQueuePool

iOS 全局并发队列管理工具。

当某个 block 所在线程被锁住时,concurrent queue 会创建大量线程以至于占用了过多资源而影响到主线程。这里可以用一个全局的 serial queue pool 来尽量控制全局线程数。

用法

// 从全局的 queue pool 中获取一个 queue
dispatch_queue_t queue = YYDispatchQueueGetForQOS(NSQualityOfServiceUtility);

// 创建一个新的 serial queue pool
YYDispatchQueuePool *pool = [[YYDispatchQueuePool alloc] initWithName:@"file.read" queueCount:5 qos:NSQualityOfServiceBackground];
dispatch_queue_t queue = [pool queue];

安装

CocoaPods

  1. 在 Podfile 中添加 pod 'YYDispatchQueuePool'
  2. 执行 pod install 或 pod update
  3. 导入 <YYDispatchQueuePool/YYDispatchQueuePool.h>。

Carthage

  1. 在 Cartfile 中添加 github "ibireme/YYDispatchQueuePool"
  2. 执行 carthage update --platform ios 并将生成的 framework 添加到你的工程。
  3. 导入 <YYDispatchQueuePool/YYDispatchQueuePool.h>。

手动安装

  1. 下载 YYDispatchQueuePool 文件夹内的所有内容。
  2. 将 YYDispatchQueuePool 内的源文件添加(拖放)到你的工程。
  3. 导入 YYDispatchQueuePool.h

系统要求

该项目最低支持 iOS 6.0 和 Xcode 8.0


常见问题及demo下载:https://github.com/ibireme/YYDispatchQueuePool

源码下载:YYDispatchQueuePool.zip




收起阅读 »

iOS 键盘管理工具

YYKeyboardManageriOS 键盘监听管理工具类。'兼容性该项目能很好的兼容 iPhone / iPad / iPod,兼容 iOS 6~11, 并且能很好的处理屏幕旋转。用法// 获取键盘管理器 YYKeyboardManager *manag...
继续阅读 »

YYKeyboardManager

iOS 键盘监听管理工具类。'

兼容性

该项目能很好的兼容 iPhone / iPad / iPod,兼容 iOS 6~11, 并且能很好的处理屏幕旋转。

用法

// 获取键盘管理器
YYKeyboardManager *manager = [YYKeyboardManager defaultManager];

// 获取键盘的 view 和 window
UIView *view = manager.keyboardView;
UIWindow *window = manager.keyboardWindow;

// 获取键盘当前状态
BOOL visible = manager.keyboardVisible;
CGRect frame = manager.keyboardFrame;
frame = [manager convertRect:frame toView:self.view];

// 监听键盘动画
[manager addObserver:self];
- (void)keyboardChangedWithTransition:(YYKeyboardTransition)transition {
CGRect fromFrame = [manager convertRect:transition.fromFrame toView:self.view];
CGRect toFrame = [manager convertRect:transition.toFrame toView:self.view];
BOOL fromVisible = transition.fromVisible;
BOOL toVisible = transition.toVisible;
NSTimeInterval animationDuration = transition.animationDuration;
UIViewAnimationCurve curve = transition.animationCurve;
}

安装

CocoaPods

  1. 在 Podfile 中添加 pod 'YYKeyboardManager'
  2. 执行 pod install 或 pod update
  3. 导入 <YYKeyboardManager/YYKeyboardManager.h>。

Carthage

  1. 在 Cartfile 中添加 github "ibireme/YYKeyboardManager"
  2. 执行 carthage update --platform ios 并将生成的 framework 添加到你的工程。
  3. 导入 <YYKeyboardManager/YYKeyboardManager.h>。

手动安装

  1. 下载 YYKeyboardManager 文件夹内的所有内容。
  2. 将 YYKeyboardManager 内的源文件添加(拖放)到你的工程。
  3. 导入 YYKeyboardManager.h

系统要求

该项目最低支持 iOS 6.0 和 Xcode 8.0










收起阅读 »

iOS 一个比较完美的 Growing TextView

iOS 一个比较完美的 Growing TextView文章缘由现在都 2019 年了,App 中使用自动增高的输入框已经很常见了,即时通讯的 Chat 界面、社交类 App 的评论功能都可以看到自增高输入框。但写出一个自增高输入框容易,写好难。现在市面上一些...
继续阅读 »

iOS 一个比较完美的 Growing TextView

文章缘由
现在都 2019 年了,App 中使用自动增高的输入框已经很常见了,即时通讯的 Chat 界面、社交类 App 的评论功能都可以看到自增高输入框。但写出一个自增高输入框容易,写好难。现在市面上一些主流 App 的输入框依然会有一些瑕疵,例如:文字挡住一部分、粘贴大量文字时出现偏移,具体问题下面详细分析。
现在 iOS 开发已经过了搭建 UI 和普通业务功能的初级阶段,App 要想赢得用户的青睐,除了 App 的功能、UI 设计,交互体验的细节处理至关重要。一般用户只要使用输入框能正常输入即可,90% 的用户不会察觉输入框的一些细节,但作为开发人员应该知道这些细节(bug)并做出处理。

主流 App 的输入框之痛

粘贴文本出现文字偏移
这个问题严格来说算 bug,毕竟粘贴还是一个很常见的操作。


挡住部分文字
一个输入框要么显示 N 行文字、要么显示 N + 1行,如果显示 N + 0.5 行就有点不可思议了。这个问题对 App 功能没有任何影响,但这么多 App 竟然都有这个问题而且非常普遍,是我始料未及的。测试了多个 App 后,只有QQ的输入框做的最好,粘贴、遮挡文字等问题根本不存在。






比较完美的输入框
写出一个自增高的输入框还是比较容易的,大致过程就是给 textView 添加左、右、下/上、高度四个约束,然后监听文字变化的通知,进而修改输入框的高度。如果想写好,就要花时间打磨了。我接下来主要说一下大家可能遇到的一些细节问题。

1、粘贴文本,文字偏移

我的做法是继承 UITextView 重写 setBounds 方法,重新调整contentOffset

- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
// NSLog(@”bounds:%@”, NSStringFromCGRect(bounds));
if (self.contentSize.height <= self.bounds.size.height + 1){
self.contentOffset = CGPointZero; // Fix wrong contentOfset
} else if (!self.tracking) {
CGPoint offset = self.contentOffset;
if (offset.y > self.contentSize.height - bounds.size.height) {
offset.y = self.contentSize.height - bounds.size.height;
if (!self.decelerating && !self.tracking && !self.dragging) {
self.contentOffset = offset;
}
// Fix wrong contentOfset when paster huge text
}
}
}

2、文字离输入框顶部间隙时大时小
正常情况下滚动输入框的文字,文字可以滚动到控件顶部。而 QQ 的输入框,不管怎么滑动文字,文字和输入框顶部都有一段固定间隔。


先了解输入框的一个属性textContainerInset,这个值默认是 (8, 0, 8, 0),就是说默认情况下文字和输入框顶部有 8pt 的偏移,所以当文字输入较多的时候文字向上滚动,那么文字和控件顶部间隙会减小到 0。
实现QQ输入框的效果,我能想到的方案是把textContainerInset全设置为 0(或者top/bottom为0),这样文字就紧挨着输入框,文字和输入框顶部的固定间距就是 0 了。接着要给UITextView添加一个 containerView ,containerView 竖向比 UITextView 高出一部分,从而实现 顶部/底部 的固定间距。

3、挡住部分文字
这个问题是因为 单行文字高度 最大行数 != 输入框的最大高度,输入框的最大高度可不是随便设置的,先确定输入框的font和最大行数,font.lineHeight 行数就是输入框的最大高度。这样就不会出现文字挡住一部分的问题了

GrowTextView

接下来就是我自己写的自增高输入框了,目前没发现什么问题,至少没有上面涉及的问题。




收起阅读 »

iOS 超强富文本编辑库

YYText功能强大的 iOS 富文本编辑与显示框架特性API 兼容 UILabel 和 UITextView支持高性能的异步排版和渲染扩展了 CoreText 的属性以支持更多文字效果支持 UIImage、UIView、CALayer 作为图文混排元素支持添...
继续阅读 »

YYText

功能强大的 iOS 富文本编辑与显示框架

特性

  • API 兼容 UILabel 和 UITextView
  • 支持高性能的异步排版和渲染
  • 扩展了 CoreText 的属性以支持更多文字效果
  • 支持 UIImage、UIView、CALayer 作为图文混排元素
  • 支持添加自定义样式的、可点击的文本高亮范围
  • 支持自定义文本解析 (内置简单的 Markdown/表情解析)
  • 支持文本容器路径、内部留空路径的控制
  • 支持文字竖排版,可用于编辑和显示中日韩文本
  • 支持图片和富文本的复制粘贴
  • 文本编辑时,支持富文本占位符
  • 支持自定义键盘视图
  • 撤销和重做次数的控制
  • 富文本的序列化与反序列化支持
  • 支持多语言,支持 VoiceOver
  • 支持 Interface Builder
  • 全部代码都有文档注释
  • 架构

    YYText 和 TextKit 架构对比



    文本属性

    YYText 原生支持的属性

    DemoAttribute NameClass
    TextAttachmentYYTextAttachment
    TextHighlightYYTextHighlight
    TextBindingYYTextBinding
    TextShadow
    TextInnerShadow
    YYTextShadow
    TextBorderYYTextBorder
    TextBackgroundBorderYYTextBorder
    TextBlockBorderYYTextBorder
    TextGlyphTransformNSValue(CGAffineTransform)
    TextUnderlineYYTextDecoration
    TextStrickthroughYYTextDecoration
    TextBackedStringYYTextBackedString

    YYText 支持的 CoreText 属性

    DemoAttribute NameClass
    Font UIFont(CTFontRef)
    Kern NSNumber
    StrokeWidth NSNumber 
    StrokeColor CGColorRef 
    Shadow NSShadow 
    Ligature NSNumber 
    VerticalGlyphForm NSNumber(BOOL) 
    WritingDirection NSArray(NSNumber) 
    RunDelegate CTRunDelegateRef 
    TextAlignment NSParagraphStyle 
    (NSTextAlignment) 
    LineBreakMode NSParagraphStyle 
    (NSLineBreakMode) 
    LineSpacing NSParagraphStyle 
    (CGFloat) 
    ParagraphSpacing 
    ParagraphSpacingBefore 
    NSParagraphStyle 
    (CGFloat) 
    FirstLineHeadIndent NSParagraphStyle 
    (CGFloat) 
    HeadIndent NSParagraphStyle 
    (CGFloat) 
    TailIndent NSParagraphStyle 
    (CGFloat) 
    MinimumLineHeight NSParagraphStyle 
    (CGFloat) 
    MaximumLineHeight NSParagraphStyle 
    (CGFloat) 
    LineHeightMultiple NSParagraphStyle 
    (CGFloat) 
    BaseWritingDirection NSParagraphStyle 
    (NSWritingDirection) 
    DefaultTabInterval 
    TabStops 
    NSParagraphStyle 
    CGFloat/NSArray(NSTextTab)



    用法

    基本用法

    // YYLabel (和 UILabel 用法一致)
    YYLabel *label = [YYLabel new];
    label.frame = ...
    label.font = ...
    label.textColor = ...
    label.textAlignment = ...
    label.lineBreakMode = ...
    label.numberOfLines = ...
    label.text = ...

    // YYTextView (和 UITextView 用法一致)
    YYTextView *textView = [YYTextView new];
    textView.frame = ...
    textView.font = ...
    textView.textColor = ...
    textView.dataDetectorTypes = ...
    textView.placeHolderText = ...
    textView.placeHolderTextColor = ...
    textView.delegate = ...



    属性文本

    // 1. 创建一个属性文本
    NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Some Text, blabla..."];

    // 2. 为文本设置属性
    text.yy_font = [UIFont boldSystemFontOfSize:30];
    text.yy_color = [UIColor blueColor];
    [text yy_setColor:[UIColor redColor] range:NSMakeRange(0, 4)];
    text.yy_lineSpacing = 10;

    // 3. 赋值到 YYLabel 或 YYTextView
    YYLabel *label = [YYLabel new];
    label.frame = ...
    label.attributedString = text;

    YYTextView *textView = [YYTextView new];
    textView.frame = ...
    textView.attributedString = text;



    文本高亮

    你可以用一些已经封装好的简便方法来设置文本高亮:

    [text yy_setTextHighlightRange:range
    color:[UIColor blueColor]
    backgroundColor:[UIColor grayColor]
    tapAction:^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect){
    NSLog(@"tap text range:...");
    }];

    文本高亮

    你可以用一些已经封装好的简便方法来设置文本高亮:

    [text yy_setTextHighlightRange:range
    color:[UIColor blueColor]
    backgroundColor:[UIColor grayColor]
    tapAction:^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect){
    NSLog(@"tap text range:...");
    }];

    或者用更复杂的办法来调节文本高亮的细节:

    // 1. 创建一个"高亮"属性,当用户点击了高亮区域的文本时,"高亮"属性会替换掉原本的属性
    YYTextBorder *border = [YYTextBorder borderWithFillColor:[UIColor grayColor] cornerRadius:3];

    YYTextHighlight *highlight = [YYTextHighlight new];
    [highlight setColor:[UIColor whiteColor]];
    [highlight setBackgroundBorder:highlightBorder];
    highlight.tapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
    NSLog(@"tap text range:...");
    // 你也可以把事件回调放到 YYLabel 和 YYTextView 来处理。
    };

    // 2. 把"高亮"属性设置到某个文本范围
    [attributedText yy_setTextHighlight:highlight range:highlightRange];

    // 3. 把属性文本设置到 YYLabel 或 YYTextView
    YYLabel *label = ...
    label.attributedText = attributedText

    YYTextView *textView = ...
    textView.attributedText = ...

    // 4. 接受事件回调
    label.highlightTapAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
    NSLog(@"tap text range:...");
    };
    label.highlightLongPressAction = ^(UIView *containerView, NSAttributedString *text, NSRange range, CGRect rect) {
    NSLog(@"long press text range:...");
    };

    @UITextViewDelegate
    - (void)textView:(YYTextView *)textView didTapHighlight:(YYTextHighlight *)highlight inRange:(NSRange)characterRange rect:(CGRect)rect {
    NSLog(@"tap text range:...");
    }
    - (void)textView:(YYTextView *)textView didLongPressHighlight:(YYTextHighlight *)highlight inRange:(NSRange)characterRange rect:(CGRect)rect {
    NSLog(@"long press text range:...");
    }

    图文混排

    NSMutableAttributedString *text = [NSMutableAttributedString new];
    UIFont *font = [UIFont systemFontOfSize:16];
    NSMutableAttributedString *attachment = nil;

    // 嵌入 UIImage
    UIImage *image = [UIImage imageNamed:@"dribbble64_imageio"];
    attachment = [NSMutableAttributedString yy_attachmentStringWithContent:image contentMode:UIViewContentModeCenter attachmentSize:image.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
    [text appendAttributedString: attachment];

    // 嵌入 UIView
    UISwitch *switcher = [UISwitch new];
    [switcher sizeToFit];
    attachment = [NSMutableAttributedString yy_attachmentStringWithContent:switcher contentMode:UIViewContentModeBottom attachmentSize:switcher.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
    [text appendAttributedString: attachment];

    // 嵌入 CALayer
    CASharpLayer *layer = [CASharpLayer layer];
    layer.path = ...
    attachment = [NSMutableAttributedString yy_attachmentStringWithContent:layer contentMode:UIViewContentModeBottom attachmentSize:switcher.size alignToFont:font alignment:YYTextVerticalAlignmentCenter];
    [text appendAttributedString: attachment];


    文本布局计算

    NSAttributedString *text = ...
    CGSize size = CGSizeMake(100, CGFLOAT_MAX);
    YYTextLayout *layout = [YYTextLayout layoutWithContainerSize:size text:text];

    // 获取文本显示位置和大小
    layout.textBoundingRect; // get bounding rect
    layout.textBoundingSize; // get bounding size

    // 查询文本排版结果
    [layout lineIndexForPoint:CGPointMake(10,10)];
    [layout closestLineIndexForPoint:CGPointMake(10,10)];
    [layout closestPositionToPoint:CGPointMake(10,10)];
    [layout textRangeAtPoint:CGPointMake(10,10)];
    [layout rectForRange:[YYTextRange rangeWithRange:NSMakeRange(10,2)]];
    [layout selectionRectsForRange:[YYTextRange rangeWithRange:NSMakeRange(10,2)]];

    // 显示文本排版结果
    YYLabel *label = [YYLabel new];
    label.size = layout.textBoundingSize;
    label.textLayout = layout;

    部分效果展示






    安装

    CocoaPods

    1. 在 Podfile 中添加 pod 'YYText'
    2. 执行 pod install 或 pod update
    3. 导入

    Carthage

    1. 在 Cartfile 中添加 github "ibireme/YYText"
    2. 执行 carthage update --platform ios 并将生成的 framework 添加到你的工程。
    3. 导入

    手动安装

    1. 下载 YYText 文件夹内的所有内容。
    2. 将 YYText 内的源文件添加(拖放)到你的工程。
    3. 链接以下 frameworks:
      • UIKit
      • CoreFoundation
      • CoreText
      • QuartzCore
      • Accelerate
      • MobileCoreServices
    4. 导入 YYText.h


    已知问题

  • YYText 并不能支持所有 CoreText/TextKit 的属性,比如 NSBackgroundColor、NSStrikethrough、NSUnderline、NSAttachment、NSLink 等,但 YYText 中基本都有对应属性作为替代。详情见上方表格。
  • YYTextView 未实现局部刷新,所以在输入和编辑大量的文本(比如超过大概五千个汉字、或大概一万个英文字符)时会出现较明显的卡顿现象。
  • 竖排版时,添加 exclusionPaths 在少数情况下可能会导致文本显示空白。
  • 当添加了非矩形的 textContainerPath,并且有嵌入大于文本排版方向宽度的 RunDelegate 时,RunDelegate 之后的文字会无法显示。这是 CoreText 的 Bug(或者说是 Feature)。

  • 常见问题及源码下载:点击这里

    demo:YYText.zip


    收起阅读 »

    iOS 数据缓存库

    YYCache高性能 iOS 缓存框架。特性LRU: 缓存支持 LRU (least-recently-used) 淘汰算法。缓存控制: 支持多种缓存控制方法:总数量、总大小、存活时间、空闲空间。兼容性: API 基本和 NSCache 保...
    继续阅读 »

    YYCache

    高性能 iOS 缓存框架。

    特性

  • LRU: 缓存支持 LRU (least-recently-used) 淘汰算法。
  • 缓存控制: 支持多种缓存控制方法:总数量、总大小、存活时间、空闲空间。
  • 兼容性: API 基本和 NSCache 保持一致, 所有方法都是线程安全的。
  • 内存缓存
    • 对象释放控制: 对象的释放(release) 可以配置为同步或异步进行,可以配置在主线程或后台线程进行。
    • 自动清空: 当收到内存警告或 App 进入后台时,缓存可以配置为自动清空。
  • 磁盘缓存
    • 可定制性: 磁盘缓存支持自定义的归档解档方法,以支持那些没有实现 NSCoding 协议的对象。
    • 存储类型控制: 磁盘缓存支持对每个对象的存储类型 (SQLite/文件) 进行自动或手动控制,以获得更高的存取性能。

  • 安装

    CocoaPods

    1. 在 Podfile 中添加 pod 'YYCache'
    2. 执行 pod install 或 pod update
    3. 导入

    Carthage

    1. 在 Cartfile 中添加 github "ibireme/YYCache"
    2. 执行 carthage update --platform ios 并将生成的 framework 添加到你的工程。
    3. 导入

    手动安装

    1. 下载 YYCache 文件夹内的所有内容。
    2. 将 YYCache 内的源文件添加(拖放)到你的工程。
    3. 链接以下的 frameworks:
      • UIKit
      • CoreFoundation
      • QuartzCore
      • sqlite3
    4. 导入 YYCache.h


    常见问题与源码下载:点击这里

    代码示例:YYCache.zip






    收起阅读 »

    iOS 强大的图片处理库(webP.编解码.gif)等

    YYImage支持以下类型动画图像的播放/编码/解码:    WebP, APNG, GIF。支持以下类型静态图像的显示/编码/解码:    WebP, PNG, GIF, JPE...
    继续阅读 »

    YYImage

  • 支持以下类型动画图像的播放/编码/解码:
        WebP, APNG, GIF。
  • 支持以下类型静态图像的显示/编码/解码:
        WebP, PNG, GIF, JPEG, JP2, TIFF, BMP, ICO, ICNS。
  • 支持以下类型图片的渐进式/逐行扫描/隔行扫描解码:
        PNG, GIF, JPEG, BMP。
  • 支持多张图片构成的帧动画播放,支持单张图片的 sprite sheet 动画。
  • 高效的动态内存缓存管理,以保证高性能低内存的动画播放。
  • 完全兼容 UIImage 和 UIImageView,使用方便。
  • 保留可扩展的接口,以支持自定义动画。
  • 每个类和方法都有完善的文档注释。

  • 安装

    CocoaPods

    1. 将 cocoapods 更新至最新版本.
    2. 在 Podfile 中添加 pod 'YYImage'
    3. 执行 pod install 或 pod update
    4. 导入
    5. 注意:pod 配置并没有包含 WebP 组件, 如果你需要支持 WebP,可以在 Podfile 中添加 pod 'YYImage/WebP'

    Carthage

    1. 在 Cartfile 中添加 github "ibireme/YYImage"
    2. 执行 carthage update --platform ios 并将生成的 framework 添加到你的工程。
    3. 导入
    4. 注意:carthage framework 并没有包含 WebP 组件。如果你需要支持 WebP,可以用 CocoaPods 安装,或者手动安装。

    手动安装

    1. 下载 YYImage 文件夹内的所有内容。
    2. 将 YYImage 内的源文件添加(拖放)到你的工程。
    3. 链接以下 frameworks:
      • UIKit
      • CoreFoundation
      • QuartzCore
      • AssetsLibrary
      • ImageIO
      • Accelerate
      • MobileCoreServices
      • libz
    4. 导入 YYImage.h
    5. 注意:如果你需要支持 WebP,可以将 Vendor/WebP.framework(静态库) 加入你的工程。

    用法

    显示动画类型的图片

    // 文件: ani@3x.gif
    UIImage *image = [YYImage imageNamed:@"ani.gif"];
    UIImageView *imageView = [[YYAnimatedImageView alloc] initWithImage:image];
    [self.view addSubview:imageView];

    播放帧动画

    // 文件: frame1.png, frame2.png, frame3.png
    NSArray *paths = @[@"/ani/frame1.png", @"/ani/frame2.png", @"/ani/frame3.png"];
    NSArray *times = @[@0.1, @0.2, @0.1];
    UIImage *image = [YYFrameImage alloc] initWithImagePaths:paths frameDurations:times repeats:YES];
    UIImageView *imageView = [YYAnimatedImageView alloc] initWithImage:image];
    [self.view addSubview:imageView];

    动画播放控制

    YYAnimatedImageView *imageView = ...;
    // 暂停:
    [imageView stopAnimating];
    // 播放:
    [imageView startAnimating];
    // 设置播放进度:
    imageView.currentAnimatedImageIndex = 12;
    // 获取播放状态:
    image.currentIsPlayingAnimation;
    //上面两个属性都支持 KVO。

    图片解码

    // 解码单帧图片:
    NSData *data = [NSData dataWithContentsOfFile:@"/tmp/image.webp"];
    YYImageDecoder *decoder = [YYImageDecoder decoderWithData:data scale:2.0];
    UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;

    // 渐进式图片解码 (可用于图片下载显示):
    NSMutableData *data = [NSMutableData new];
    YYImageDecoder *decoder = [[YYImageDecoder alloc] initWithScale:2.0];
    while(newDataArrived) {
    [data appendData:newData];
    [decoder updateData:data final:NO];
    if (decoder.frameCount > 0) {
    UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
    // progressive display...
    }
    }
    [decoder updateData:data final:YES];
    UIImage image = [decoder frameAtIndex:0 decodeForDisplay:YES].image;
    // final display...


    图片编码

    // 编码静态图 (支持各种常见图片格式):
    YYImageEncoder *jpegEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeJPEG];
    jpegEncoder.quality = 0.9;
    [jpegEncoder addImage:image duration:0];
    NSData jpegData = [jpegEncoder encode];

    // 编码动态图 (支持 GIF/APNG/WebP):
    YYImageEncoder *webpEncoder = [[YYImageEncoder alloc] initWithType:YYImageTypeWebP];
    webpEncoder.loopCount = 5;
    [webpEncoder addImage:image0 duration:0.1];
    [webpEncoder addImage:image1 duration:0.15];
    [webpEncoder addImage:image2 duration:0.2];
    NSData webpData = [webpEncoder encode];

    图片类型探测

    // 获取图片类型
    YYImageType type = YYImageDetectType(data);
    if (type == YYImageTypePNG) ...

    常见问题及源码:点击这里

    demo下载:YYImage.zip


    收起阅读 »

    iOS JSON转换库

    YYModel特性高性能: 模型转换性能接近手写解析代码。自动类型转换: 对象类型可以自动转换,详情见下方表格。类型安全: 转换过程中,所有的数据类型都会被检测一遍,以保证类型安全,避免崩溃问题。无侵入性: 模型无需继承自其他基类。轻量: 该框架只有 5 个文...
    继续阅读 »

    YYModel

    特性

    • 高性能: 模型转换性能接近手写解析代码。
    • 自动类型转换: 对象类型可以自动转换,详情见下方表格。
    • 类型安全: 转换过程中,所有的数据类型都会被检测一遍,以保证类型安全,避免崩溃问题。
    • 无侵入性: 模型无需继承自其他基类。
    • 轻量: 该框架只有 5 个文件 (包括.h文件)。
    • 文档和单元测试: 文档覆盖率100%, 代码覆盖率99.6%


    使用方法

    简单的 Model 与 JSON 相互转换

    // JSON:
    {
    "uid":123456,
    "name":"Harry",
    "created":"1965-07-31T00:00:00+0000"
    }

    // Model:
    @interface User : NSObject
    @property UInt64 uid;
    @property NSString *name;
    @property NSDate *created;
    @end
    @implementation User
    @end


    // 将 JSON (NSData,NSString,NSDictionary) 转换为 Model:
    User *user = [User yy_modelWithJSON:json];

    // 将 Model 转换为 JSON 对象:
    NSDictionary *json = [user yy_modelToJSONObject];

    当 JSON/Dictionary 中的对象类型与 Model 属性不一致时,YYModel 将会进行如下自动转换。自动转换不支持的值将会被忽略,以避免各种潜在的崩溃问题。


    JSON/DictionaryModel
    NSStringNSNumber,NSURL,SEL,Class
    NSNumberNSString
    NSString/NSNumber基础类型 (BOOL,int,float,NSUInteger,UInt64,...)
    NaN 和 Inf 会被忽略
    NSStringNSDate 以下列格式解析:
    yyyy-MM-dd
    yyyy-MM-dd HH:mm:ss
    yyyy-MM-dd'T'HH:mm:ss
    yyyy-MM-dd'T'HH:mm:ssZ
    EEE MMM dd HH:mm:ss Z yyyy
    NSDateNSString 格式化为 ISO8601:
    "YYYY-MM-dd'T'HH:mm:ssZ"
    NSValuestruct (CGRect,CGSize,...)
    NSNullnil,0
    "no","false",...@(NO),0
    "yes","true",...@(YES),1



    Model 属性名和 JSON 中的 Key 不相同

    // JSON:
    {
    "n":"Harry Pottery",
    "p": 256,
    "ext" : {
    "desc" : "A book written by J.K.Rowing."
    },
    "ID" : 100010
    }

    // Model:
    @interface Book : NSObject
    @property NSString *name;
    @property NSInteger page;
    @property NSString *desc;
    @property NSString *bookID;
    @end
    @implementation Book
    //返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。
    + (NSDictionary *)modelCustomPropertyMapper {
    return @{@"name" : @"n",
    @"page" : @"p",
    @"desc" : @"ext.desc",
    @"bookID" : @[@"id",@"ID",@"book_id"]};
    }
    @end


    你可以把一个或一组 json key (key path) 映射到一个或多个属性。如果一个属性没有映射关系,那默认会使用相同属性名作为映射。

    在 json->model 的过程中:如果一个属性对应了多个 json key,那么转换过程会按顺序查找,并使用第一个不为空的值。

    在 model->json 的过程中:如果一个属性对应了多个 json key (key path),那么转换过程仅会处理第一个 json key (key path);如果多个属性对应了同一个 json key,则转换过过程会使用其中任意一个不为空的值。

    Model 包含其他 Model

    // JSON
    {
    "author":{
    "name":"J.K.Rowling",
    "birthday":"1965-07-31T00:00:00+0000"
    },
    "name":"Harry Potter",
    "pages":256
    }

    // Model: 什么都不用做,转换会自动完成
    @interface Author : NSObject
    @property NSString *name;
    @property NSDate *birthday;
    @end
    @implementation Author
    @end

    @interface Book : NSObject
    @property NSString *name;
    @property NSUInteger pages;
    @property Author *author; //Book 包含 Author 属性
    @end
    @implementation Book
    @end


    安装

    CocoaPods

    1. 在 Podfile 中添加 pod 'YYModel'
    2. 执行 pod install 或 pod update
    3. 导入

    Carthage

    1. 在 Cartfile 中添加 github "ibireme/YYModel"
    2. 执行 carthage update --platform ios 并将生成的 framework 添加到你的工程。
    3. 导入

    手动安装

    1. 下载 YYModel 文件夹内的所有内容。
    2. 将 YYModel 内的源文件添加(拖放)到你的工程。
    3. 导入 YYModel.h


    常见问题及demo:点击这里

    源码下载:YYModel.zip



    收起阅读 »

    iOS 超好用的图表库

    AAChartKit前言AAChartKit 项目,是AAInfographics的 Objective-C 语言版本,是在流行的开源前端图表库Highcharts的基础上,封装的面向对象的,一组简单易用,极其精美的图表绘制控件....
    继续阅读 »

    AAChartKit

    前言

    AAChartKit 项目,是AAInfographics的 Objective-C 语言版本,是在流行的开源前端图表库Highcharts的基础上,封装的面向对象的,一组简单易用,极其精美的图表绘制控件.可能是这个星球上 UI 最精致的第三方 iOS 开源图表库了(✟我以无神论者的名义向上帝起誓🖐,我真的没有在说鬼话✟)

    功能特性

    🎂  环境友好,兼容性强. 适配 iOS 8+, 支持iOS iPad OSmacOS, 支持 Objective-C语言, 同时更有 Swift 语言版本 AAInfographics  Java 语言版本 AAChartCore Kotlin 语言版本 AAChartCore-Kotlin 可供使用, 配置导入工程简单易操作. 支持的所有语言版本及连接,参见此列表.

    🚀  功能强大,类型多样 -. 支持柱状图 条形图 折线图 曲线图 折线填充图 曲线填充图雷达图极地图扇形图气泡图散点图区域范围图柱形范围图面积范围图面积范围均线图直方折线图直方折线填充图箱线图瀑布图热力图桑基图金字塔图漏斗图、等二十几种类型的图形,不可谓之不多.

    📝  现代化声明式语法. 与过往的命令式编程技巧不同, 在 AAChartKit 中绘制任意一款自定义图表, 你完全无需关心挠人的内在实现细节. 描述你所要得到的, 你便得到你所描述的.

    🔬  细致入微的用户自定义功能. 基础的主标题副标题X 轴Y 轴自不必谈, 从纵横的交互准星线、跟手的浮动提示框, 到切割数值的值域分割线值域分割颜色带, 再到细小的线条类型,标记点样式, 各种细微的图形子组件, 应有尽有. 以至于不论是极简、抽象的小清新风格, 还是纷繁复杂的严肃商业派头, 均可完美驾驭.

    🎮  交互式图形动画 . 有着清晰和充满细节的用户交互方式, 与此同时, 图形渲染动画效果细腻精致, 流畅优美. 有三十多种以上渲染动画效果可供选择, 用户可自由设置渲染图形时的动画时间和动画类型, 关于图形渲染动画类型,具体参见 AAChartKit 动画类型.

    🦋  极简主义 . AAChartView + AAChartModel = Chart,在 AAChartKit 图表框架当中,遵循这样一个极简主义公式:图表视图控件 + 图表模型 = 你想要的图表. 同另一款强大而又精美的图表库AAInfographics完全一致.

      链式编程语法 . 支持类 Masonry 链式编程语法, 一行代码即可配置完成 AAChartModel模型对象实例.

    🎈  简洁清晰,轻便易用 . 最少仅仅需要 五行代码 即可完成整个图表的绘制工作(使用链式编程语法配置 AAChartModel 实例对象时, 无论你写多少行代码, 理论上只能算作是一行). 🤪🤪🤪

    🖱  交互事件回调 支持图表的用户点击事件及单指滑动事件, 可在此基础上实现双表联动乃至多表联动,以及其他更多更复杂的自定义用户交互效果.

    👌  支持手势缩放 . 支持各个方向的图表手势缩放和拖动阅览, 手势缩放类型具体参见 AAChartKit 手势缩放类型, 默认禁用手势缩放功能

    效果图


    CocoaPods 安装 (推荐)

    1. 在 Podfile 中添加以下内容
    pod 'AAChartKit', :git => 'https://github.com/AAChartModel/AAChartKit.git'
    1. 执行  pod install 或  pod update

    手动安装
    1. 将项目Demo中的文件夹AAChartKitLib拖入到所需项目中.
    2. 在你的项目的 .pch 全局宏定义文件中添加
    #import "AAGlobalMacro.h"

    使用

    1. 在你的ViewController视图控制器文件中添加
    #import "AAChartKit.h"
    1. 创建视图AAChartView
    CGFloat chartViewWidth  = self.view.frame.size.width;
    CGFloat chartViewHeight = self.view.frame.size.height - 250;
    _aaChartView = [[AAChartView alloc]init];
    _aaChartView.frame = CGRectMake(0, 60, chartViewWidth, chartViewHeight);
    ////禁用 AAChartView 滚动效果(默认不禁用)
    //self.aaChartView.scrollEnabled = NO;
    [self.view addSubview:_aaChartView];
    1. 配置视图模型AAChartModel
    AAChartModel *aaChartModel= AAObject(AAChartModel)
    .chartTypeSet(AAChartTypeArea)//设置图表的类型(这里以设置的为折线面积图为例)
    .titleSet(@"编程语言热度")//设置图表标题
    .subtitleSet(
    @"虚拟数据")//设置图表副标题
    .categoriesSet(@[@"Java",@"Swift",@"Python",@"Ruby", @"PHP",@"Go",@"C",@"C#",@"C++"])//图表横轴的内容
    .yAxisTitleSet(
    @"摄氏度")//设置图表 y 轴的单位
    .seriesSet(@[
    AAObject(AASeriesElement)
    .nameSet(@"2017")
    .
    dataSet(@[@7.0, @6.9, @9.5, @14.5, @18.2, @21.5, @25.2, @26.5, @23.3, @18.3, @13.9, @9.6]),
    AAObject(AASeriesElement)
    .
    nameSet(@"2018")
    .dataSet(@[@0.2, @0.8, @5.7, @11.3, @17.0, @22.0, @24.8, @24.1, @20.1, @14.1, @8.6, @2.5]),
    AAObject(AASeriesElement)
    .nameSet(@"2019")
    .
    dataSet(@[@0.9, @0.6, @3.5, @8.4, @13.5, @17.0, @18.6, @17.9, @14.3, @9.0, @3.9, @1.0]),
    AAObject(AASeriesElement)
    .
    nameSet(@"2020")
    .dataSet(@[@3.9, @4.2, @5.7, @8.5, @11.9, @15.2, @17.0, @16.6, @14.2, @10.3, @6.6, @4.8]),
    ])
    ;
    1. 绘制图形(创建 AAChartView 实例对象后,首次绘制图形调用此方法)
    /*图表视图对象调用图表模型对象,绘制最终图形*/
    [_aaChartView aa_drawChartWithChartModel:aaChartModel];



    当前已支持的图表类型有十种以上,说明如下

    typedef NSString *AAChartType;

    AACHARTKIT_EXTERN AAChartType const AAChartTypeColumn; //柱形图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeBar; //条形图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeArea; //折线区域填充图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeAreaspline; //曲线区域填充图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeLine; //折线图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeSpline; //曲线图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeScatter; //散点图
    AACHARTKIT_EXTERN AAChartType const AAChartTypePie; //扇形图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeBubble; //气泡图
    AACHARTKIT_EXTERN AAChartType const AAChartTypePyramid; //金字塔图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeFunnel; //漏斗图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeColumnrange; //柱形范围图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeArearange; //区域折线范围图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeAreasplinerange; //区域曲线范围图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeBoxplot; //箱线图
    AACHARTKIT_EXTERN AAChartType const AAChartTypeWaterfall; //瀑布图
    AACHARTKIT_EXTERN AAChartType const AAChartTypePolygon; //多边形图

    当前已支持的图表手势缩放类型共有三种,说明如下

    typedef NSString *AAChartZoomType;

    AACHARTKIT_EXTERN AAChartZoomType const AAChartZoomTypeNone; //禁用手势缩放功能(默认禁用手势缩放)
    AACHARTKIT_EXTERN AAChartZoomType const AAChartZoomTypeX; //支持图表 X轴横向缩放
    AACHARTKIT_EXTERN AAChartZoomType const AAChartZoomTypeY; //支持图表 Y轴纵向缩放
    AACHARTKIT_EXTERN AAChartZoomType const AAChartZoomTypeXY; //支持图表等比例缩放

    AAChartModel 属性配置列表

    AAPropStatementAndPropSetFuncStatement(copy,   AAChartModel, NSString *, title) //标题文本内容
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, titleFontSize) //标题字体尺寸大小
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, titleFontColor) //标题字体颜色
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, titleFontWeight) //标题字体粗细

    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, subtitle) //副标题文本内容
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, subtitleFontSize) //副标题字体尺寸大小
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, subtitleFontColor) //副标题字体颜色
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, subtitleFontWeight) //副标题字体粗细

    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, backgroundColor) //图表背景色(必须为十六进制的颜色色值如红色"#FF0000")
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSArray <NSString *>*, colorsTheme) //图表主题颜色数组
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSArray <NSString *>*, categories) //x轴坐标每个点对应的名称(注意:这个不是用来设置 X 轴的值,仅仅是用于设置 X 轴文字内容的而已)
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSArray *, series) //图表的数据列内容

    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, AAChartSubtitleAlignType, subtitleAlign) //图表副标题文本水平对齐方式。可选的值有 “left”,”center“和“right”。 默认是:center.
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, AAChartType, chartType) //图表类型
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, AAChartStackingType, stacking) //堆积样式
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, AAChartSymbolType, markerSymbol) //折线曲线连接点的类型:"circle", "square", "diamond", "triangle","triangle-down",默认是"circle"
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, AAChartSymbolStyleType, markerSymbolStyle)
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, AAChartZoomType, zoomType) //缩放类型 AAChartZoomTypeX 表示可沿着 x 轴进行手势缩放
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, AAChartAnimation, animationType) //设置图表的渲染动画类型
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, animationDuration) //设置图表的渲染动画时长(动画单位为毫秒)

    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, inverted) //x 轴是否垂直,默认为否
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, gradientColorsThemeEnabled) //是否将常规主题颜色数组 colorsTheme 自动转换为半透明渐变效果的颜色数组(设置后就不用自己再手动去写渐变色字典,相当于是设置渐变色的一个快捷方式,当然了,如果需要细致地自定义渐变色效果,还是需要自己手动配置渐变颜色字典内容,具体方法参见图表示例中的`颜色渐变条形图`示例代码),默认为否
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, polar) //是否极化图形(变为雷达图),默认为否

    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, dataLabelEnabled) //是否显示数据,默认为否
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, dataLabelFontColor) //Datalabel font color
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, dataLabelFontSize) //Datalabel font size
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, dataLabelFontWeight) //Datalabel font weight


    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, xAxisVisible) //x 轴是否可见(默认可见)
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, xAxisReversed) // x 轴翻转,默认为否

    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, xAxisLabelsEnabled) //x 轴是否显示文字
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, xAxisLabelsFontSize) //x 轴文字字体大小
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, xAxisLabelsFontColor) //x 轴文字字体颜色
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, AAChartFontWeightType, xAxisLabelsFontWeight) //x 轴文字字体粗细

    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, xAxisGridLineWidth) //x 轴网格线的宽度
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, xAxisTickInterval) //x轴刻度点间隔数(设置每隔几个点显示一个 X轴的内容)

    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, xAxisCrosshairWidth) //设置 x 轴准星线的宽度
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, xAxisCrosshairColor) //设置 x 轴准星线的颜色
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, AALineDashSyleType, xAxisCrosshairDashStyleType) //设置 x 轴准星线的线条样式类型


    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, yAxisVisible) //y 轴是否可见(默认可见)
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, yAxisReversed) //y 轴翻转,默认为否

    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, yAxisLabelsEnabled) //y 轴是否显示文字
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, yAxisLabelsFontSize) //y 轴文字字体大小
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, yAxisLabelsFontColor) //y 轴文字字体颜色
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, AAChartFontWeightType , yAxisLabelsFontWeight) //y 轴文字字体粗细

    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, yAxisTitle) //y 轴标题
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, yAxisLineWidth) //y y-axis line width
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, yAxisGridLineWidth) //y轴网格线的宽度
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, yAxisAllowDecimals) //是否允许 y 轴显示小数
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSArray *, yAxisPlotLines) //y 轴基线的配置
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, yAxisMax) //y 轴最大值
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, yAxisMin) //y 轴最小值(设置为0就不会有负数)
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, yAxisTickInterval)
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSArray *, yAxisTickPositions) //自定义 y 轴坐标(如:[@(0), @(25), @(50), @(75) , (100)])

    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, yAxisCrosshairWidth) //设置 y 轴准星线的宽度
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, yAxisCrosshairColor) //设置 y 轴准星线的颜色
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, AALineDashSyleType, yAxisCrosshairDashStyleType) //设置 y 轴准星线的线条样式类型


    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, tooltipEnabled) //是否显示浮动提示框(默认显示)
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, tooltipShared)//是否多组数据共享一个浮动提示框
    AAPropStatementAndPropSetFuncStatement(copy, AAChartModel, NSString *, tooltipValueSuffix) //浮动提示框单位后缀

    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, connectNulls) //设置折线是否断点重连(是否连接空值点)
    AAPropStatementAndPropSetFuncStatement(assign, AAChartModel, BOOL, legendEnabled) //是否显示图例 lengend(图表底部可点按的圆点和文字)
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, borderRadius) //柱状图长条图头部圆角半径(可用于设置头部的形状,仅对条形图,柱状图有效)
    AAPropStatementAndPropSetFuncStatement(strong, AAChartModel, NSNumber *, markerRadius) //折线连接点的半径长度

    源码下载:AAChartKit-demo.zip

    常见问题及详细说明:点击这里



    收起阅读 »

    iOS 相册选择器推荐

    HXPhotoPicker效果预览特性 - Features 查看、选择GIF图片 照片、视频可同时多选/原图 3DTouch预览照片 长按拖动改变顺序 自定义相机拍照、录制视频 自定义转场动画&nb...
    继续阅读 »

    HXPhotoPicker

    效果预览



    特性 - Features

    •  查看、选择GIF图片
    •  照片、视频可同时多选/原图
    •  3DTouch预览照片
    •  长按拖动改变顺序
    •  自定义相机拍照、录制视频
    •  自定义转场动画
    •  查看、选择LivePhoto iOS9.1以上才有用
    •  浏览网络图片、网络视频
    •  仿微信编辑图片功能
    •  自定义裁剪视频时长
    •  传入本地图片、视频
    •  在线下载iCloud上的资源
    •  两种相册展现方式(列表、弹窗)
    •  支持Cell上添加
    •  支持草稿功能
    •  同一界面多个不同选择器
    •  支持暗黑模式
    •  支持横向布局
    •  支持Xib和Masonry布局
    •  支持自定义item的大小
    •  支持滑动手势选择

    安装 - Installation

    CocoaPods
    # 将以下内容添加到您的Podfile中:
    # 不使用网络图片功能
    pod 'HXPhotoPicker', '~> 3.1.9'

    # 使用SDWebImage加载网络图片
    pod 'HXPhotoPicker/SDWebImage', '~> 3.1.9'

    # 使用YYWebImage加载网络图片
    pod 'HXPhotoPicker/YYWebImage', '~> 3.1.9'

    # 搜索不到库或最新版时请执行
    pod repo update rm ~/Library/Caches/CocoaPods/search_index.json
    Carthage
    # 将以下内容添加到您的Cartfile中:
    github "SilenceLove/HXPhotoPicker"
    手动导入
    手动导入:将项目中的“HXPhotoPicker”文件夹拖入项目中
    使用前导入头文件 "HXPhotoPicker.h"

    要求 - Requirements

    • iOS8及以上系统可使用. ARC环境. - iOS 8 or later. Requires ARC
    • 访问相册和相机需要配置四个info.plist文件
    • Privacy - Photo Library Usage Description 和 Privacy - Camera Usage Description 以及 Privacy - Microphone Usage Description
    • Privacy - Location When In Use Usage Description 使用相机拍照时会获取位置信息
    • 相机拍照功能请使用真机调试

    应用示例 - Examples

    跳转相册选择照片

    // 懒加载 照片管理类
    - (HXPhotoManager *)manager {
    if (!_manager) {
    _manager = [[HXPhotoManager alloc] initWithType:HXPhotoManagerSelectedTypePhotoAndVideo];
    }
    return _manager;
    }

    // 方法一:
    HXWeakSelf
    [self hx_presentSelectPhotoControllerWithManager:self.manager didDone:^(NSArray *allList, NSArray *photoList, NSArray *videoList, BOOL isOriginal, UIViewController *viewController, HXPhotoManager *manager) {
    weakSelf.total.text = [NSString stringWithFormat:@"总数量:%ld ( 照片:%ld 视频:%ld )",allList.count, photoList.count, videoList.count];
    weakSelf.original.text = isOriginal ? @"YES" : @"NO";
    NSSLog(@"block - all - %@",allList);
    NSSLog(@"block - photo - %@",photoList);
    NSSLog(@"block - video - %@",videoList);
    } cancel:^(UIViewController *viewController, HXPhotoManager *manager) {
    NSSLog(@"block - 取消了");
    }];

    // 方法二:
    // 照片选择控制器
    HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithManager:self.manager delegate:self];
    [self presentViewController:nav animated:YES completion:nil];

    // 通过 HXCustomNavigationControllerDelegate 代理返回选择的图片以及视频
    /**
    点击完成按钮

    @param photoNavigationViewController self
    @param allList 已选的所有列表(包含照片、视频)
    @param photoList 已选的照片列表
    @param videoList 已选的视频列表
    @param original 是否原图
    */
    - (void)photoNavigationViewController:(HXCustomNavigationController *)photoNavigationViewController didDoneAllList:(NSArray *)allList photos:(NSArray *)photoList videos:(NSArray *)videoList original:(BOOL)original;

    /**
    点击取消

    @param photoNavigationViewController self
    */
    - (void)photoNavigationViewControllerDidCancel:(HXCustomNavigationController *)photoNavigationViewController;

    单独使用HXPhotoPreviewViewController预览图片

    HXCustomAssetModel *assetModel1 = [HXCustomAssetModel assetWithLocaImageName:@"1" selected:YES];
    // selected 为NO 的会过滤掉
    HXCustomAssetModel *assetModel2 = [HXCustomAssetModel assetWithLocaImageName:@"2" selected:NO];
    HXCustomAssetModel *assetModel3 = [HXCustomAssetModel assetWithNetworkImageURL:[NSURL URLWithString:@"http://tsnrhapp.oss-cn-hangzhou.aliyuncs.com/1466408576222.jpg"] selected:YES];
    // selected 为NO 的会过滤掉
    HXCustomAssetModel *assetModel4 = [HXCustomAssetModel assetWithNetworkImageURL:[NSURL URLWithString:@"http://tsnrhapp.oss-cn-hangzhou.aliyuncs.com/0034821a-6815-4d64-b0f2-09103d62630d.jpg"] selected:NO];
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"QQ空间视频_20180301091047" withExtension:@"mp4"];
    HXCustomAssetModel *assetModel5 = [HXCustomAssetModel assetWithLocalVideoURL:url selected:YES];

    HXPhotoManager *photoManager = [HXPhotoManager managerWithType:HXPhotoManagerSelectedTypePhotoAndVideo];
    photoManager.configuration.saveSystemAblum = YES;
    photoManager.configuration.photoMaxNum = 0;
    photoManager.configuration.videoMaxNum = 0;
    photoManager.configuration.maxNum = 10;
    photoManager.configuration.selectTogether = YES;
    photoManager.configuration.photoCanEdit = NO;
    photoManager.configuration.videoCanEdit = NO;

    HXWeakSelf
    // 长按事件
    photoManager.configuration.previewRespondsToLongPress = ^(UILongPressGestureRecognizer *longPress,
    HXPhotoModel *photoModel,
    HXPhotoManager *manager,
    HXPhotoPreviewViewController *previewViewController) {
    hx_showAlert(previewViewController, @"提示", @"长按事件", @"确定", nil, nil, nil);
    };
    // 跳转预览界面时动画起始的view
    photoManager.configuration.customPreviewFromView = ^UIView *(NSInteger currentIndex) {
    HXPhotoSubViewCell *viewCell = [weakSelf.photoView collectionViewCellWithIndex:currentIndex];
    return viewCell;
    };
    // 跳转预览界面时展现动画的image
    photoManager.configuration.customPreviewFromImage = ^UIImage *(NSInteger currentIndex) {
    HXPhotoSubViewCell *viewCell = [weakSelf.photoView collectionViewCellWithIndex:currentIndex];
    return viewCell.imageView.image;
    };
    // 退出预览界面时终点view
    photoManager.configuration.customPreviewToView = ^UIView *(NSInteger currentIndex) {
    HXPhotoSubViewCell *viewCell = [weakSelf.photoView collectionViewCellWithIndex:currentIndex];
    return viewCell;
    };
    [photoManager addCustomAssetModel:@[assetModel1, assetModel2, assetModel3, assetModel4, assetModel5]];

    [self hx_presentPreviewPhotoControllerWithManager:photoManager
    previewStyle:HXPhotoViewPreViewShowStyleDark
    currentIndex:0
    photoView:nil];


    UIViewController+HXExtension.h
    /// 跳转预览照片界面
    /// @param manager 照片管理者
    /// @param previewStyle 预览样式
    /// @param currentIndex 当前预览的下标
    /// @param photoView 照片展示视图 - 没有就不传
    - (void)hx_presentPreviewPhotoControllerWithManager:(HXPhotoManager *)manager
    previewStyle:(HXPhotoViewPreViewShowStyle)previewStyle
    currentIndex:(NSUInteger)currentIndex
    photoView:(HXPhotoView * _Nullable)photoView;


    使用如何保存草稿

    通过 HXPhotoManager 对象进行存储
    /// 获取保存在本地文件的模型数组
    - (NSArray *)getLocalModelsInFile;

    /// 将模型数组保存到本地文件
    - (BOOL)saveLocalModelsToFile;

    /// 将保存在本地文件的模型数组删除
    - (BOOL)deleteLocalModelsInFile;

    /// 将本地获取的模型数组添加到manager的数据中
    /// @param models 在本地获取的模型数组
    - (void)addLocalModels:(NSArray *)models;

    /// 将本地获取的模型数组添加到manager的数据中
    - (void)addLocalModels;


    demo:HXPhotoPicker.zip

    常见问题及源码地址:点击这里


    收起阅读 »

    iOS 网络图片加载库

    SDWebImage  一款超级好用的网络图片加载库集成方式pod 'SDWebImage', '~> 5.0'使用方式#import [imageView sd_setImageWithURL:[NSURL URLWithString:@"图片地址...
    继续阅读 »

    SDWebImage  一款超级好用的网络图片加载库

    集成方式

    pod 'SDWebImage', '~> 5.0'

    使用方式

    #import <SDWebImage/SDWebImage.h>
    [imageView sd_setImageWithURL:[NSURL URLWithString:@"图片地址"]
    placeholderImage:[UIImage imageNamed:@"占位图名字"]];

        加载gif

    SDAnimatedImageView *imageView = [SDAnimatedImageView new];
    SDAnimatedImage *animatedImage = [SDAnimatedImage imageNamed:@"image.gif"];
    imageView.image = animatedImage;

       使用Blocks,采用这个方案可以在网络图片加载过程中得知图片的下载进度和图片加载成功与否

    [imageView sd_setImageWithURL:[NSURL URLWithString:@"图片地址"] placeholderImage:[UIImage imageNamed:@"占位图"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { //... completion code here ... }];

       取图片的缓存大小
    [SDImageCache sharedImageCache] getSize];
       清理内存,磁盘缓存
    [[SDImageCache sharedImageCache] clearMemory];


    常见问题及demo地址:点击这里


    收起阅读 »

    iOS 提示框

    推荐一个好用的iOS 提示框库MBProgressHUD集成方式pod 'MBProgressHUD', '~> 1.2.0'或者直接将附件拖入项目内导入#import "MBProgressHUD.h" 效果图:在这里顺便分享一下使用小技巧我们可以将hud定...
    继续阅读 »

    推荐一个好用的iOS 提示框库

    MBProgressHUD

    集成方式

    pod 'MBProgressHUD', '~> 1.2.0'

    或者直接将附件拖入项目内

    导入#import "MBProgressHUD.h"


    效果图:





    在这里顺便分享一下使用小技巧

    我们可以将hud定义成宏 .

    #pragma mark - hud 提示

    /**

     默认请求开始的hud

     */

    #define HudShow MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];\

    hud.label.text = NSLocalizedString(@"请求中...", @"HUD loading title");


    /**

     自定义title的请求开始hud

     */

    #define HudShowStr(str) MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];\

    hud.label.text = NSLocalizedString(str, @"HUD loading title");


    /**

     移除hud

     */

    #define HudHidden         [hud hideAnimated:YES];


    /**

     自定义title 的提示hud

     */ 

    #define HudMessageStr(str) hud.mode = MBProgressHUDModeText;\

    hud.label.text = NSLocalizedString(str, @"HUD message title"); [hud hideAnimated:YES afterDelay:1];\


    /**

     提示的hud .title来自json

     */

    #define HudMessage hud.mode = MBProgressHUDModeText;\

    hud.label.text = NSLocalizedString(@"根据需求传值例如取服务器json的某个值", @"HUD message title"); [hud hideAnimated:YES afterDelay:1];\



    /**

     请求错误时的hud

     */

    #define HudError   hud.mode = MBProgressHUDModeText;\

    hud.label.text = NSLocalizedString(@"网络差", @"HUD message title"); \

    [hud hideAnimated:YES afterDelay:1.f];




    /**

     alert效果的hud

     */

    #define alertHudShow(str) MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];\

    hud.mode = MBProgressHUDModeText;\

    hud.label.text = NSLocalizedString(str, @"HUD message title"); [hud hideAnimated:YES afterDelay:1];\





    /**

     请求时错误才提示的hud

     */

    #define RequestErrorHud MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];\

    hud.mode = MBProgressHUDModeText;\

    hud.label.text = NSLocalizedString(@"网络差", @"HUD message title"); [hud hideAnimated:YES afterDelay:1];\


    源码下载: Hud.zip

    常见问题及demo:点击这里

    收起阅读 »

    iOS 微博主页、简书主页、QQ联系人页面等效果

    类似微博主页、简书主页、QQ联系人页面等效果。多页面嵌套,既可以上下滑动,也可以左右滑动切换页面。支持HeaderView悬浮、支持下拉刷新、上拉加载更多。功能特点支持OC与Swift;支持列表懒加载,等到列表真正显示的时候才加载,而不是一次性加载所有列表;支...
    继续阅读 »

    类似微博主页、简书主页、QQ联系人页面等效果。多页面嵌套,既可以上下滑动,也可以左右滑动切换页面。支持HeaderView悬浮、支持下拉刷新、上拉加载更多。

    功能特点

    • 支持OC与Swift;
    • 支持列表懒加载,等到列表真正显示的时候才加载,而不是一次性加载所有列表;
    • 支持首页下拉刷新、列表视图下拉刷新、列表视图上拉加载更多;
    • 支持悬浮SectionHeader的垂直位置调整;
    • 支持从顶部用力往上滚动,下面的列表会跟着滚动,而不会突然卡主,需要使用JXPagerSmoothView类;
    • 列表封装简洁,只要遵从JXPagingViewListViewDelegate协议即可。UIView、UIViewController等都可以;
    • 使用JXCategoryView/JXSegmentedView分类控制器,几乎支持所有主流效果、高度自定义、可灵活扩展;
    • 支持横竖屏切换;
    • 支持点击状态栏滚动当前列表到顶部;
    • 支持列表显示和消失的生命周期方法;
    • isListHorizontalScrollEnabled属性控制列表是否可以左右滑动,默认YES;
    • 支持FDFullscreenPopGesture等全屏手势兼容处理;
    效果图




    安装

    手动

    Swift版本: Clone代码,拖入JXPagingView-Swift文件夹,使用JXPagingView类;

    OC版本: Clone代码,拖入JXPagerView文件夹,使用JXPagerView类;

    CocoaPods

    • Swift版本

    支持swift版本:5.0+

    target '' do
    pod 'JXPagingView/Paging'
    end
    • OC版本
    target '' do
    pod 'JXPagingView/Pager'
    end

    Swift与OC的仓库地址不一样,请注意选择!

    pod repo update然后再pod install


    使用

    swift版本使用类似,只是类名及相关API更改为JXPagingView

    1、初始化JXCategoryTitleViewJXPagerView

    self.categoryView = [[JXCategoryTitleView alloc] initWithFrame:frame];
    //配置categoryView,细节参考源码

    self.pagerView = [[JXPagerView alloc] initWithDelegate:self];
    [self.view addSubview:self.pagerView];

    //⚠️⚠️⚠️将pagerView的listContainerView和categoryView.listContainer进行关联,这样列表就可以和categoryView联动了。⚠️⚠️⚠️
    self.categoryView.listContainer = (id)self.pagerView.listContainerView;

    Swift版本列表关联代码

    //给JXPagingListContainerView添加extension,表示遵从JXSegmentedViewListContainer的协议
    extension JXPagingListContainerView: JXSegmentedViewListContainer {}
    //⚠️⚠️⚠️将pagingView的listContainerView和segmentedView.listContainer进行关联,这样列表就可以和categoryView联动了。⚠️⚠️⚠️
    segmentedView.listContainer = pagingView.listContainerView

    2、实现JXPagerViewDelegate协议

    /**
    返回tableHeaderView的高度,因为内部需要比对判断,只能是整型数
    */
    - (NSUInteger)tableHeaderViewHeightInPagerView:(JXPagerView *)pagerView {
    return JXTableHeaderViewHeight;
    }

    /**
    返回tableHeaderView
    */
    - (UIView *)tableHeaderViewInPagerView:(JXPagerView *)pagerView {
    return self.userHeaderView;
    }


    /**
    返回悬浮HeaderView的高度,因为内部需要比对判断,只能是整型数
    */
    - (NSUInteger)heightForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
    return JXheightForHeaderInSection;
    }


    /**
    返回悬浮HeaderView
    */
    - (UIView *)viewForPinSectionHeaderInPagerView:(JXPagerView *)pagerView {
    return self.categoryView;
    }

    /**
    返回列表的数量
    */
    - (NSInteger)numberOfListsInPagerView:(JXPagerView *)pagerView {
    //和categoryView的item数量一致
    return self.titles.count;
    }

    /**
    根据index初始化一个对应列表实例。注意:一定要是新生成的实例!!!
    只要遵循JXPagerViewListViewDelegate即可,无论你返回的是UIView还是UIViewController都可以。
    */
    - (id)pagerView:(JXPagerView *)pagerView initListAtIndex:(NSInteger)index {
    TestListBaseView *listView = [[TestListBaseView alloc] init];
    if (index == 0) {
    listView.dataSource = @[@"橡胶火箭", @"橡胶火箭炮", @"橡胶机关枪"...].mutableCopy;
    }else if (index == 1) {
    listView.dataSource = @[@"吃烤肉", @"吃鸡腿肉", @"吃牛肉", @"各种肉"].mutableCopy;
    }else {
    listView.dataSource = @[@"【剑士】罗罗诺亚·索隆", @"【航海士】娜美", @"【狙击手】乌索普"...].mutableCopy;
    }
    [listView beginFirstRefresh];
    return listView;
    }

    3、实现JXPagerViewListViewDelegate协议

    列表可以是任意类,UIView、UIViewController等等都可以,只要实现了JXPagerViewListViewDelegate协议就行。

    ⚠️⚠️⚠️一定要保证scrollCallback的正确回调,许多朋友都容易疏忽这一点,导致异常,务必重点注意!

    下面的使用代码参考的是TestListBaseView

    /**
    返回listView。如果是vc包裹的就是vc.view;如果是自定义view包裹的,就是自定义view自己。
    */
    - (UIView *)listView {
    return self;
    }

    /**
    返回listView内部持有的UIScrollView或UITableView或UICollectionView
    主要用于mainTableView已经显示了header,listView的contentOffset需要重置时,内部需要访问到外部传入进来的listView内的scrollView
    */
    - (UIScrollView *)listScrollView {
    return self.tableView;
    }


    /**
    当listView内部持有的UIScrollView或UITableView或UICollectionView的代理方法`scrollViewDidScroll`回调时,需要调用该代理方法传入的callback
    */
    - (void)listViewDidScrollCallback:(void (^)(UIScrollView *))callback {
    self.scrollCallback = callback;
    }

    4、列表回调处理

    TestListBaseView在其tableView的滚动回调中,通过调用上面持有的scrollCallback,把列表的滚动事件回调给JXPagerView内部。

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    !self.scrollCallback ?: self.scrollCallback(scrollView);
    }

    常见问题及demo:点击这里



    收起阅读 »

    iOS 下拉刷新控件

    推荐一个iOS的下拉刷新控件 .支持tableview 和collection!先介绍一下基本使用姿势另一种默认下拉刷新和上拉加载更多(通过action)self.tableView.mj_header = [MJRefreshNormalHeader hea...
    继续阅读 »

    推荐一个iOS的下拉刷新控件 .支持tableview 和collection!

    先介绍一下基本使用姿势

    默认下拉刷新和上拉加载更多(通过block)

    self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
    //网络请求加载数据完成后在停止刷新
    [self.tableView.mj_header endRefreshing];
    }];
    //这种上拉刷新footer在tableview的底部
    self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
    //网络请求加载数据完成后在停止刷新
    [self.tableView.mj_footer endRefreshing];
    }];
    //另一种上拉刷新,这个上拉刷新footer在tableview最后一条数据的底部
    self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
    //网络请求加载数据完成后在停止刷新
    [self.tableView.mj_footer endRefreshing];
    }];
    //马上进入刷新状态
    [self.tableView.mj_header beginRefreshing];

    另一种默认下拉刷新和上拉加载更多(通过action)

    self.tableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(refreshAction)];
    //这种上拉刷新footer在tableview的底部
    self.tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreAction)];
    //另一种上拉刷新,这个上拉刷新footer在tableview最后一条数据的底部
    self.tableView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingTarget:self refreshingAction:@selector(loadMoreAction)];
    这里的前提是得给mj_header、mj_footer赋值。
    self.tableView.mj_header.hidden = YES;//隐藏下拉
    self.tableView.mj_header.hidden = NO;//显示下拉
    self.tableView.mj_footer.hidden = YES;//隐藏上拉
    self.tableView.mj_footer.hidden = NO;//显示上拉

    二、动画图片的下拉刷新和上拉加载

    MJRefreshGifHeader *header = [MJRefreshGifHeader headerWithRefreshingBlock:^{
    NSLog(@"aa");
    }];
    // 设置普通状态的动画图片
    NSMutableArray *idleImages = [NSMutableArray array];
    for (NSUInteger i = 1; i<=60; i++) {
    UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"dropdown_anim__000%zd", I]];
    [idleImages addObject:image];
    }
    [header setImages:idleImages forState:MJRefreshStateIdle];

    // 设置即将刷新状态的动画图片(一松开就会刷新的状态)
    NSMutableArray *refreshingImages = [NSMutableArray array];
    for (NSUInteger i = 1; i<=3; i++) {
    UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"dropdown_loading_0%zd", I]];
    [refreshingImages addObject:image];
    }
    [header setImages:refreshingImages forState:MJRefreshStatePulling];

    // 设置正在刷新状态的动画图片
    [header setImages:refreshingImages forState:MJRefreshStateRefreshing];
    self.tableView.mj_header = header;

    常见与demo下载:点击这里

    源码下载:MJRefresh-3.5.1.zip


    收起阅读 »

    iOS 推荐一个项目框架给大家

    最近经常在iOS交流群看到大家有问"有没有什么直接可以用的项目框架啊?""有没有直接创建好结构的框架啊?""有没有什么好用的开源框架啊?"今天就给大家推荐一款Coding-iOS使用方式:项目里用到了 CocoaPods 和 Carthage,如果没有安装的话...
    继续阅读 »

    最近经常在iOS交流群看到大家有问

    "有没有什么直接可以用的项目框架啊?"

    "有没有直接创建好结构的框架啊?"

    "有没有什么好用的开源框架啊?"

    今天就给大家推荐一款

    Coding-iOS

    使用方式:

    项目里用到了 CocoaPods 和 Carthage,如果没有安装的话,需要先自行安装。

    Clone 代码后,初次执行前,需要双击运行根目录下的bootstrap脚本。这个过程涉及到下载依赖,可能会有点久,需耐心等待。

    Tip:由于用到了 submodule,所以必需要把 git 仓库 clone 到本地

    下面介绍一下文件的大概目录先:


    .
    ├── Coding_iOS
    │   ├── Models:数据类
    │   ├── Views:视图类
    │   │   ├── CCell:所有的 CollectionViewCell 都在这里
    │   │   ├── Cell:所有的 TableViewCell 都在这里
    │   │   └── XXX:ListView(项目、动态、任务、讨论、文档、代码)和 InputView(用于聊天和评论的输入框)
    │   ├── Controllers:控制器,对应app中的各个页面
    │   │   ├── Login:登录页面
    │   │   ├── RootControllers:登录后的根页面
    │   │   ├── MeSetting:设置信息页面
    │   │   └── XXX:其它页面
    │   ├── Images:app 中用到的所有的图片都在这里
    │   ├── Resources:资源文件
    │   ├── Util:一些常用控件和 Category、Manager 之类
    │   │   ├── Common
    │   │   ├── Manager
    │   │   ├── OC_Category
    │   │   └── ObjcRuntime
    │   └── Vendor:用到的一些第三方类库,一般都有改动
    │      ├── AFNetworking
    │      ├── AGEmojiKeyboard
    │      ├── ASProgressPopUpView
    │      ├── ActionSheetPicker
    │      ├── FontAwesome+iOS
    │      ├── MJPhotoBrowser
    │      ├── MLEmojiLabel
    │      ├── NSDate+Helper
    │      ├── NSStringEmojize
    │      ├── PPiAwesomeButton
    │      ├── QBImagePickerController
    │      ├── RDVTabBarController
    │      ├── SMPageControl
    │      ├── SVPullToRefresh
    │      ├── SWTableViewCell
    │      ├── UMENG
    │      ├── UMessage_Sdk_1.1.0
    │      ├── XGPush
    │      ├── XTSegmentControl
    │    └── iCarousel
    └── Pods:项目使用了 [CocoaPods](http://code4app.com/article/cocoapods-install-usage) 这个类库管理工具


    再说下项目的启动流程:

    在 AppDelegate 的启动方法中,先设置了一下 Appearance 的样式,然后根据用户的登录状态选择是去加载登录页面 LoginViewController,还是登录后的 RootTabViewController 页面。

    RootTabViewController 继承自第三方库 RDVTabBarController。在 RootTabViewController 里面依次加载了 Project_RootViewController、MyTask_RootViewController、Tweet_RootViewController、Message_RootViewController、Me_RootViewController 五个 RootViewController,后续的页面跳转都是基于这几个 RootViewController 引过去的。

    项目里面还有些需要注意的点

    • Coding_NetAPIManager:基本上 app 的所有请求接口都放在了这里。网络请求使用的是 AFNetworking 库,与服务器之间的数据交互格式用的都是 json(与 Coding 使用的 api 一致)。

    • 关于推送:刚开始是用的 友盟推送,后来又改用了 腾讯信鸽,因为要兼顾旧版本 app 的推送,所以服务器是同时保留了两套推送。但是为了确保新版本的 app 不同时收到双份相同的推送消息,所以当前代码里还存留了友盟的 sdk,用于解除推送 token 与友盟 Alias 的绑定。

    • 关于 ProjectViewController:这个就是进入到某个项目之后的页面,这里包含了项目的动态、任务、讨论、文档、代码、成员各类信息,而且每类信息里面还可能会有新的分类(如‘任务’里面还分有各个成员的任务);这个页面相当的臃肿,我对它们做了拆分,都放在视图类 Views 目录下面。 首先是把数据列表独立成了对应的 XXXListView(如 ProjectTaskListView);然后如果需要标签切换的话,会再新建一个 XXXsView(如:ProjectTasksView),在这个视图中,上面会放一个切换栏 XTSegmentControl 显示各个标签,下面放一个 iCarousel 可以滑动显示各个标签的内容;最后这些视图都会存储在 ProjectViewController 的 projectContentDict 变量里面,根据顶部导航栏选择的类别,去显示或隐藏对应的视图。

    • 关于 UIMessageInputView:这个是私信聊天的输入框。因为这个输入框好多地方都有用到(冒泡、任务、讨论的评论还有私信),所以这个输入框就写成了一个相对独立的控件,并且直接显示在了 keyWindow 里面而不是某个视图里。这里的表情键盘用的是 AGEmojiKeyboard 改写了一下。

    • 关于 Emoji:这个,Coding 站点的 emoji 都是用的图片,而且服务器是不接受大部分 emoji 字符的,所以刚开始的时候 app 一直不能处理 emoji 表情;又因为没有 emoji 图片名和 emoji code 码的对应关系表,所以拖了很久都没能做好转换。直到在 github 上面找到了 NSStringEmojize 这个项目;试了一下,虽然也不能全部解析,但是大部分表情都能正确显示了,不能更感谢。

    • 关于如何正确显示冒泡的内容:api 返回的数据里面,冒泡内容都是 html 格式,需要做一下预处理;其实私信、讨论里面的内容也是 html。解析 html 的类名是 HtmlMediaItem,它是先用 hpple 对 html 进行了解析,然后把对应的 media 元素和对应的位置做一个存储,显示的时候便可以根据需要来显示了。

    最后说下 CocoaPods 里面用到的第三方类库


    如有其他使用疑问请点击 常见使用问题及源码





    收起阅读 »

    iOS视频播放器

    非常棒的视频播放器 目前支持的功能如下:普通模式的播放,类似于腾讯视频、爱奇艺等APP;列表普通模式的播放,包括手动点击播放、滑动到屏幕中间自动播放,wifi网络智能播放等等;列表的亮暗模式播放,类似于微博、UC浏览器视频列表等APP;列表视频滑出屏...
    继续阅读 »

    非常棒的视频播放器 

    目前支持的功能如下:

    • 普通模式的播放,类似于腾讯视频、爱奇艺等APP;
    • 列表普通模式的播放,包括手动点击播放、滑动到屏幕中间自动播放,wifi网络智能播放等等;
    • 列表的亮暗模式播放,类似于微博、UC浏览器视频列表等APP;
    • 列表视频滑出屏幕后停止播放、滑出屏幕后小窗播放;
    • 优雅的全屏,支持横屏和竖屏全屏模式



    支持多种效果播放, 并且支持ijk 

    使用方式:

    因为作者将库更新的相似于组件化 ,所以尽量直接pod使用 .如不需要ijk相关功能, 直接pod如下三个库

    pod 'ZFPlayer', '~> 4.0'
    pod 'ZFPlayer/ControlView', '~> 4.0'
    pod 'ZFPlayer/AVPlayer', '~> 4.0'

    如果需要使用ijk 那么在添加

    pod 'ZFPlayer/ijkplayer', '~> 4.0'


    默认播放器使用方式代码

    #import

    #import

    #import


    @property (nonatomic, strong) ZFPlayerController *player;

    @property (nonatomic, strong) ZFPlayerControlView *controlView;



    ZFAVPlayerManager *playerManager = [[ZFAVPlayerManager alloc] init];

        playerManager.shouldAutoPlay = YES;


        self.player = [ZFPlayerController playerWithPlayerManager:playerManager containerView:self.containerView];

        self.controlView.portraitControlView.fullScreenBtn.hidden = YES;

        self.player.controlView = self.controlView;

        

        /// 设置退到后台继续播放

        self.player.pauseWhenAppResignActive = NO;

        @zf_weakify(self)

        /// 播放完成

        self.player.playerDidToEnd = ^(id  _Nonnull asset) {

            @zf_strongify(self)

        // your code


        };

        // 设置播放地址(支持沙盒路径和网络路径)

        self.player.assetURL = self.playUrl;

        [self.controlView showTitle:@"标题" coverURLString:@"videoRrlString" fullScreenMode:ZFFullScreenModeAutomatic];



    - (ZFPlayerControlView *)controlView {

        if (!_controlView) {

            _controlView = [ZFPlayerControlView new];

            _controlView.fastViewAnimated = YES;

            _controlView.autoHiddenTimeInterval = 5;

            _controlView.autoFadeTimeInterval = 0.5;

            _controlView.prepareShowLoading = YES;

            _controlView.prepareShowControlView = NO;

        }

        return _controlView;

    }




    这样就可以实现一个类似于腾讯视频/爱奇艺的播放器啦



    具体中文说明可阅读 : ZFPlayer中文深度说明

    具体英文说明及Demo下载:常见问题与Demo下载





    收起阅读 »

    iOS网络请求库

    pod 集成pod 'AFNetworking', '~> 4.0'或者下载附件直接添加到项目当中AFNetworking.zip 4.0AFN3.x.zip 3.x作为一个iOS开发者耳熟能详的网络请求库&n...
    继续阅读 »

    pod 集成


    pod 'AFNetworking''~> 4.0'



    或者下载附件直接添加到项目当中

    AFNetworking.zip 4.0

    AFN3.x.zip 3.x

    作为一个iOS开发者耳熟能详的网络请求库 .具体用法不在赘述这里简单介绍一下使用中的技巧吧(因为目前普遍使用的是3.x的版本,所以技巧主要适用于3.x )

    正常用法:

    [[AFHTTPSessionManager managerPOST:@"port"parameters:nilprogress:^(NSProgress * _Nonnull uploadProgress) {

                

            } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

                

            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

                

            }];


    可其实AFNmanager方法只是伪单例我们完全可以基于AFN进行一次二次封装做成真单例

    :ZyNetWorking.h

    **

     *  请求成功所走方法

     *

     *  @param responseObject 请求返还的数据

     */

    typedef void (^ZuYResponseSuccess)(NSURLSessionDataTask * task,id responseObject,NSInteger code);



    /**

     *  请求错误所走方法

     *

     *  @param error 请求错误返还的信息

     */

    typedef void (^ZYResponseFail)(NSURLSessionDataTask * task, NSError * error);



    typedef void (^ZYProgress)(NSProgress *progress);

    @interfaceZyNetworking : NSObject


    + (instancetype)shareInstance;


    -(void)POST:(NSString *)url

         params:(NSDictionary *__nullable)params

        success:(ZuYResponseSuccess)success

           fail:(ZYResponseFail)fail;

    ZyNetWorking.m

    @interfaceZyNetworking ()

    @property (nonatomicstrongAFHTTPSessionManager *manager;

    @end

    @implementationZyNetworking

    staticZyNetworking *_instance = nil;

    + (instancetype)shareInstance

    {

        static dispatch_once_t onceToken ;

        dispatch_once(&onceToken, ^{

            _instance = [[super allocWithZone:NULLinit];

            _instance.manager = [AFHTTPSessionManagermanager];

            _instance.manager.responseSerializer.acceptableContentTypes = [NSSetsetWithObjects:@"text/plain",@"application/json"@"text/json"@"text/javascript",nil];

            _instance.manager.requestSerializer.timeoutInterval = 30.f;// 请求超时时间

        }) ;

        return_instance;

    }

     

    -(void)POST:(NSString *)url params:( NSDictionary *__nullable)params

        success:(ZuYResponseSuccess)success fail:(ZYResponseFail)fail{

         

         //如果需要请求token权鉴 ,可也在这里进行统一处理

        [_instance.manager.requestSerializersetValue:token forHTTPHeaderField:@"token"];

        

            [_instance.manager POST:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

         //your code 

         //根据你的业务需求进行你想要的处理

         //甚至可以配合测试业务处理请求日志



                NSInteger code = [[responseObject objectForKey:@"code"integerValue];


                success(task,responseObject,code);


                

            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {

                

                NSHTTPURLResponse * responses = (NSHTTPURLResponse *)task.response;

             // your code . 

             //示例根据失败也业务码进行不同的处理 .例如401 需要重新请求token   402强制退出登录等 . 也可记录请求日志(错误)

             // 如不需要也可直接做 fail(task,error)的回调

                if (responses.statusCode == 401) {

                 

                    [self zuyuTokenRefresh:url params:params];

                   

                }else if (responses.statusCode == 402) {

                    

                }else{

                    fail(task,error);

                }

            }]; 

        

    }

     



    git地址:https://github.com/AFNetworking/AFNetworking

    收起阅读 »