UIImage图片拉伸

1
2
3
// 做到一个聊天背景图时 发现背景图片需要自动大小, 记录下图片拉伸的使用方法
// 参数的意思是从宽高的位置指向的一个像素的+1像素进行拉伸 (参数宽高 范围 是保护部分不会被拉伸)
image = [image stretchableImageWithLeftCapWidth:image.size.width/2 topCapHeight:image.size.height/2];

iOS中图片(UIImage)拉伸技巧
转: https://my.oschina.net/u/2340880/blog/403996

iOS中图片拉伸技巧与方法总结

一、了解几个图像拉伸的函数和方法

1、直接拉伸法

简单暴力,却是最最常用的方法,直接将图片设置为ImageView的image属性,图片便会随UIImageView对象的大小做自动拉伸。这种拉伸的方法有一个致命的缺陷,它会使图像发生失真与形变。

2、像素点的拉伸

  • (UIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight;

这个函数我们可以用来拉伸类似QQ,微信的聊天气泡背景图,它的两个参数分别leftCapWidth和topCapHeight,这两个参数给定一个坐标,比如:

1
2
UIImage * img= [UIImage imageNamed:@"11.png"];
img = [img stretchableImageWithLeftCapWidth:1 topCapHeight:1];

这段代码的意思是将图片从左起第2列,上起第2行,坐标为(2,2)的像素点进行复制。将图片进行拉伸。这个方法和上面的方法比起来似乎灵活性更多了,但其也有它的一些局限,如果被拉伸的图片中间也有需要拉伸的像素,这个方法就无能为力了,例如,如下的一张图片,我们需要将其拉伸放大:

3、区域的拉伸

1
- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets;

这个函数需要设置一个UIEdgeInsets参数,UIEdgeInsets结构体如下:

1
2
3
typedef struct UIEdgeInsets {
CGFloat top, left, bottom, right;
} UIEdgeInsets;

它分别对用了图片进行拉伸的区域距离顶部、左部、下部、右部的像素。比如,一个1010像素的图片,将UIEdgeInsets参数全部设置为1,则实际拉伸的部分就是中间的88的区域的像素。有一点需要注意,这个方法默认使用的拉伸模式是区域复制,比如还是上面的图案,如下代码拉伸:

1
2
UIImage * img= [UIImage imageNamed:@"11.png"];
img = [img resizableImageWithCapInsets:UIEdgeInsetsMake(1, 1, 1, 1)];

可以明显的看到中间的虚线,这便是区域复制的杰作。

那么问题又来了,如果某些图片中间有渐变,我们该怎么处理了,来看下一个函数。

4、拉伸模式的设置

1
2
- (UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets resizingMode:(UIImageResizingMode)resizingMode;

这个函数和上一个函数相比,唯一的差别是多了一个参数。这个参数是个枚举,如下:

1
2
3
4
typedef NS_ENUM(NSInteger, UIImageResizingMode) {
UIImageResizingModeTile,//进行区域复制模式拉伸
UIImageResizingModeStretch,//进行渐变复制模式拉伸
};

现在就明了了,我们只需要设置一下模式,就可以实现渐变拉伸了:

1
2
UIImage * img= [UIImage imageNamed:@"11.png"];
img = [img resizableImageWithCapInsets:UIEdgeInsetsMake(1, 1, 1, 1) resizingMode:UIImageResizingModeStretch];

二、拉伸的用武之地

圆角按钮,空心按钮,渐变的背景,内容可变的标签,聊天气泡等等这样的素材在APP中很可能会多次出现,并且每次出现的尺寸可能还会略微有些差异,如果仅仅依靠美工的素材,恐怕不仅很难达到要求,也会额外增加软件的内存开销,这时,我们使用恰当的拉伸技巧,能使我们的代码更加健壮,APP更加高效。

三、一点小经验

你是否注意观察过最细的线?

看到上面的问句,你可能有些差异。最细的线不就是一像素么?确实,能绘图画出来的最细的实心线确实是一像素,但在一个项目中,我们优秀的美工察觉到无论她把线做的多么细,无论我怎样控制拉伸方法,绘制出的登录框总是没有QQ的细,QQ的框线看起来更加干脆利索。后来索性用绘图画出登录框,结果很不幸,我依然无法将线做到像QQ登录框那样细致。后来偶然试了一种方法,不知原理是否正确,效果总算达到了,当然这也要归功于我们的美工,她将一个图片做的很大,适配最大的分辨率,然后让我手动缩,如此一来,那线就变得非常细。


如果只拉伸左边和右边,中间不变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (UIImage *)dc_stretchLeftAndRightWithContainerSize:(CGSize)size
{
CGSize imageSize = self.size;
CGSize bgSize = size;
//1.第一次拉伸右边 保护左边
UIImage *image = [self stretchableImageWithLeftCapWidth:imageSize.width *0.8 topCapHeight:imageSize.height * 0.5];
//第一次拉伸的距离之后图片总宽度
CGFloat tempWidth = (bgSize.width)/2 + imageSize.width/2;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(tempWidth, imageSize.height), NO, [UIScreen mainScreen].scale);
[image drawInRect:CGRectMake(0, 0, tempWidth, bgSize.height)];
//拿到拉伸过的图片
UIImage *firstStrechImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//2.第二次拉伸左边 保护右边
UIImage *secondStrechImage = [firstStrechImage stretchableImageWithLeftCapWidth:firstStrechImage.size.width *0.1 topCapHeight:firstStrechImage.size.height*0.5];
return secondStrechImage;
}

AutoLayout框架Masonry使用心得

转自 :http://www.starming.com/index.php?v=index&view=81

我们组分享会上分享了页面布局的一些写法,中途提到了AutoLayout,会后我决定将很久前挖的一个坑给填起来(还有好多坑就不说了,说了不填更毁形象了)。

可使用的框架首推Masonry,关于为啥选择Masonry看看官方文档就明白了https://github.com/SnapKit/Masonry,官方称AutoLayout所有功能Masonry都支持。这次项目界面方面我就全部使用了Masonry。

AutoLayout的一些基本概念

  • 利用约束来控制视图的大小和位置,系统会在运行时通过设置的约束计算得到frame再绘制屏幕
  • 两个属性Content Compression Resistance(排挤,值越高越固定)和Content Hugging(拥抱),Masonry代码如下
1
2
3
4
5
6
7
//content hugging 为1000
[view setContentHuggingPriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisHorizontal];
// content compression 为250
[view setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
forAxis:UILayoutConstraintAxisHorizontal];
  • multipler属性表示约束值为约束对象的百分比,在Masonry里有对应的multipliedBy函数

    1
    2
    //宽度为superView宽度的20%
    make.width.equalTo(superView.mas_width).multipliedBy(0.2);
  • AutoLayout下UILabel设置多行计算需要设置preferredMaxLayoutWidth

    1
    label.preferredMaxWidth = [UIScreen mainScreen].bounds.size.width - margin - padding;
  • preferredMaxLayoutWidth用来制定最大的宽,一般用在多行的UILabel中

  • systemLayoutSizeFittingSize方法能够获得view的高度
  • iOS7有两个很有用的属性,topLayoutGuide和bottomLayoutGuide,这个两个主要是方便获取UINavigationController和UITabBarController的头部视图区域和底部视图区域。
    1
    2
    //Masonry直接支持这个属性
    make.top.equalTo(self.mas_topLayoutGuide);

AutoLayout关于更新的几个方法的区别

  • setNeedsLayout:告知页面需要更新,但是不会立刻开始更新。执行后会立刻调用layoutSubviews。
  • layoutIfNeeded:告知页面布局立刻更新。所以一般都会和setNeedsLayout一起使用。如果希望立刻生成新的frame需要调用此方法,利用这点一般布局动画可以在更新布局后直接使用这个方法让动画生效。
  • layoutSubviews:系统重写布局
  • setNeedsUpdateConstraints:告知需要更新约束,但是不会立刻开始
  • updateConstraintsIfNeeded:告知立刻更新约束
  • updateConstraints:系统更新约束

Masonry使用注意事项

  • 用mas_makeConstraints的那个view需要在addSubview之后才能用这个方法
  • mas_equalTo适用数值元素,equalTo适合多属性的比如make.left.and.right.equalTo(self.view)
  • 方法and和with只是为了可读性,返回自身,比如make.left.and.right.equalTo(self.view)和make.left.right.equalTo(self.view)是一样的。
  • 因为iOS中原点在左上角所以注意使用offset时注意right和bottom用负数。

Masonry适配iOS6和iOS7时需要注意的问题

开发项目时是先在iOS8上调试完成的,测试时发现低版本的系统会发生奔溃的现象,修复后总结问题主要是在equalTo的对象指到了父视图的父视图或者父视图同级的子视图上造成的,所以做约束时如果出现了奔溃问题百分之九十都是因为这个。

Masonry使用范例

基本写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//相对于父视图边距为10
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
//相对于父视图边距为10简洁写法
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
//这两个作用完全一样
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.greaterThanOrEqualTo(self.view);
make.left.greaterThanOrEqualTo(self.view.mas_left);
}];
//.equalTo .lessThanOrEqualTo .greaterThanOrEqualTo使用NSNumber
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400);
make.left.lessThanOrEqualTo(@10);
}];
//如果不用NSNumber可以用以前的数据结构,只需用mas_equalTo就行
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(42);
make.height.mas_equalTo(20);
make.size.mas_equalTo(CGSizeMake(50, 100));
make.edges.mas_equalTo(UIEdgeInsetsMake(10, 0, 10, 0));
make.left.mas_equalTo(self.view).mas_offset(UIEdgeInsetsMake(10, 0, 10, 0));
}];
//也可以使用数组
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.height.equalTo(@[self.view.mas_height, superview.mas_height]);
make.height.equalTo(@[self.view, superview]);
make.left.equalTo(@[self.view, @100, superview.mas_right]);
}];
// priority的使用
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.greaterThanOrEqualTo(self.view.mas_left).with.priorityLow();
make.top.equalTo(self.view.mas_top).with.priority(600);
}];
//同时创建多个约束
[self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) {
//让top,left,bottom,right都和self.view一样
make.edges.equalTo(self.view);
//edges
make.edges.equalTo(self.view).insets(UIEdgeInsetsMake(5, 10, 15, 20));
//size
make.size.greaterThanOrEqualTo(self.view);
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50));
//center
make.center.equalTo(self.view);
make.center.equalTo(self.view).centerOffset(CGPointMake(-5, 10));
//chain
make.left.right.and.bottom.equalTo(self.view);
make.top.equalTo(self.view);
}];

