how to make UITextView height dynamic according to text length?

swift3 UITextView自适应高度

 

http://stackoverflow.com/questions/38714272/how-to-make-uitextview-height-dynamic-according-to-text-length

在Swift语言中处理JSON – 转换JSON和Model

背景

JSON是移动端开发常用的应用层数据交换协议。最常见的场景便是,客户端向服务端发起网络请求,服务端返回JSON文本,然后客户端解析这个JSON文本,再把对应数据展现到页面上。

但在编程的时候,处理JSON是一件麻烦事。在不引入任何轮子的情况下,我们通常需要先把JSON转为Dictionary,然后还要记住每个数据对应的Key,用这个Key在Dictionary中取出对应的Value来使用。这个过程我们会犯各种错误:

  • Key拼写错了;
  • 路径写错了;
  • 类型搞错了;
  • 没拿到值懵逼了;
  • 某一天和服务端约定的某个字段变更了,没能更新所有用到它的地方;

为了解决这些问题,很多处理JSON的开源库应运而生。在Swift中,这些开源库主要朝着两个方向努力:

  1. 保持JSON语义,直接解析JSON,但通过封装使调用方式更优雅、更安全;
  2. 预定义Model类,将JSON反序列化为类实例,再使用这些实例;

对于1,使用最广、评价最好的库非 SwiftyJSON 莫属,它很能代表这个方向的核心。它本质上仍然是根据JSON结构去取值,使用起来顺手、清晰。但也正因如此,这种做法没能妥善解决上述的几个问题,因为Key、路径、类型仍然需要开发者去指定;

对于2,我个人觉得这是更合理的方式。由于Model类的存在,JSON的解析和使用都受到了定义的约束,只要客户端和服务端约定好了这个Model类,客户端定义后,在业务中使用数据时就可以享受到语法检查、属性预览、属性补全等好处,而且一旦数据定义变更,编译器会强制所有用到的地方都改过来才能编译通过,非常安全。这个方向上,开源库们做的工作,主要就是把JSON文本反序列化到Model类上了。这一类JSON库有 ObjectMapperJSONNeverDieHandyJSON 等。而 HandyJSON 是其中使用最舒服的一个库,本文将介绍用  HandyJSON 来进行Model和JSON间的互相转换。

为什么用HandyJSON

在Swift中把JSON反序列化到Model类,在HandyJSON出现以前,主要使用两种方式:

  1. 让Model类继承自NSObject,然后class_copyPropertyList()方法获取属性名作为Key,从JSON中取得Value,再通过Objective-C runtime支持的KVC机制为类属性赋值;如JSONNeverDie
  2. 支持纯Swift类,但要求开发者实现Mapping函数,使用重载的运算符进行赋值,如ObjectMapper

这两者都有显而易见的缺点。前者要求Model继承自NSObject,非常不优雅,且直接否定了用struct来定义Model的方式;后者的Mapping函数要求开发者自定义,在其中指明每个属性对应的JSON字段名,代码侵入大,且仍然容易发生拼写错误、维护困难等问题。

HandyJSON独辟蹊径,采用Swift反射+内存赋值的方式来构造Model实例,规避了上述两个方案遇到的问题。

把JSON转换为Model

简单类型

某个Model类想支持通过HandyJSON来反序列化,只需要在定义时,实现HandyJSON协议,这个协议只要求实现一个空的init()函数。

比如我们和服务端约定了一个Animal数据,里面有name/id/num字段,那么我们这样定义Animal类:

然后假设我们从服务端拿到这样一个JSON文本:

引入HandyJSON以后,我们就可以这样来做反序列化了:

简单吧~

比较复杂的类型

HandyJSON支持在类定义里使用各种形式的基本属性,包括可选(?),隐式解包可选(!),数组(Array),字典(Dictionary),Objective-C基本类型(NSString、NSNumber),各种类型的嵌套([Int]?、[String]?、[Int]!、…)等等。比如下面这个看起来比较复杂的类型:

一样轻松转换:

嵌套的Model类

如果Model类中的某个属性是另一个自定义的Model类,那么只要那个Model类也实现了HandyJSON协议,就一样可以转换:

指定反序列化JSON中某个节点

有时候服务端返回给我们的JSON文本包含了大量的状态信息,和Model无关,比如statusCodedebugMessage等,或者有用的数据是在某个节点以下,那么我们可以指定反序列化哪个节点:

有继承关系的Model类

如果某个Model类继承自另一个Model类,只需要这个父Model类实现HandyJSON协议就可以:

自定义解析方式

HandyJSON还提供了一个扩展能力,就是允许自行定义Model类某个字段的解析Key、解析方式。我们经常会有这样的需求:

  • 某个Model中,我们不想使用和服务端约定的key作为属性名,想自己定一个;
  • 有些类型如enumtuple是无法直接从JSON中解析出来的,但我们在Model类中有这样的属性;

HandyJSON协议提供了一个可选的mapping()函数,我们可以在其中指定某个字段用什么Key、或者用什么方法从JSON中解析出它的值。如我们有一个Model类和一个服务端返回的JSON串:

可以看到,Cat类的id属性和JSON文本中的Key是对应不上的;而对于parent这个属性来说,它是一个元组,做不到从JSON中的"Tom/Lily"解析出来。所以我们要定义一个Mapping函数来做这两个支持:

就这样,HandyJSON完美地帮我们进行了JSON到Model类的转换。真是太Handy了。

把Model转换为JSON文本

HandyJSON还提供了把Model类序列化为JSON文本的能力,简直无情。

基本类型

如果只需要进行序列化,那么在定义Model类时,不需要做任何特殊的改动。任何一个类的实例,直接调用HandyJSON的序列化方法去序列化,就能得到JSON字符串

可以通过prettify参数来指定获得的是否是格式化后的JSON串。

复杂类型

即使Model类中有别的Model类啥的,都一样支持。

总结

有了HandyJSON的支持,现在我们可以开心地在Swift中使用JSON了。这个库还在更新中,已经支持了Swift 2.2+, Swift 3.0+。如果大家有什么需求或者建议,快去 https://github.com/alibaba/handyjson 给作者(哈哈没错就是我)提issue吧~~

 

Swift – 修改导航栏的样式(文字颜色,背景颜色,背景图片)

默认情况,导航栏UINavigationController的样式如下,如果想要使用代码修改样式也是比较简单的。

原文:Swift - 修改导航栏的样式(文字颜色,背景颜色,背景图片)
1,修改导航栏背景色
原文:Swift - 修改导航栏的样式(文字颜色,背景颜色,背景图片)
2,修改导航栏文字颜色
原文:Swift - 修改导航栏的样式(文字颜色,背景颜色,背景图片)

3,修改导航栏按钮颜色
不管是默认按钮,还是自定义的按钮,颜色都会被修改的。
原文:Swift - 修改导航栏的样式(文字颜色,背景颜色,背景图片)

4,修改导航栏背景图片
如果背景图片不需要延伸到状态栏后面,那么背景图片高度是44点(88像素)。
原文:Swift - 修改导航栏的样式(文字颜色,背景颜色,背景图片)

如果需要把导航栏也包含在背景图片下,那么背景图片高度改为64点(128像素)。

原文:Swift - 修改导航栏的样式(文字颜色,背景颜色,背景图片)

不管何种尺寸,设置代码如下:


 

 

原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_962.html

 

Swift – 在导航栏左侧或者右侧放置多个按钮

使用 navigationItem.leftBarButtonItem 和 navigationItem.rightBarButtonItem 可以很方便的分别设置导航栏左侧和右侧按钮,但一侧只能有一个按钮。

如果想要在一侧使用多个按钮,可以通过 leftBarButtonItems 和 rightBarButtonItems 来实现。
下面演示给导航栏右侧添加两个按钮(搜索和设置):
同时两个按钮间设置了间距,并将设置按钮顶到边上。

原文:Swift - 在导航栏左侧或者右侧放置多个按钮

 

原文出自:www.hangge.com  转载请保留原文链接:http://www.hangge.com/blog/cache/detail_964.html

 

Swift – 将导航栏标题修改成自定义视图(图片、按钮、分段选择等)

默认导航栏UINavigationController的标题是文字,通过navigationItem.titleView属性可以很方便的将其替换成自定义的UIView视图。

1,使用图片作为标题

原文:Swift - 将导航栏标题修改成自定义视图(图片、按钮、分段选择等)

2,使用按钮作为标题
原文:Swift - 将导航栏标题修改成自定义视图(图片、按钮、分段选择等)

3,使用分段选择控件作为标题
原文:Swift - 将导航栏标题修改成自定义视图(图片、按钮、分段选择等)

原文链接:http://www.hangge.com/blog/cache/detail_963.html

Swift – 使用Core Data进行数据持久化存储