AutoLayout情况如何计算UITableView的变高高度

主要是UILabel的高度会有变化,所以这里主要是说说label变化时如何处理,设置UILabel的时候注意要设置preferredMaxLayoutWidth这个宽度,还有ContentHuggingPriority为UILayoutPriorityRequried

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CGFloat maxWidth = [UIScreen mainScreen].bounds.size.width - 10 * 2;
textLabel = [UILabel new];
textLabel.numberOfLines = 0;
textLabel.preferredMaxLayoutWidth = maxWidth;
[self.contentView addSubview:textLabel];
[textLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(statusView.mas_bottom).with.offset(10);
make.left.equalTo(self.contentView).with.offset(10);
make.right.equalTo(self.contentView).with.offset(-10);
make.bottom.equalTo(self.contentView).with.offset(-10);
}];
[_contentLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];

如果版本支持最低版本为iOS 8以上的话可以直接利用UITableViewAutomaticDimension在tableview的heightForRowAtIndexPath直接返回即可。

1
2
3
4
5
6
7
tableView.rowHeight = UITableViewAutomaticDimension;
tableView.estimatedRowHeight = 80; //减少第一次计算量,iOS7后支持
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// 只用返回这个!
return UITableViewAutomaticDimension;
}

但如果需要兼容iOS 8之前版本的话,就要回到老路子上了,主要是用systemLayoutSizeFittingSize来取高。步骤是先在数据model中添加一个height的属性用来缓存高,然后在table view的heightForRowAtIndexPath代理里static一个只初始化一次的Cell实例,然后根据model内容填充数据,最后根据cell的contentView的systemLayoutSizeFittingSize的方法获取到cell的高。具体代码如下

//在model中添加属性缓存高度
@interface DataModel : NSObject
@property (copy, nonatomic) NSString *text;
@property (assign, nonatomic) CGFloat cellHeight; //缓存高度
@end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
static CustomCell *cell;
//只初始化一次cell
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([CustomCell class])];
});
DataModel *model = self.dataArray[(NSUInteger) indexPath.row];
[cell makeupData:model];
if (model.cellHeight <= 0) {
//使用systemLayoutSizeFittingSize获取高度
model.cellHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1;
}
return model.cellHeight;
}

动画

因为布局约束就是要脱离frame这种表达方式的,可是动画是需要根据这个来执行,这里面就会有些矛盾,不过根据前面说到的布局约束的原理,在某个时刻约束也是会被还原成frame使视图显示,这个时刻可以通过layoutIfNeeded这个方法来进行控制。具体代码如下

1
2
3
4
5
6
7
8
9
10
11
[aniView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.bottom.left.right.equalTo(self.view).offset(10);
}];
[aniView mas_updateConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).offset(30);
}];
[UIView animateWithDuration:3 animations:^{
[self.view layoutIfNeeded];
}];

参考文章

除非注明,均为Starming星光社原创,微信公共号 starming-weixin 发现更多,转载请注明本文地址:http://www.starming.com/index.php?v=index&view=81

寻找上层的ViewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
extension UIView {
func findController() -> UIViewController! {
return self.findControllerWithClass(UIViewController.self)
}
func findNavigator() -> UINavigationController! {
return self.findControllerWithClass(UINavigationController.self)
}
func findControllerWithClass<T>(clzz: AnyClass) -> T? {
var responder = self.nextResponder()
while(responder != nil) {
if (responder!.isKindOfClass(clzz)) {
return responder as? T
}
responder = responder?.nextResponder()
}
return nil
}
}

还有一种通过rootviewController 寻找的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//获取当前屏幕显示的viewcontroller
- (UIViewController *)getCurrentVC
{
UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
UIViewController *currentVC = [self getCurrentVCFrom:rootViewController];
return currentVC;
}
- (UIViewController *)getCurrentVCFrom:(UIViewController *)rootVC
{
UIViewController *currentVC;
if ([rootVC presentedViewController]) {
// 视图是被presented出来的
rootVC = [rootVC presentedViewController];
}
if ([rootVC isKindOfClass:[UITabBarController class]]) {
// 根视图为UITabBarController
currentVC = [self getCurrentVCFrom:[(UITabBarController *)rootVC selectedViewController]];
} else if ([rootVC isKindOfClass:[UINavigationController class]]){
// 根视图为UINavigationController
currentVC = [self getCurrentVCFrom:[(UINavigationController *)rootVC visibleViewController]];
} else {
// 根视图为非导航类
currentVC = rootVC;
}
return currentVC;
}
//swift
/// 获取当前显示的VC
func getCurrentVC()->UIViewController{
let rootViewController = (UIApplication.shared.delegate as! AppDelegate).window?.rootViewController
// if rootViewController == nil{return nil}
let currentVC = self.getCurrentVCFrom(rootVC: rootViewController!)
return currentVC
}
fileprivate func getCurrentVCFrom(rootVC:UIViewController)->UIViewController{
//var currentVC:UIViewController!;
var currentVC:UIViewController = rootVC; // 这里先赋值处理,不然会崩溃(特定条件下)
var rootVC2 = rootVC
if rootVC.presentedViewController != nil{
rootVC2 = rootVC2.presentedViewController!
}
if (rootVC2.isKind(of: UITabBarController.classForCoder())){
rootVC2 = self.getCurrentVCFrom(rootVC: (rootVC2 as! UITabBarController).selectedViewController!)
}else if rootVC2.isKind(of: UINavigationController.classForCoder()){
currentVC = self.getCurrentVCFrom(rootVC: (rootVC2 as! UINavigationController).visibleViewController!)
}else{
currentVC = rootVC2
}
return currentVC
}

webCookie 设置,自动登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
var param = ""
if self.webUrl.rangeOfString("?") != nil{
param = "&random=\(random)"
}else{
param = "?random=\(random)"
}
let webNSUrl = NSURL(string: (self.webUrl+param))!
if (webNSUrl).host?.rangeOfString("chenzhao.date") != nil{
var is3Gcookie = false
let cookies_1 = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies
if cookies_1 != nil{
for c in cookies_1!{
if c.name == "COOKIE"{
// NSHTTPCookieStorage.sharedHTTPCookieStorage().deleteCookie(c)
if c.value == UserInfo.userInstance.jKey{
is3Gcookie = true
}else{
NSHTTPCookieStorage.sharedHTTPCookieStorage().deleteCookie(c)
}
}
}
}
if UserInfo.userInstance.key != ""{
if is3Gcookie == false{
let cookieProperties = [NSHTTPCookieName:"COOKIE",
NSHTTPCookieValue:UserInfo.userInstance.key,
NSHTTPCookieDomain:".chenzhao.date",
NSHTTPCookiePath:"/", //mark 位置"/"是域名的全网
// NSHTTPCookieVersion:"0",
]
//
let cookie = NSHTTPCookie(properties:cookieProperties)
// Getdevice.println("手动组成的cookie\(cookie) : path = \(webNSUrl.path!) :host = \(webNSUrl.host!)")
if cookie != nil{
NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(cookie!)
}
}
}
}
let cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
// Getdevice.println("加载数据设置headers cookies:\(headers)")
let request = NSMutableURLRequest(URL: webNSUrl, cachePolicy: NSURLRequestCachePolicy.UseProtocolCachePolicy, timeoutInterval: 20)
request.HTTPShouldHandleCookies = true
request.allHTTPHeaderFields = headers
// request.addValue("测试", forHTTPHeaderField: "test")
webView.loadRequest(request)

获取APP的Launch Image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Tips:获取APPLaunch Image
+ (UIImage *)getLanchImage
{
CGSize viewSize = CGSizeMake([[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height);
NSString *viewOrientation = @"Portrait"; //横屏请设置成 @"Landscape"
NSString *launchImage = nil;
NSArray* imagesDict = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"UILaunchImages"];
for (NSDictionary* dict in imagesDict)
{
CGSize imageSize = CGSizeFromString(dict[@"UILaunchImageSize"]);
if (CGSizeEqualToSize(imageSize, viewSize) && [viewOrientation isEqualToString:dict[@"UILaunchImageOrientation"]])
{
launchImage = dict[@"UILaunchImageName"];
}
}
// UIImageView *launchView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:launchImage]];
// launchView.frame = [[UIScreen mainScreen] bounds];
// launchView.contentMode = UIViewContentModeScaleAspectFill;
// [self.window addSubview:launchView];
return [UIImage imageNamed:launchImage];
}

UICollectionViewFlowLayout最大间距设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//
// DanceTeamSearchLayout.swift
// boosjdance
//
// Created by boosj on 16/8/29.
// Copyright © 2016年 cz. All rights reserved.
//
//swift3
import UIKit
/// 舞队 搜索记录的layout
class DanceTeamSearchLayout: UICollectionViewFlowLayout {
//我们想设置的最大间距,可根据需要改
//var maximumSpacing:CGFloat = 10
/**
重写当前方法 实现控制item最大间距
- parameter rect: rect 绘图范围
- returns: item属性数组
*/
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
if attributes != nil{
for i in 0 ..< attributes!.count{
if i == 0{
let current = attributes![i]
var frame = current.frame
frame.origin.x = 10
current.frame = frame
continue
}
//当前attributes
let current = attributes![i]
//上一个attributes
let prevLayoutAttributes = attributes![ (i - 1)]
//前一个cell的最右边
let origin = prevLayoutAttributes.frame.maxX
// var yOffset = current.frame.origin.y
//如果当前一个cell的最右边加上我们想要的间距加上当前cell的宽度依然在contentSize中,我们改变当前cell的原点位置
//不加这个判断的后果是,UICollectionView只显示一行,原因是下面所有cell的x值都被加到第一行最后一个元素的后面了
// self.minimumInteritemSpacing 列间距
if (origin + self.minimumInteritemSpacing + current.frame.size.width)<self.collectionViewContentSize.width{
var frame = current.frame
frame.origin.x = origin + self.minimumInteritemSpacing
current.frame = frame
}else{
var frame = current.frame
frame.origin.x = self.sectionInset.left // 边缘的预留宽度
// frame.origin.y = yOffset + frame.height + maximumSpacing
current.frame = frame
}
// printLog("cell测试frame: \(current.frame)")
}
}
return attributes
}
/*
如果我们的布局是会时刻变化的, 需要在滚动的过程中重新布局 , 那么我们需要
设置这个方法的返回值为true, 默认为false
* 当返回值为true的时候会将collectionView的layout设置为invalidated,
将会使collectionView重新调用上面的prepareLayout()...方法重新获得布局
* 同时, 当屏幕旋转的时候collectionView的bounds也会调用这个方法
如果设置为false, 那么将不会达到屏幕适配的效果,
* 需要注意的是, 当collectionView执行一些操作(delete insert reload)等的时候,
不会调用这个方法, 会直接重新调用上面的prepareLayout()...方法重新获得布局
*/
// override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
// return true
// }
}