一,Core Data介绍
1,Core Data是iOS5之后才出现的一个数据持久化存储框架,它提供了对象-关系映射(ORM)的功能,即能够将对象转化成数据,也能够将保存在数据库中的数据还原成对象。

2,虽然其底层也是由类似于SQL的技术来实现,但我们不需要编写任何SQL语句,有点像Java开发中的Hibernate持久化框架

3,Core Data数据最终的存储类型可以是:SQLite数据库,XML,二进制,内存里,或自定义数据类型。
4,与SQLite区别:只能取出整个实体记录,然后分解,之后才能得到实体的某个属性。

二,Core Data的使用准备 – 数据模型和实体类的创建
1,创建项目的时候,勾选“Use Core Data”。完毕后在 AppDelegate 中,会生成相关代码。
原文:Swift - 使用Core Data进行数据持久化存储
2,打开项目中的 xcdatamodeld 文件,在右边的数据模型编辑器的底部工具栏点击 Add Entity 添加实体。
同时在属性栏中对实体命名进行修改,并在 Attribute 栏目中添加 idusernamepassword 三个属性。
原文:Swift - 使用Core Data进行数据持久化存储
3,点击下方的 Editor Style 按钮可以查看实体的关系图。
原文:Swift - 使用Core Data进行数据持久化存储
4,自 iOS10 和 swift3 之后,访问 CoreData 的方法简洁了许多,我们不再需要手动新建对应于 entity 的 class。

三,Core Data的使用
1,首先在代码中引入CoreData库


2,插入(保存)数据操作

3,查询数据操作

4,修改数据操作

即将查询出来的对象进行重新赋值,然后再使用context.save方法重新保存即可

5,删除数据操作
删除操作使用context.delete方法,删除某个对象。然后使用context.save方法保存更新到数据库

 

四,数据的存放位置
默认Core Data生成的是sqlite文件,保存在Documents文件夹下
原文:Swift - 使用Core Data进行数据持久化存储

原文出自:www.hangge.com

转载请保留原文链接:http://www.hangge.com/blog/cache/detail_767.html

Swift – 使用UserDefaults来进行本地数据存储

UserDefaults 适合存储轻量级的本地客户端数据,比如记住密码功能,要保存一个系统的用户名、密码。使用 UserDefaults 是首选。下次再登陆的时候就可以直接从 UserDefaults 里面读取上次登陆的信息。
一般来说本地存储数据我们还可以是用 SQlite 数据库,或者使用自己建立的 plist 文件什么的,但这还得自己显示创建文件,读取文件,很麻烦,而是用 UserDefaults 则不用管这些东西,就像读字符串一样,直接读取就可以了。
UserDefaults 支持的数据格式也很多,有:Int,Float,Double,BOOL,Array,Dictionary,甚至 Any 类型。
1,下面通过一个样例演示UserDefaults的用法:
(1)如果是第一次运行程序通过 CFUUIDCreate 方法生成一个唯一字符串作为用户id储存起来(形如:B8DDB58D-73BF-4E39-A051-365858FC4626)
(2)往后运行时直接从 UserDefaults 中把用户id取出

执行结果如下:

原文:Swift - 使用UserDefaults来进行本地数据存储

2,对原生数据类型的储存和读取

3,系统对象的存储与读取
系统对象实现存储,需要通过 archivedData 方法转换成 Data 为载体,才可以存储。下面以 UILabel 对象为例:

对于 UIImage 对象的存储比较特殊。注意下方高亮部分,如果我们过直接把 image1 存储起来,再取出转换回 UIImage 就变成了 nil。必须先转成 image2 再存储。

4,自定义对象的存储和读取
如果想要存储自己定义的类,首先需要对该类实现 NSCoding 协议来进行归档和反归档(序列化和反序列化)。即该类内添加 func encode(with coder: NSCoder) 方法和 init(coder decoder: NSCoder) 方法,将属性进行转换。

5,删除存储对象
通过 removeObject() 方法可以删除已保存的数据。当然如果这个存储对象不存在也不会报错。

原文出自:www.hangge.com

转载请保留原文链接:http://www.hangge.com/blog/cache/detail_668.html

 

如何从项目中移除CocoaPods

使用CocoaPod管理第三方代码是非常方便的,但是当不需要使用的时候,删除起来就非常麻烦,这里我根据国外朋友的方案翻译了一下。不足之处望见谅。