一个左对齐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//
// UICollectionViewLeftAlignedLayout.swift
// test
//
// Created by boosj on 2017/8/14.
// Copyright © 2017年 boosjcz. All rights reserved.
//
import UIKit
extension UICollectionViewLayoutAttributes {
/** 每行第一个item左对齐 **/
func leftAlignFrame(_ sectionInset:UIEdgeInsets) {
var frame = self.frame
frame.origin.x = sectionInset.left
self.frame = frame
}
}
class UICollectionViewLeftAlignedLayout: UICollectionViewFlowLayout {
//MARK: - 重新UICollectionViewFlowLayout的方法
override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
/** Collection所有的UICollectionViewLayoutAttributes */
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributesToReturn = super.layoutAttributesForElements(in: rect)!
var attributesCopy = [UICollectionViewLayoutAttributes]()
for attributes in attributesToReturn {
if nil == attributes.representedElementKind {
let itemAttributesCopy = attributes.copy() as! UICollectionViewLayoutAttributes
let indexPath = itemAttributesCopy.indexPath
itemAttributesCopy.frame = self.layoutAttributesForItem(at: indexPath)!.frame
// manipulate itemAttributesCopy
attributesCopy.append(itemAttributesCopy)
}else{
// 忘记把这里app下 所以才没有进 头部代理
attributesCopy.append(attributes)
}
}
return attributesCopy // (copy 版 数据 防止警告 ,但是会无发进入头部代理)
}
//
/** 每个item的UICollectionViewLayoutAttributes */
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
//现在item的UICollectionViewLayoutAttributes
let currentItemAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as! UICollectionViewLayoutAttributes
//现在section的sectionInset
let sectionInset = self.evaluatedSectionInset(indexPath.section)
//是否是section的第一个item
let isFirstItemInSection = indexPath.item == 0
//出去section偏移量的宽度
let layoutWidth: CGFloat = self.collectionView!.frame.width - sectionInset.left - sectionInset.right
//是section的第一个item
if isFirstItemInSection {
//每行第一个item左对齐
currentItemAttributes.leftAlignFrame(sectionInset)
return currentItemAttributes
}
//前一个item的NSIndexPath
let previousIndexPath = IndexPath(item: indexPath.item - 1, section: indexPath.section)
//前一个item的frame
let previousFrame = self.layoutAttributesForItem(at: previousIndexPath)!.frame
//为现在item计算新的left
let previousFrameRightPoint: CGFloat = previousFrame.origin.x + previousFrame.size.width
//现在item的frame
let currentFrame = currentItemAttributes.frame
//现在item所在一行的frame
let strecthedCurrentFrame = CGRect(x: sectionInset.left, y: currentFrame.origin.y, width: layoutWidth, height: currentFrame.size.height)
//previousFrame和strecthedCurrentFrame是否有交集,没有,说明这个item和前一个item在同一行,item是这行的第一个item
let isFirstItemInRow = !previousFrame.intersects(strecthedCurrentFrame)
//item是这行的第一个item
if isFirstItemInRow {
//每行第一个item左对齐
currentItemAttributes.leftAlignFrame(sectionInset)
return currentItemAttributes
}
//不是每行的第一个item
var frame = currentItemAttributes.frame
//为item计算新的left = previousFrameRightPoint + item之间的间距
frame.origin.x = previousFrameRightPoint + self.evaluatedMinimumInteritemSpacing(indexPath.item)
//为item的frame赋新值
currentItemAttributes.frame = frame
return currentItemAttributes
}
//MARK: - System
/** item行间距 **/
fileprivate func evaluatedMinimumInteritemSpacing(_ ItemAtIndex:Int) -> CGFloat {
if let delete = self.collectionView?.delegate {
weak var delegate = (delete as! UICollectionViewDelegateFlowLayout)
if delegate!.responds(to: #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:minimumInteritemSpacingForSectionAt:))) {
let mini = delegate?.collectionView!(self.collectionView!, layout: self, minimumInteritemSpacingForSectionAt: ItemAtIndex)
if mini != nil {
return mini!
}
}
}
return self.minimumInteritemSpacing
}
/** section的偏移量 **/
fileprivate func evaluatedSectionInset(_ itemAtIndex:Int) -> UIEdgeInsets {
if let delete = self.collectionView?.delegate {
weak var delegate = (delete as! UICollectionViewDelegateFlowLayout)
if delegate!.responds(to: #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:insetForSectionAt:))) {
let sectionInset = delegate?.collectionView!(self.collectionView!, layout: self, insetForSectionAt: itemAtIndex)
if sectionInset != nil {
return sectionInset!
}
}
}
return self.sectionInset
}
}

设置文本的部分文本的颜色NSMutableAttributedString 样式文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
设置需要的文本颜色
:param: text 需要设置颜色的文本
:returns: return NSMutableAttributedString
*/
func setTextColor(number:String)->NSMutableAttributedString{
let testNumber = (number as NSString).length
let titlestr = "共\(number)个视频"
var str = NSMutableAttributedString(string: titlestr)
str.addAttribute(NSForegroundColorAttributeName, value: violetColor, range: NSMakeRange(1,testNumber)) // testNumber 为多少个字有颜色
return str
}
/**
设置第二行文本 和文本颜色设置
:param: title title 第二行标题文本
:param: begin begin 颜色设置开始位置
:param: end end 颜色设置多少个字符
*/
func setTwoTitle(title:String,begin:Int,count:Int)
{
var str = NSMutableAttributedString(string: title)
str.addAttribute(NSForegroundColorAttributeName, value: violetColor, range: NSMakeRange(begin,count))
self.titleLabel_2.attributedText = str
}
以前看到这种字号和颜色不一样的字符串,想出个讨巧的办法就是“¥150”一个UILable,“元/位”一个UILable。今天翻看以前的工程,command点进UITextField中看到[attributedText]这个关键字,以前都没注意过UITextField还有这个属性,其实UITextViewUILable也有这个属性,iOS6就已经有了,说来惭愧,对此罚站1秒钟。
NSAttributedString叫做富文本,是一种带有属性的字符串,通过它可以轻松的在一个字符串中表现出多种字体、字号、字体大小等各不相同的风格,还可以对段落进行格式化。
通过以下代码即可实现上面图示效果,十分方便,从此再也不用设置两个UILable,并且处心积虑的处理它们的长度了。
1 UILabel * aLable = [[UILabel alloc] initWithFrame:CGRectMake(100, 500, 200, 40)];
2 aLable.textAlignment = NSTextAlignmentCenter;
3 [self.view addSubview:aLable];
4
5 NSString * aString = @"¥150 元/位";
6
7 //富文本对象
8 NSMutableAttributedString * aAttributedString = [[NSMutableAttributedString alloc] initWithString:aString];
9
10 //富文本样式
11 [aAttributedString addAttribute:NSForegroundColorAttributeName //文字颜色
12 value:[UIColor redColor]
13 range:NSMakeRange(0, 4)];
14
15 [aAttributedString addAttribute:NSFontAttributeName //文字字体
16 value:[UIFont systemFontOfSize:25]
17 range:NSMakeRange(0, 4)];
18
19 aLable.attributedText = aAttributedString;
常用属性:
NSFontAttributeName           文字字体
NSParagraphStyleAttributeName     段落样式(字符串通过“\n”进行分段,此设置必须在lable.numberOfLines = 0时有效,value通过NSMutableParagraphStyle设置,它有以下属性)
[段落样式-插曲]
1 @property(readwrite) CGFloat lineSpacing; //行间距
2 @property(readwrite) CGFloat paragraphSpacing; //段间距
3 @property(readwrite) NSTextAlignment alignment; //对齐方式
4 @property(readwrite) CGFloat firstLineHeadIndent; //首行缩紧
5 @property(readwrite) CGFloat headIndent; //除首行之外其他行缩进
6 @property(readwrite) CGFloat tailIndent; //每行容纳字符的宽度
7 @property(readwrite) NSLineBreakMode lineBreakMode; //换行方式
8 @property(readwrite) CGFloat minimumLineHeight; //最小行高
9 @property(readwrite) CGFloat maximumLineHeight; //最大行高
10 @property(readwrite) NSWritingDirection baseWritingDirection; //书写方式(NSWritingDirectionNatural,NSWritingDirectionLeftToRight,NSWritingDirectionRightToLeft)
11 @property(readwrite) CGFloat lineHeightMultiple;
12 @property(readwrite) CGFloat paragraphSpacingBefore;
13 @property(readwrite) float hyphenationFactor;
14 @property(readwrite,copy,NS_NONATOMIC_IOSONLY) NSArray *tabStops NS_AVAILABLE_IOS(7_0);
15 @property(readwrite,NS_NONATOMIC_IOSONLY) CGFloat defaultTabInterval NS_AVAILABLE_IOS(7_0);
[段落样式demo]
1 UILabel * lable = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, self.view.frame.size.width-100, 200)];
2 lable.backgroundColor = [UIColor lightGrayColor];
3 lable.numberOfLines = 0;
4 [self.view addSubview:lable];
5
6 NSString * string = @"Always believe that something wonderful is about \nto happen!";
7
8 //富文本
9 NSMutableAttributedString * attributedString = [[NSMutableAttributedString alloc] initWithString:string];
10
11 //段落样式
12 NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
13
14 #warning lable.numberOfLines必须为0,段落样式才生效
15 //行间距
16 paragraphStyle.lineSpacing = 10.0;
17 //段落间距
18 paragraphStyle.paragraphSpacing = 20.0;
19
20 // paragraphStyle.baseWritingDirection = NSWritingDirectionLeftToRight;
21 // paragraphStyle.firstLineHeadIndent = 10.0;
22 // paragraphStyle.headIndent = 50.0;
23 // paragraphStyle.tailIndent = 200.0;
24
25 [attributedString addAttribute:NSParagraphStyleAttributeName
26 value:paragraphStyle
27 range:NSMakeRange(0, string.length)];
28
29 lable.attributedText = attributedString;
NSForegroundColorAttributeName    文字前景色
NSBackgroundColorAttributeName    文字背景色
NSLigatureAttributeName        连体字(NSNumber @0:无连体,@1:默认连体,系统字体不包含对连体的支持)
NSUnderlineStyleAttributeName     下划线
NSStrokeColorAttributeName       只有在NSStrokeWidthAttributeName设置了值之后才有效(默认字体颜色和前景色一致,如果设置的颜色和前景色不一致则前景色无效)
NSStrokeWidthAttributeName      设置该属性之后字体变成空心字体,字体边线宽度为value设定的值
NSBaselineOffsetAttributeName     值为NSNumber类型,表明文字相对于其他文字基准线向上的偏移量
NSUnderlineColorAttributeName     值为UIColor类型,下划线颜色(只有在NSUnderlineStyleAttributeName的value为@1时有效)
NSUnderlineStyleAttributeName     值为NSNumber类型,下划线宽度(默认值为@0:下划线宽度为0——不现实下划线,@1:字符串有下划线)
属性挺多的,有其他需要的话command点进去看一下就ok,如果对他们的功能不了解三根指头点一下关键词,或者按住option点一下看看官方文档的Description相信就会有所了解了,其他的暂时就不介绍了。
如有问题,欢迎指正,小弟在此拜谢。
这里有篇讲解富文本的文章:http://www.2cto.com/kf/201409/334308.html