操作步骤

  1. 删除本地文件(PodfilePodfile.lockPods文件夹)
  2. 删除本地生成的xcworkspace文件
  3. 打开项目,在Frameworks文件夹下,删除Pods.xcconfiglibPods.a
  4. 进入项目Build Phases,删除Copy Pods ResourcesEmbed Pods FrameworksCheck Pods Manifest.lock 三项。

  5. 因为删除了CocoaPod管理的第三方代码,所以在工程里面之前引用的第三方代码都会报错。这时候需要删除对应的代码。

通过第三方插件删除

我们可以通过CocoaPod的核心团队成员开源的插件cocoapods-dependencies进行操作。这样能保证删除操作根据CocoaPod的更新而保持最新的。

swift storyboard传值

故事版传递大概分为两种

1.故事版内视图之间传值

2.多个故事版之间传值

 

Swift UINavigationController基本用法

首先创建两个子视图控制器 FirstViewController和SecondViewController,然后把FirstViewController设置为导航视图控制器的根视图控制器

代码

FirstViewController

SecondViewController

接着代开AppDelegate.swift文件,我们将在该文件的didFinishLaunchingWithOptions方法中,创建一个导航控制器,并将FirstViewController视图控制器作为导航视图控制器的根视图控制器

 

Swift UITabBarController用法

介绍

 

分别创建三个视图:FirstViewController、SecondViewController、ThirdViewController,然后在第一个视图上面点击按钮可以跳转到第二个视图

代码如下

FirstViewController

SecondViewController

ThirdViewController

实现UITabBarController

打开AppDelegate.swift

 

Swift点击按钮弹出新的视图控制器并传值

点击默认视图控制器按钮后,传递值给新的视图控制器

新的视图控制器关闭后,传递新的值给默认视图控制器

ViewController

NewViewController

 

为什么PHP应该使用 Repository Pattern设计模式

Repository 模式

为了保持代码的整洁性和可读性,使用Repository Pattern 是非常有用的。事实上,我们也不必仅仅为了使用这个特别的设计模式去使用Laravel,然而在下面的场景下,我们将使用OOP的框架Laravel 去展示如何使用repositories 使我们的Controller层不再那么啰嗦、更加解耦和易读。下面让我们更深入的研究一下。

不使用 repositories

其实使用Repositories并不是必要的,在你的应用中你完全可以不使用这个设计模式的前提下完成绝大多数的事情,然而随着时间的推移你可能把自己陷入一个死角,比如不选择使用Repositories会使你的应用测试很不容易,具体的实现将会变的很复杂,下面我们看一个例子。HousesController.php

这是一个很典型的一段代码使用Eloquent和数据库交互,这段代码工作的很正常,但是controller层对于Eloquent而言将是紧耦合的。在此我们可以注入一个repository创建一个解耦类型的代码版本,这个解耦的版本代码可以使后续程序的具体实现更加简单。

使用 repositories

其实完成整个repository模式需要相当多的步骤,但是一旦你完成几次就会自然而然变成了一种习惯了,下面我们将详细介绍每一步。

1. 创建 Repository 文件夹

首先我们需要在app文件夹创建自己Repository文件夹repositories,然后文件夹的每一个文件都要设置相应的命名空间。

2: 创建相应的 Interface

第二步创建对应的接口,其决定着我们的repository类必须要实现的相关方法,如下例所示,在此再次强调的是命名空间一定要记得加上。HouseRepositoryInterface.php

3: 创建对应的 Repository

现在我们可以创建我们repository类 来给我们干活了,在这个类文件中我们可以把我们的绝大多数的数据库查询都放进去,不论多么复杂。如下面的例子 DbHouseRepository.php

4: 创建后端服务提供

首先你需要理解所谓服务提供,请参考手册服务提供者 BackendServiceProvider.php

当然你也可以新建一个文件夹主要放我们的provider相关文件。 上面一段代码主要说的是,当你在controller层使用类型提示HouseRepositoryInterface, 我们知道你将会使用DbHouseRepository.

5: 更新你的Providers Array

其实在上面的代码中,我们已经实现了一个依赖注入,但如果我们要使用在此我们是需要手动去写的,为了更为方面,我们需要增加这个providers到 app/config/app.php 中的 providers数组里面, 只需要在最后加上App\Repositories\BackendServiceProvider::class,

6:最后使用依赖注入更新你的controller

当我们完成上面的那些内容之后,我们在Controller只需要简单的调用方法代替之前的复杂的数据库调用,如下面内容:HousesController.php