UITextField 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
titleTextField.becomeFirstResponder() 设置为第一响应者
/初始化textfield并设置位置及大小
UITextField *text = [[UITextField alloc]initWithFrame:CGRectMake(20, 20, 130, 30)];
//设置边框样式,只有设置了才会显示边框样式
text.borderStyle = UITextBorderStyleRoundedRect;
typedef enum {
UITextBorderStyleNone,
UITextBorderStyleLine,
UITextBorderStyleBezel,
UITextBorderStyleRoundedRect
} UITextBorderStyle;
//设置输入框的背景颜色,此时设置为白色 如果使用了自定义的背景图片边框会被忽略掉
text.backgroundColor = [UIColor whiteColor];
//设置背景
text.background = [UIImage imageNamed:@"dd.png"];
//设置背景
text.disabledBackground = [UIImage imageNamed:@"cc.png"];
//当输入框没有内容时,水印提示 提示内容为password
text.placeholder = @"password";
//设置输入框内容的字体样式和大小
text.font = [UIFont fontWithName:@"Arial" size:20.0f];
//设置字体颜色
text.textColor = [UIColor redColor];
//输入框中是否有个叉号,在什么时候显示,用于一次性删除输入框中的内容
text.clearButtonMode = UITextFieldViewModeAlways;
typedef enum {
UITextFieldViewModeNever, 重不出现
UITextFieldViewModeWhileEditing, 编辑时出现
UITextFieldViewModeUnlessEditing, 除了编辑外都出现
UITextFieldViewModeAlways 一直出现
} UITextFieldViewMode;
//输入框中一开始就有的文字
text.text = @"一开始就在输入框的文字";
//每输入一个字符就变成点 用语密码输入
text.secureTextEntry = YES;
//是否纠错
text.autocorrectionType = UITextAutocorrectionTypeNo;
typedef enum {
UITextAutocorrectionTypeDefault, 默认
UITextAutocorrectionTypeNo, 不自动纠错
UITextAutocorrectionTypeYes, 自动纠错
} UITextAutocorrectionType;
//再次编辑就清空
text.clearsOnBeginEditing = YES;
//内容对齐方式
text.textAlignment = UITextAlignmentLeft;
//内容的垂直对齐方式 UITextField继承自UIControl,此类中有一个属性contentVerticalAlignment
text.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
//设置为YES时文本会自动缩小以适应文本窗口大小.默认是保持原来大小,而让长文本滚动
textFied.adjustsFontSizeToFitWidth = YES;
//设置自动缩小显示的最小字体大小
text.minimumFontSize = 20;
//设置键盘的样式
text.keyboardType = UIKeyboardTypeNumberPad;
typedef enum {
UIKeyboardTypeDefault, 默认键盘,支持所有字符
UIKeyboardTypeASCIICapable, 支持ASCII的默认键盘
UIKeyboardTypeNumbersAndPunctuation, 标准电话键盘,支持+*#字符
UIKeyboardTypeURL, URL键盘,支持.com按钮 只支持URL字符
UIKeyboardTypeNumberPad, 数字键盘
UIKeyboardTypePhonePad, 电话键盘
UIKeyboardTypeNamePhonePad, 电话键盘,也支持输入人名
UIKeyboardTypeEmailAddress, 用于输入电子 邮件地址的键盘
UIKeyboardTypeDecimalPad, 数字键盘 有数字和小数点
UIKeyboardTypeTwitter, 优化的键盘,方便输入@、#字符
UIKeyboardTypeAlphabet = UIKeyboardTypeASCIICapable,
} UIKeyboardType;
//首字母是否大写
text.autocapitalizationType = UITextAutocapitalizationTypeNone;
typedef enum {
UITextAutocapitalizationTypeNone, 不自动大写
UITextAutocapitalizationTypeWords, 单词首字母大写
UITextAutocapitalizationTypeSentences, 句子的首字母大写
UITextAutocapitalizationTypeAllCharacters, 所有字母都大写
} UITextAutocapitalizationType;
//return键变成什么键
text.returnKeyType =UIReturnKeyDone;
typedef enum {
UIReturnKeyDefault, 默认 灰色按钮,标有Return
UIReturnKeyGo, 标有Go的蓝色按钮
UIReturnKeyGoogle,标有Google的蓝色按钮,用语搜索
UIReturnKeyJoin,标有Join的蓝色按钮
UIReturnKeyNext,标有Next的蓝色按钮
UIReturnKeyRoute,标有Route的蓝色按钮
UIReturnKeySearch,标有Search的蓝色按钮
UIReturnKeySend,标有Send的蓝色按钮
UIReturnKeyYahoo,标有Yahoo的蓝色按钮
UIReturnKeyYahoo,标有Yahoo的蓝色按钮
UIReturnKeyEmergencyCall, 紧急呼叫按钮
} UIReturnKeyType;
//键盘外观
textView.keyboardAppearance=UIKeyboardAppearanceDefault
typedef enum {
UIKeyboardAppearanceDefault, 默认外观,浅灰色
UIKeyboardAppearanceAlert, 深灰 石墨色
} UIReturnKeyType;
//设置代理 用于实现协议
text.delegate = self;
//把textfield加到视图中
[self.window addSubview:text];
//最右侧加图片是以下代码 左侧类似
UIImageView *image=[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"right.png"]];
text.rightView=image;
text.rightViewMode = UITextFieldViewModeAlways;
typedef enum {
UITextFieldViewModeNever,
UITextFieldViewModeWhileEditing,
UITextFieldViewModeUnlessEditing,
UITextFieldViewModeAlways
} UITextFieldViewMode;
//按return键键盘往下收 becomeFirstResponder
类要采用UITextFieldDelegate协议
text.delegate = self; 声明text的代理是我,我会去实现把键盘往下收的方法 这个方法在UITextFieldDelegate里所以我们要采用UITextFieldDelegate这个协议
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[text resignFirstResponder]; //主要是[receiver resignFirstResponder]在哪调用就能把receiver对应的键盘往下收
return YES;
}
重写绘制行为
除了UITextField对象的风格选项,你还可以定制化UITextField对象,为他添加许多不同的重写方法,来改变文本字段的显示行为。这些方法都会返回一个CGRect结构,制定了文本字段每个部件的边界范围。以下方法都可以重写。
– textRectForBounds:   //重写来重置文字区域
– drawTextInRect:   //改变绘文字属性.重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了.
– placeholderRectForBounds:  //重写来重置占位符区域
– drawPlaceholderInRect:  //重写改变绘制占位符属性.重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了.
– borderRectForBounds:  //重写来重置边缘区域
– editingRectForBounds:  //重写来重置编辑区域
– clearButtonRectForBounds:  //重写来重置clearButton位置,改变size可能导致button的图片失真
– leftViewRectForBounds:
– rightViewRectForBounds:
委托方法
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
//返回一个BOOL值,指定是否循序文本字段开始编辑
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField{
//开始编辑时触发,文本字段将成为first responder
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField{
//返回BOOL值,指定是否允许文本字段结束编辑,当编辑结束,文本字段会让出first responder
//要想在用户结束编辑时阻止文本字段消失,可以返回NO
//这对一些文本字段必须始终保持活跃状态的程序很有用,比如即时消息
return NO;
}
- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
//当用户使用自动更正功能,把输入的文字修改为推荐的文字时,就会调用这个方法。
//这对于想要加入撤销选项的应用程序特别有用
//可以跟踪字段内所做的最后一次修改,也可以对所有编辑做日志记录,用作审计用途。
//要防止文字被改变可以返回NO
//这个方法的参数中有一个NSRange对象,指明了被改变文字的位置,建议修改的文本也在其中
return YES;
}
- (BOOL)textFieldShouldClear:(UITextField *)textField{
//返回一个BOOL值指明是否允许根据用户请求清除内容
//可以设置在特定条件下才允许清除内容
return YES;
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
//返回一个BOOL值,指明是否允许在按下回车键时结束编辑
//如果允许要调用resignFirstResponder 方法,这回导致结束编辑,而键盘会被收起[textField resignFirstResponder];
//查一下resign这个单词的意思就明白这个方法了
return YES;
}
通知
UITextField派生自UIControl,所以UIControl类中的通知系统在文本字段中也可以使用。除了UIControl类的标准事件,你还可以使用下列UITextField类特有的事件
UITextFieldTextDidBeginEditingNotification
UITextFieldTextDidChangeNotification
UITextFieldTextDidEndEditingNotification
当文本字段退出编辑模式时触发。通知的object属性存储了最终文本。
因为文本字段要使用键盘输入文字,所以下面这些事件发生时,也会发送动作通知
UIKeyboardWillShowNotification //键盘显示之前发送
UIKeyboardDidShowNotification //键盘显示之后发送
UIKeyboardWillHideNotification //键盘隐藏之前发送
UIKeyboardDidHideNotification //键盘隐藏之后发送
UITextField 限制输入字数
UITextField 限制输入字数
方法一
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
if (range.location>= 11)
return NO;
returnYES;
}
方法二
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSString * toBeString = [textField.text stringByReplacingCharactersInRange:range withString:string];
if (toBeString.length > 11) {
textField.text = [toBeString substringToIndex:11];
return NO;
}
returnYES;
}
当前光标位置 range.location
已选文字长度 range.length
输入文字长度 textView.text.length
已有文字长度 text.length
————mark 消息机制侦听更改 ----
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(textFieldChanged:)
name:UITextFieldTextDidChangeNotification
object:textField];
searchText = UITextField(frame: CGRect(x: interval_W, y: logoImageView.center.y+logoImageView.bounds.height*1.5, width: stageWidth*(500/640), height: stageWidth*(70/640)))
searchText.layer.borderColor = violetColor.CGColor
searchText.layer.borderWidth = 1.0
searchText.layer.masksToBounds = true
searchText.placeholder = " 请输入舞曲或者舞队名称"
searchText.addTarget(self, action: "searchTextChange:", forControlEvents: UIControlEvents.EditingChanged)
searchimage = UIImageView(image: UIImage(named: "ser_serch.png"))
searchimage.center.x = searchimage.bounds.width
searchimage.center.y = searchText.bounds.height/2//searchimage.bounds.height/2
searchText.addSubview(searchimage)
searchText.delegate = self
searchText.clearButtonMode = UITextFieldViewMode.Always
searchText.returnKeyType = UIReturnKeyType.Search
self.view.addSubview(searchText)
-----
func searchTextChange(text:UITextField){
if text.text == nil || text.text == ""{
searchimage.hidden = false
}else{
searchimage.hidden = true
}
}
全局的取消键盘响应者
- (IBAction)View_TouchDown:(id)sender {
// 发送resignFirstResponder.
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
}
UIApplication.sharedApplication().sendAction("resignFirstResponder", to: nil, from: nil, forEvent: nil)

UITextField的placeholder文字的位置,颜色等的自定义设置

From: https://www.jianshu.com/p/615145fe3801

首先想到的是如果系统默认的没有能改变的,那就自定义添加不同的重写该控件的方法

我们有时需要定制化UITextField对象的风格,可以添加许多不同的重写方法,来改变文本字段的显示行为。这些方法都会返回一个CGRect结构,制定了文本字段每个部件的边界范围,甚至修改placeHolder颜色,字体。

– textRectForBounds:   //重写来重置文字区域

– drawTextInRect:   //改变绘文字属性.重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了.

– placeholderRectForBounds:  //重写来重置占位符区域

– drawPlaceholderInRect:  //重写改变绘制占位符属性.重写时调用super可以按默认图形属性绘制,若自己完全重写绘制函数,就不用调用super了

– borderRectForBounds:  //重写来重置边缘区域

– editingRectForBounds:  //重写来重置编辑区域

– clearButtonRectForBounds:  //重写来重置clearButton位置,改变size可能导致button的图片失真

– leftViewRectForBounds:

– rightViewRectForBounds:

通过– drawPlaceholderInRect:方法可改变placeHolder颜色、字体,请看代码:
首先定义一个类CustomTextField让它继承UITextField实现以下方法即可:

//控制清除按钮的位置
-(CGRect)clearButtonRectForBounds:(CGRect)bounds {
   return CGRectMake(bounds.origin.x + bounds.size.width - 50, bounds.origin.y + bounds.size.height -20, 16, 16);
}


//控制placeHolder的位置,左右缩20
-(CGRect)placeholderRectForBounds:(CGRect)bounds {

    //return CGRectInset(bounds, 20, 0);
   CGRect inset = CGRectMake(bounds.origin.x+100, bounds.origin.y, bounds.size.width -10, bounds.size.height);//更好理解些
   return inset;
}


//控制显示文本的位置
-(CGRect)textRectForBounds:(CGRect)bounds {
    //return CGRectInset(bounds, 50, 0);
    CGRect inset = CGRectMake(bounds.origin.x+190, bounds.origin.y, bounds.size.width -10, bounds.size.height);//更好理解些
    return inset;
}


//控制编辑文本的位置
-(CGRect)editingRectForBounds:(CGRect)bounds {
    //return CGRectInset( bounds, 10 , 0 );
   CGRect inset = CGRectMake(bounds.origin.x +10, bounds.origin.y, bounds.size.width -10, bounds.size.height);
   return inset;
}


//控制左视图位置
- (CGRect)leftViewRectForBounds:(CGRect)bounds {
   CGRect inset = CGRectMake(bounds.origin.x +10, bounds.origin.y, bounds.size.width-250, bounds.size.height);
   return inset;
    //return CGRectInset(bounds,50,0);
}


//控制placeHolder的颜色、字体
- (void)drawPlaceholderInRect:(CGRect)rect
{
    //CGContextRef context = UIGraphicsGetCurrentContext();
    //CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
    [[UIColororangeColor] setFill];

    [[selfplaceholder] drawInRect:rectwithFont:[UIFontsystemFontOfSize:20]];
}

//下面是使用CustomTextField的代码,可放在viewDidLoad等方法中
_textField = [[CustomTextField alloc] initWithFrame:CGRectMake(20, 150, 280, 30)];
_textField.placeholder = @”手机号”;
_textField.borderStyle = UITextBorderStyleRoundedRect;
_textField.textAlignment = UITextAlignmentLeft;
_textField.delegate = self;
_textField.clearButtonMode = UITextFieldViewModeWhileEditing;
//_textField.text = @”随便”;
UIImageView *imgv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@”anyImage.png”]];
_textField.leftView = imgv;
_textField.leftViewMode = UITextFieldViewModeAlways;
[self.view addSubview:_textField];

IOS 运作处理器数据的获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
——————————————————iOS 运作处理器数据的获取 —————步数获取------------------
UILabel *label =[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 320, 568/2)];
[label setBackgroundColor:[UIColor grayColor]]; label.textColor =[UIColor redColor];
label.textAlignment = NSTextAlignmentCenter;
[self.view addSubview:label];
if ([CMStepCounter isStepCountingAvailable])
{
NSLog(@"isStepCountingAvailable!!!");
NSOperationQueue *queueStep = [[NSOperationQueue alloc] init] ; //20步数,更新一次
[_cmStepCounter startStepCountingUpdatesToQueue:queueStep updateOn:20 withHandler:^(NSInteger numberOfSteps, NSDate *timestamp, NSError *error) { NSLog(@"numberOfSteps==%ld,timestamp==%@",(long)numberOfSteps,timestamp);
_steps = numberOfSteps;
label.text = [NSString stringWithFormat:@"%ld",_steps];
}];
}else{
NSLog(@"isNOT StepCountingAvailable");
}
- (IBAction)doSomeThingsAction:(id)sender {
__block NSInteger stepsNumber = 0;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate *tempDate = [dateFormatter dateFromString:@"2014-08-31 20:20:20"];
NSOperationQueue *queueStep = [[NSOperationQueue alloc] init] ;
[_stepManager queryStepCountStartingFrom:tempDate to:[NSDate date] toQueue:queueStep withHandler:^(NSInteger numberOfSteps, NSError *error)
{
if (!error)
{
stepsNumber = numberOfSteps;
dispatch_async(dispatch_get_main_queue(), ^{
self.countLabel.text = [NSString stringWithFormat:@"%ld",stepsNumber];
});
}else{
NSLog(@"%@",error);
} }];
[_activityManager queryActivityStartingFromDate:tempDate toDate:[NSDate date] toQueue:queueStep withHandler:^(NSArray *activities, NSError *error) {
if (!error) {
dispatch_async(dispatch_get_main_queue(), ^{ CMMotionActivity *tempActivity = [activities objectAtIndex:0]; NSLog(@"%@",tempActivity);
});
}else{ NSLog(@"%@",error); } }]; }
————2
在ios8中,ios7新增加的计步器CMStepCounter已经过时,而用CMPedometer代替。
具体代码(CMStepCounter)
if ([CMStepCounterisStepCountingAvailable]) {
self.stepCounter=[[CMStepCounter alloc]init];
NSOperationQueue *queue=[[NSOperationQueue alloc]init];
[self.stepCounter startStepCountingUpdatesToQueue:queue updateOn:5 withHandler:^(NSInteger numberOfSteps, NSDate *_Nonnull timestamp, NSError * _Nullable error) {
self.stepLabel.text=[NSString stringWithFormat:@"用户已经行走了【%d】步!",numberOfSteps];
}];
}
(CMPedometer)
if ([CMPedometer isStepCountingAvailable]) {
self.pedometer=[[CMPedometer alloc]init];
[self.pedometer startPedometerUpdatesFromDate:[NSDate date] withHandler:^(CMPedometerData *_Nullable pedometerData, NSError * _Nullable error) {
CMPedometerData *data=(CMPedometerData *)pedometerData;
NSNumber *number=data.numberOfSteps;
self.stepLabel.text=[NSString stringWithFormat:@"用户已经行走了【%@】步!",number];
}];
}
————————————————可以实现的时间间隔 获取步数 --------
//ios8
if
([CMStepCounter
isStepCountingAvailable])
{
CMStepCounter* stepCounter=[[CMStepCounter alloc]init];
NSOperationQueue *queue=[[NSOperationQueue alloc]init];
NSDate * newdate = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSUIntegerMax fromDate:newdate];
components.hour = 0;
components.minute = 0;
components.second = 0;
// components.nanosecond = 0 not available in iOS
NSTimeInterval ts = (double)(int)[[calendar dateFromComponents:components] timeIntervalSince1970];
NSDate * olddate = [NSDate dateWithTimeIntervalSince1970:ts];//[NSDate dateWithTimeInterval:-24*60*60
// 时间间隔获取步数
[stepCounter queryStepCountStartingFrom:olddate to:newdate toQueue:queue withHandler:^(NSInteger numberOfSteps, NSError * _Nullable error) {
NSLog(@"3行走了多少步%ld",(long)numberOfSteps)//成功, 时间转换下正常就可以
}];
// [stepCounter startStepCountingUpdatesToQueue:queue updateOn:5 withHandler:^(NSInteger numberOfSteps, NSDate * _Nonnull timestamp, NSError * _Nullable error) {
//
// NSLog(@"2行走了多少步%ld",(long)numberOfSteps)
//
// }];
}
//ios7
if ([CMPedometer isStepCountingAvailable]) {
CMPedometer* pedometer=[[CMPedometer alloc]init];
NSDate * newdate = [NSDate date];
NSDate * olddate = [NSDate dateWithTimeInterval:-24*60*60 sinceDate:newdate];
// 时间间隔获取步数
[pedometer queryPedometerDataFromDate:olddate toDate:newdate withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
CMPedometerData *data=(CMPedometerData *)pedometerData;
NSNumber *number=data.numberOfSteps;
NSLog(@"4行走了多少步%ld",(long)number)
}];
// [pedometer startPedometerUpdatesFromDate:[NSDate date] withHandler:^(CMPedometerData * _Nullable pedometerData, NSError * _Nullable error) {
//
// CMPedometerData *data=(CMPedometerData *)pedometerData;
//
// NSNumber *number=data.numberOfSteps;
//
// NSLog(@"2行走了多少步%ld",(long)number)
//
// }];
}

格式化时间

//格式化时间

1
2
3
4
5
6
7
8
9
10
var formatter:NSTimeInterval = NSTimeInterval()
// formatter.stringForObjectValue(thetime)
formatter = 1420790950.580
var str:NSDate = NSDate(timeIntervalSince1970: formatter)
let df = NSDateFormatter()
df.dateFormat="yyyy-M-d” // yyyy.MM.dd HH:mm:ss
var ti = df.stringFromDate(str)
println(" 222 \(ti)")