iOS 枚举的巧用

From: https://www.jianshu.com/p/97e582fe89f3

前言

在之前的一篇文章中简单的提到了这个问题, 但是自己写的不详细, 并且自己深入了解的也不是特别多, 在开发中也没怎么用到,所以经过阅读者的反馈对这个问题很是疑惑! 本篇文章会分析之前的不足之处, 如果有地方不对还请帮忙纠正!

相关文章: iOS开发中你是否遇到这些经验问题(二)

1.使用层面的理解

在这里首先讲如何简单的使用, 仅仅是使用层面(有理解错误的地方帮忙纠正), 然后我们在去理解位运算符! 在下面的图中我们可以看见枚举值中有<<(位运算符:左移):

如果我们在枚举值中看见<<那我们就可以通过|(位运算符:或)进行组合使用如下代码为例:

//随便添加一个UITextField
UITextField *field = [UITextField new];
//Begin,Changed,DidEnd都能触发UITextField的事件
[field addTarget:self action:@selector(textFieldDidChanged) forControlEvents: UIControlEventEditingDidBegin |
                 UIControlEventValueChanged |
                 UIControlEventEditingDidEnd
    ];

[self.view addSubview:field];

如下图枚举值中没有<<,这就是普通的NSInteger类型的枚举, 所以不能组合使用:

那苹果官方是怎么知道我们多个条件组合使用了呢? 答案是通过&(位运算符:与)进行判断的:

//controlEvents是组合使用后的一个值
 NSUInteger controlEvents = UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd;
    /**
    //通过 & 来判断是否包含:
    UIControlEventEditingDidBegin,
    UIControlEventValueChanged,
    UIControlEventEditingDidEnd
     */
    if (controlEvents & UIControlEventEditingDidBegin) {

        NSLog(@"UIControlEventEditingDidBegin");

    }else if (controlEvents & UIControlEventValueChanged) {

        NSLog(@"UIControlEventValueChanged");

    }else if (controlEvents & UIControlEventEditingDidEnd) {

        NSLog(@"UIControlEventEditingDidEnd");
    }

那么我们接下来看看使用过程中牵扯到的位运算符, 我们会在下面举个例子!

2.理解位运算符

首先我们有一个枚举, 下面代码2种写法我们暂时先不用管,等位运算符讲完我们会讨论枚举的宏使用:

//typedef NS_OPTIONS(NSInteger, myTests) {
//    nameA = 1 << 0,
//    nameB = 1 << 1,
//    nameC = 1 << 2,
//    nameD = 1 << 3,
//};

typedef enum {
    nameA = 1 << 0,
    nameB = 1 << 1,
    nameC = 1 << 2,
    nameD = 1 << 3,

}myTests;

/**
 nameA = 1 << 0 :值为1(2的0次方)
 nameB = 1 << 1 :值为2(2的1次方)
 nameC = 1 << 2 :值为4(2的2次方)
 nameD = 1 << 3 :值为8(2的3次方)
 */

通过&进行判断我们来看看输出结果如下图:

我们得到NSInteger value = nameA | nameB;的组合的值, 判断结果是:1nameA的值, 2nameB的值, nameCnameD没有组合使用所以值为0,最后我们知道如果value & nameC0说明value不包含nameC 相反则包含!

还有一点就是value & nameA就是nameA的值为1, value & nameB就是nameB的值为2

  • <<(左移):a << b就表示把a转为二进制后左移b位(在后面添b0
  • |(或):只要有一个为1, 结果就是1
  • &(与):只要有二个为1, 结果才是1

我们已经知道nameA = 1, nameB = 2, nameC = 4, nameD = 8下面来通过二进制来解释:

NSInteger value = nameA | nameB | nameC | nameD;
    转成二进制:
    nameA: 0 0 0 1
      |
    nameB: 0 0 1 0
      |
    nameC: 0 1 0 0
      |
    nameD: 1 0 0 0
   ----------------
    value: 1 1 1 1
    上面是使用 | 得出value的值为1111(|的意思是有一个为1结果就为1)


    下面是使用 & 判断输出的值(&的意思就是有二个为1结果才为1)

     value: 1 1 1 1         value: 1 1 1 1
       &                      &
     nameA: 0 0 0 1         nameB: 0 0 1 0
    ----------------       ----------------
     结果值: 0 0 0 1         结果值: 0 0 1 0

     我就写2个例子:0001就是nameA的值, 0010就是nameB的值

相信大家已经明白其中的道理了, 接下来我们来看看枚举的宏, 为了更好阅读也可以看下面的截图:

3.枚举的宏(NS_ENUMNS_OPTIONS)

NS_ENUMNS_OPTIONS宏提供了一个简洁、定义枚举和C语言选项的简单方法。

The NS_ENUM and NS_OPTIONS macros provide a concise, simple way of defining enumerations and options in C-based languages. These macros improve code completion in Xcode and explicitly specify the type and size of your enumerations and options. Additionally, this syntax declares enums in a way that is evaluated correctly by older compilers, and by newer ones that can interpret the underlying type information.

这是最初的使用方法:

enum {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};
typedef NSInteger UITableViewCellStyle;

--------------------------------------------------

enum {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;

通过使用枚举的宏:

NS_ENUM:是用来声明一般的NSInteger(下面代码使用NSInteger)类型的枚举

Use the NS_ENUM macro to define enumerations, a set of values that are mutually exclusive.

NS_OPTIONS:是用来声明位掩码(bitmasked)

Use the NS_OPTIONS macro to define options, a set of bitmasked values that may be combined together.

//NS_ENUM
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
        UITableViewCellStyleDefault,
        UITableViewCellStyleValue1,
        UITableViewCellStyleValue2,
        UITableViewCellStyleSubtitle
};

--------------------------------------------------

//NS_OPTIONS
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

NS_OPTIONSNS_ENUMenum 是有什么区别呢?

1.通过上面介绍我们可以看出enum可以声明一般类型和位掩码(bitmasked)类型

2.NS_ENUM声明一般类型, NS_OPTIONS声明掩码(bitmasked)类型

3.那么问题又来了, 直接用enum不就可以了? 答案不是这样的, 苹果建议我们在OC中使用NS_ENUMNS_OPTIONS, 为什么呢? 因为他们除了推断出不同类型的枚举,再就是当编译Objective-C++模式,它们产生的代码是不同的, 就是因为不同所以混编的时候使用enum会报错!
大家可以看看stackoverflow中的问题解答!不知道我的理解是否有错误, 如果有错误还希望帮忙纠正!

iOS 内购

From: http://blog.csdn.net/u014220518/article/details/55253275

一直觉得苹果生态圈很安全,iOS操作系统也很流畅,而且软件有严格的审核机制,软件来源唯一(当然是你不越狱),但是这也为苹果的霸权铺路;上家公司做了一个APP,可以充值虚拟金币,但是如果是虚拟道具,就必须使用苹果内购,不然审核过不了,而且很黑,三七分;当然,你如果购买真是东西,比如淘宝、京东等等就不需要了!这里我就来说说苹果内购流程,附上图文教程,希望大家喜欢!

1.填写协议

2.请求合同

3.填写地址

4.阅读并同意协议

5.填写联系方式

6.添加地址

7.选择设置地址

8.继续填写第二个

9.绑定银行卡和选择添加

10.选择国家

11.添加国际银行标识

注意:查询银行标识的方法的地址如下。

https://e.czbank.com/CORPORBANK/query_unionBank_index.jsp

12.银行卡信息

13.填写最后一项

14.内容比较多

15.一堆条约

16.继续填写

17.核对信息

18.继续

19.澳大利亚的不要管了

20.加拿大的也不用管了

21.填写完成

1.添加内购项目

2.选择内购类型

3.根据自己APP的需求选择类型

4.继续

5.商品列表

6.选择APP商品

1.选择用户职能

2.添加测试人员

![][60]

[60]:

1.先导入StoreKit.framework库;

2.创建AppPayManager管理类,遵循协议

3.创建单利和支付方法

#import <Foundation/Foundation.h>
#import <StoreKit/StoreKit.h>

@interface AppPayManager : NSObject

//个人信息单例
+ (AppPayManager *)manager;

- (void)buyProductsWithId:(NSString *)productsId andQuantity:(NSInteger)quantity;

@end

4.程序启动添加SKPay观察者,同事像我们的服务器请求商品列表

//1. 程序启动添加SKPay观察者,同时像后端请求产品列表信息
- (void)launch {
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    [self requestProductList];
}

- (void)requestProductList{
    NSLog(@"requestProductList");
}

5.程序结束移除观察者

- (void)terminate {
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

6.根据商品ID进行购买

- (void)buyProductsWithId:(NSString *)productsId andQuantity:(NSInteger)quantity {
    self.productsId = productsId;
    self.quantity = quantity;
    if ([SKPaymentQueue canMakePayments]) {
        //允许程序内付费购买
        [self RequestProductData:@[self.productsId]];
    } else {
        //您的手机没有打开程序内付费购买
        UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"您的手机没有打开程序内付费购买" message:nil delegate:nil cancelButtonTitle:@"关闭" otherButtonTitles:nil];
        [alerView show];
    }
}

7.根据商品ID数组,SKProductsRequest请求购买

- (void)RequestProductData:(NSArray *)productsIdArr {
    //请求对应的产品信息
    NSSet *nsset = [NSSet setWithArray:productsIdArr];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    [request start];
}

8.SKProductsRequestDelegate 会接收到请求响应,在此回调中,发送购买请求

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    //收到产品反馈信息
    NSArray *myProduct = response.products;
    NSLog(@"产品Product ID:%@", response.invalidProductIdentifiers);
    NSLog(@"产品付费数量: %d", (int) [myProduct count]);
    // populate UI
    for (SKProduct *product in myProduct) {
        //        NSLog(@"product info");
        //        NSLog(@"  基本描述: %@", [product description]);
        //        NSLog(@"  IAP的id: %@", product.productIdentifier);
        //        NSLog(@"  地区编码: %@", product.priceLocale.localeIdentifier);
        //        NSLog(@"  本地价格: %@", product.price);
        //        NSLog(@"  语言代码: %@", [product.priceLocale objectForKey:NSLocaleLanguageCode]);
        //        NSLog(@"  国家代码: %@", [product.priceLocale objectForKey:NSLocaleCountryCode]);
        //        NSLog(@"  货币代码: %@", [product.priceLocale objectForKey:NSLocaleCurrencyCode]);
        //        NSLog(@"  货币符号: %@", [product.priceLocale objectForKey:NSLocaleCurgegrencySymbol]);
        //        NSLog(@"  本地标题: %@", product.localizedTitle);
        //        NSLog(@"  本地描述: %@", product.localizedDescription);
        [self updateProductPriceWithId:product.productIdentifier andPrice:product.price];
        if ([[product.priceLocale objectForKey:NSLocaleCurrencyCode] isEqualToString:@"CNY"]) {
            self.currencyCode = @"¥";
        } else {
            self.currencyCode = [product.priceLocale objectForKey:NSLocaleCurrencySymbol];
        }
    }
    //发送购买请求
    for (SKProduct *prct in myProduct) {
        if ([self.productsId isEqualToString:prct.productIdentifier]) {
            SKMutablePayment *payment = nil;
            payment = [SKMutablePayment paymentWithProduct:prct];
            payment.quantity = self.quantity;
            [[SKPaymentQueue defaultQueue] addPayment:payment];
        }
    }
}

- (void)updateProductPriceWithId:(NSString *)productIdentifier andPrice:(NSDecimalNumber *)price{
    NSLog(@"productIdentifier == %@",productIdentifier);
    NSLog(@"price == %@",price);
}

9.SKPaymentTransactionObserver 此协议会监听到购买结果,根据购买结果的不同,做出不同的逻辑

#pragma mark - SKPaymentTransactionObserver
//----监听购买结果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    //交易结果
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased: {
                //交易完成
                [self completeTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateFailed: {
                //交易失败
                [self failedTransaction:transaction];
                UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"交易失败" message:nil delegate:nil cancelButtonTitle:@"关闭" otherButtonTitles:nil];
                [alerView show];
            }
                break;
            case SKPaymentTransactionStateRestored: {
                //已经购买过该商品
                [self restoreTransaction:transaction];
                UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"已经购买过该商品" message:nil delegate:nil cancelButtonTitle:@"关闭" otherButtonTitles:nil];
                [alerView show];
            }
                break;
            case SKPaymentTransactionStatePurchasing: {
                //商品添加进列表
                NSLog(@"商品添加进列表");
            }
                break;
            case SKPaymentTransactionStateDeferred: {
                NSLog(@"SKPayment Transaction State Deferred");
            }
                break;
            default:
                break;
        }
    }
}
- (void)failedTransaction: (SKPaymentTransaction *)transaction{
    NSLog(@"失败");
    if (transaction.error.code != SKErrorPaymentCancelled) { }
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

- (void)restoreTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"交易恢复处理");
}

9.购买成功后,将信息上传自己的服务器

- (void)completeTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"-----completeTransaction--------");
    NSString *product = transaction.payment.productIdentifier;
    if ([product length] > 0) {
        NSArray *tt = [product componentsSeparatedByString:@"."];
        NSString *bookid = [tt lastObject];
        if ([bookid length] > 0) {
            [self recordTransaction:bookid];
            [self provideContent:bookid];}
    }
}

//记录交易
- (void)recordTransaction:(NSString *)product{
    NSLog(@"记录交易--product == %@",product);
}

//处理下载内容
- (void)provideContent:(NSString *)product{
    NSLog(@"处理下载内容--product == %@",product);
}

希望对你有所帮助!

iOS11及Xcode9适配问题汇总


“自动语言版本升级 弹出 含有 swift2.x 语言 需要在xcode 8 中升级后在转换”
然后莫名了,并没有啊,感觉, (xcode 8 语言共存 )
然后观察项目设置 发现

1 PROJECT 中的Swift Language Version - > 设置成 Unspecified
2 TAEGETS 中 Swift Language Version -> 设置成 swift3.2(老版本) 然后edit 菜单中转换 end


From: https://www.lee1994.com/ios11ji-xcode9gua-pei-wen-ti-hui-zong/

UIScrollView and UITableView的新特性

ScrollView

如果有一些文本位于UI滚动视图的内部,并包含在导航控制器中,现在一般navigationContollers会传入一个contentInset给其最顶层的viewController的scrollView,在iOS11中进行了一个很大的改变,不再通过scrollView的contentInset属性了,而是新增了一个属性:adjustedContentInset,通过下面两种图的对比,能够表示adjustContentInset表示的区域:

新增的contentInsetAdjustmentBehavior属性用来配置adjustedContentInset的行为,该结构体有以下几种类型:

typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {  
    UIScrollViewContentInsetAdjustmentAutomatic, 
    UIScrollViewContentInsetAdjustmentScrollableAxes,
    UIScrollViewContentInsetAdjustmentNever,
    UIScrollViewContentInsetAdjustmentAlways,
}

@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;
@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset;

//adjustedContentInset值被改变的delegate
- (void)adjustedContentInsetDidChange; 
- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView;

UIScrollViewContentInsetAdjustmentBehavior 是一个枚举类型,值有以下几种:

  • automatic 和scrollableAxes一样,scrollView会自动计算和适应顶部和底部的内边距并且在scrollView 不可滚动时,也会设置内边距.
  • scrollableAxes 自动计算内边距.
  • never不计算内边距
  • always 根据safeAreaInsets 计算内边距

TableView

1.UITableview UICollectionView MJRefresh下拉刷新错乱的问题

if (@available(iOS 11.0, *)) {
    _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    _tableView.contentInset = UIEdgeInsetsMake(64, 0, 49, 0);//iPhoneX这里是88
    _tableView.scrollIndicatorInsets = _tableView.contentInset;
}

2.在iOS 11中默认启用Self-Sizing 未使用AutoLayout的TableView中的高度会出现问题.

Self-Sizing在iOS11下是默认开启的,Headers, footers, and cells都默认开启Self-Sizing,所有estimated 高度默认值从iOS11之前的 0 改变为UITableViewAutomaticDimension.

如果目前项目中没有使用estimateRowHeight属性,在iOS11的环境下就要注意了,因为开启Self-Sizing之后,tableView是使用estimateRowHeight属性的,这样就会造成contentSizecontentOffset值的变化,如果是有动画是观察这两个属性的变化进行的,就会造成动画的异常,因为在估算行高机制下,contentSize的值是一点点地变化更新的,所有cell显示完后才是最终的contentSize值。因为不会缓存正确的行高,tableView reloadData的时候,会重新计算contentSize,就有可能会引起contentOffset的变化。iOS11下不想使用Self-Sizing的话,可以通过以下方式关闭:

self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;

3.TableView的separatorInset扩展

iOS 7 引入separatorInset属性,用以设置 cell 的分割线边距,在 iOS 11 中对其进行了扩展。可以通过新增的UITableViewSeparatorInsetReference枚举类型的separatorInsetReference属性来设置separatorInset属性的参照值.

通过下面的参考图可以看出他们的区别:

4. TableView和SafeArea(安全区)

有以下几点需要注意:

  • separatorInset 被自动地关联到 safe area insets,因此,默认情况下,表视图的整个内容避免了其根视图控制器的安全区域的插入。
  • UITableviewCellUITableViewHeaderFooterViewcontentview 在安全区域内;因此你应该始终在 contentview 中使用add-subviews操作。
  • 所有的 headers 和 footers 都应该使用UITableViewHeaderFooterView,包括 table headers 和 footers、section headers 和 footers。

5. TableView的滑动操作

在iOS8之后,苹果官方增加了UITableVIew的右滑操作接口,即新增了一个代理方法tableView: editActionsForRowAtIndexPath:和一个类UITableViewRowAction,代理方法返回的是一个数组,我们可以在这个代理方法中定义所需要的操作按钮(删除、置顶等),这些按钮的类就是UITableViewRowAction。这个类只能定义按钮的显示文字、背景色、和按钮事件。并且返回数组的第一个元素在UITableViewCell的最右侧显示,最后一个元素在最左侧显示。从iOS 11开始有了一些改变,首先是可以给这些按钮添加图片了,然后是如果实现了以下两个iOS 11新增的代理方法,将会取代tableView: editActionsForRowAtIndexPath:代理方法:

这两个代理方法返回的是UISwipeActionsConfiguration类型的对象,创建该对象及赋值可看下面的代码片段:

- ( UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
    //删除
    UIContextualAction *deleteRowAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:@"delete" handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
        [self.titleArr removeObjectAtIndex:indexPath.row];
        completionHandler (YES);
    }];
    deleteRowAction.image = [UIImage imageNamed:@"icon_del"];
    deleteRowAction.backgroundColor = [UIColor blueColor];

    UISwipeActionsConfiguration *config = [UISwipeActionsConfiguration configurationWithActions:@[deleteRowAction]];
    return config;
}


typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
    UIContextualActionStyleNormal,
    UIContextualActionStyleDestructive
} NS_SWIFT_NAME(UIContextualAction.Style)

创建UIContextualAction对象时,UIContextualActionStyle有两种类型,如果是置顶、已读等按钮就使用UIContextualActionStyleNormal类型,delete操作按钮可使用UIContextualActionStyleDestructive类型,当使用该类型时,如果是右滑操作,一直向右滑动某个cell,会直接执行删除操作,不用再点击删除按钮,这也是一个好玩的更新.

typedef NS_ENUM(NSInteger, UIContextualActionStyle) {
    UIContextualActionStyleNormal,
    UIContextualActionStyleDestructive
} NS_SWIFT_NAME(UIContextualAction.Style)

滑动操作这里还有一个需要注意的是,当cell高度较小时,会只显示image,不显示title,当cell高度够大时,会同时显示image和title。我写demo测试的时候,因为每个cell的高度都较小,所以只显示image,然后我增加cell的高度后,就可以同时显示image和title了。见下图对比:

iOS11中 UIKit’s Bars 上的变化

WWDC通过iOS新增的文件管理App:Files开始介绍,在Files这个APP中能够看到iOS11中UIKit’s Bars的一些新特性:在浏览功能上的大标题视图(向上滑动后标题会回到原来的UI效果)、横屏状态下tab上的文字和icon会变为左右排列:

竖屏

横屏

在iPhone上,tab上的图标较小,tab bar较小,这样垂直空间可多放置内容。如果有人看不清楚tab bar上的图标或文字,可以通过长按tab bar上的任意item,会将该item显示在HUD上,这样可以清楚的看清icon和text。对tool bar 和 navigation bar同理,长按item也会放大显示.

  • UIBarItem

UIBarItem是UI tab bar item和UI bar button item的父类,要想实现上面介绍的效果,只需要为UIBarItem 设置landscapeImagePhone属性,在storyboard中也支持这个设置,对于HUD的image需要设置另一个iOS11新增的属性:largeContentSizeImage,关于这部分更详细的讨论,可以参考 WWDC2017 Session 215:What’s New in Accessibility

  • 控制大标题的显示

UINavigationbar中新增了一个BOOL属性prefersLargeTitles,将该属性设置为ture,navigationbar就会在整个APP中显示大标题,如果想要在控制不同页面大标题的显示,可以通过设置当前页面的navigationItemlargeTitleDisplayMode属性.

navigationItem.largeTitleDisplayMode 

typedef NS_ENUM(NSInteger, UINavigationItemLargeTitleDisplayMode) {  
/// 自动模式依赖上一个 item 的特性
UINavigationItemLargeTitleDisplayModeAutomatic,
/// 针对当前 item 总是启用大标题特性
UINavigationItemLargeTitleDisplayModeAlways,
/// Never 
UINavigationItemLargeTitleDisplayModeNever,
}

把你的UISearchController赋值给navigationItem,就可以实现将UISearchController集成到 Navigation.

navigationItem.searchController  //iOS 11 新增属性
navigationItem.hidesSearchBarWhenScrolling //决定滑动的时候是否隐藏搜索框;iOS 11 新增属性

滚动的时候,以下交互操作都是由UINavigationController负责调动的:

UIsearchController搜索框效果更新
大标题效果的控制
Rubber banding效果 //当你开始往下拉,大标题会变大来回应那个滚轮

所以,如果你使用navigation bar,组装一些整体push和pop体验,你不会得到searchController的集成、大标题的控制更新和Rubber banding效果,因为这些都是由UINavigationController控制的。

Margins 和 Insets

基于约束的Auto Layout, 使我们搭建能够动态响应内部和外部变化的用户界面. Auto Layout为每一个view都定义了margin. margin指的是控件显示内容部分的边缘和控件边缘的距离.
可以用layoutMargins或者layoutMarginsGuide属性获得viewmargin, margin是视图内部的一部分. layoutMargins允许获取或者设置UIEdgeInsets结构的margin. layoutMarginsGuide则获取到只读的UILayoutGuide对象.

在iOS11新增了一个属性:directional layout margins,该属性是NSDirectionalEdgeInsets结构体类型的属性:

typedef struct NSDirectionalEdgeInsets {  
    CGFloat top, leading, bottom, trailing;
} NSDirectionalEdgeInsets API_AVAILABLE(ios(11.0),tvos(11.0),watchos(4.0));

layoutMarginsUIEdgeInsets结构体类型的属性:

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

从上面两种结构体的对比可以看出, NSDirectionalEdgeInsets属性用leadingtrailing 取代了之前的 leftright.

directional layout margins属性的说明如下:

directionalLayoutMargins.leading is used on the left when the user interface direction is LTR and on the right for RTL.
Vice versa for directionalLayoutMargins.trailing.

例子: 当你设置了trailing = 30; 当在一个right to left 语言下trailing的值会被设置在view的左边, 可以通过layoutMarginleft属性读出该值. 如下图所示:

还有其他一些更新. 自从引入layout margins, 当将一个view添加到viewController时, viewController会修改viewlayoutMargins为UIKit定义的一个值, 这些调整对外是封闭的. 从iOS11开始, 这些不再是一个固定的值, 它们实际是最小值, 你可以改变viewlayoutMargins为任意一个更大的值. 而且, viewController新增了一个属性: viewRespectsSystemMinimumLayoutMargins, 如果你设置该属性为false, 你就可以改变你的layoutMargins为任意你想设置的值, 包括0, 如下图所示:

iPhoneX相关适配

参考

Creating apps for iPhone X.

Updating Your App for iOS 11 - WWDC 2017 - Session 204 - iOS

你可能需要为你的APP适配iOS11

关于iPhone X 的适配

iPhone X 适配大集合 https://github.com/2877025939/iOS11

From: https://www.lee1994.com/guan-yu-iphone/

1.屏幕尺寸相关变化

  1. 高度增加了145pt,变成812pt.
  2. 屏幕圆角显示,注意至少留10pt边距。
  3. 状态栏高度由20pt变成44pt,留意这个距离就能避开“刘海”的尴尬,相应的导航栏以上变化64->88。
  4. 底部工具栏需要为home indicator留出34pt边距。
  5. 物理分辨率为1125px * 2436px.

2.横竖屏安全区对比

3.其他设备安全区域对比

4.应用设计

5.控件布局

更多可查看官方文档和视频Creating apps for iPhone X.

如何让APP适配?

APP启动样式适配

相信有一部分道友的APP在iPhone X上运行时并没有像想象中那样占满整个屏幕, 而是保持着原有的高度 在屏幕中心位置, 针对这个问题 目前经过实验得出可以通过以下方式使APP按照全屏模式运行:

  1. 通过LaunchScreen.storyboard方式启动
  2. 如果使用的是Assets中的LaunchImage, 在增加了iPhone X尺寸的图片配置后.

LaunchScreen.storyboard方式不用多说, 这里说一下如何在LaunchImage中增加iPhone X尺寸的图片配置.

准备一张尺寸:1125 * 2436的启动图片, 移动到LaunchImage的Finder目录中, 并在LaunchImage中的Contents.json文件中增加 (注意Json格式):

{
    "extent" : "full-screen",
    "idiom" : "iphone",
    "subtype" : "2436h",
    "filename" : "图片名.png",
    "minimum-system-version" : "11.0",
    "orientation" : "portrait",
    "scale" : "3x"
}

按照以上方式配置就完全解决了这个问题.

APP内部样式适配

iOS11为UIViewControllerUIView增加了两个新的属性safeAreaInsetssafeAreaLayoutGuide, 通过这两个属性我们可以获得安全区域的范围, 通过上图可以很清楚的看到安全区域的范围, 我们要做的是让那些不能被遮挡的内容和控件在安全区域范围内显示, 注意!这句话非常重要.

  • safeAreaInsets 适用于手动计算.
  • safeAreaLayoutGuide 适用于自动布局.

Frame布局方式适配示例

一般来说 可以通过在原有视图坐标计算时加入安全区域的范围值, 下面举个例子:

一个APP 它的NavigationBar使用的是自定义的UIView, 并非UINavigationBar, 这个自定义的UIViewframe属性为CGRectMake(0, 0, 375, 64).
这样的UIView在其他设备上是没有问题的, 标准的导航栏尺寸, 但是如果运行在iPhone X上 你会发现看上去无比的别扭, 因为异形屏幕会造成部分显示内容的遮挡问题, 这时候就要用到安全区域这个概念来解决这一问题.

先说一说通常自定义导航栏的结构, 高度是为6444高度的导航栏内容区域和20高度的状态栏区域(电池条那部分 status bar)组成, 44高度的导航栏内容区域用来显示导航栏上的控件, 如返回按钮 标题视图等等, 现在, 在iPhone X上, 原来的状态栏(status bar)高度不再考虑了, 取而代之的是安全区域顶部间距safeAreaInsets.top, 我们将原有的结构改为 安全区域顶部距离屏幕的距离safeAreaInsets.top + 导航栏内容区域44, 这样完成了一个自定义导航栏视图在iPhone X上的适配.

下面是适配前后的效果对比:

再来看一下适配后的自定义导航栏视图的UI结构

这只是举一个简单的例子, 因为不同的应用设计都是不同的, 但是适配的思路都是一样的, 还是那句话 我们要做的是让那些不能被遮挡的内容和控件在安全区域范围内显示, 在计算布局时 将安全区域这个新特性考虑进去, iPhone X的适配也不是难事.

说一下在代码中的安全区域的适配该如何去写:

iOS11 为UIViewControllerUIView增加了一个新的方法 - (void)viewSafeAreaInsetsDidChange;

这个方法如名字一样 在安全区域改变后会被调用, 我们可以在需要适配的UIViewControllerUIView中重写这个方法, 并且在这个方法中来根据safeAreaInsets属性更新子视图控件的布局位置.

这里有一点要注意的是当UIViewController调用- (void)viewDidLoad时它的所有子视图的safeAreaInsets属性都等于UIEdgeInsetsZero, 也就是说在- (void)viewSafeAreaInsetsDidChange;方法调用前 是无法通过当前视图控制器的子视图获取到safeAreaInsets的, 不过获取当前window对象的safeAreaInsets属性用来计算也是可以的, 但是不建议这么做, 一个视图控制器的子视图的处理当然要以它所在的控制器为准.

- (void)viewSafeAreaInsetsDidChangeUIViewController中第一次调用的时间是在- (void)viewWillAppear:(BOOL)animated调用之后, 在- (void)viewWillLayoutSubviews调用之前.

当然如果你要改变一个UIViewControllersafeAreaInsets值, 可以通过设置addtionalSafeAreaInsets属性来实现, 例如你要自定义一些特殊的样式时.

如果你改变了一个UIViewControllersafeAreaInsets属性值, - (void)viewSafeAreaInsetsDidChange也会被调用.

另外, 给道友们的分享一个获取某View安全区域范围的宏

#define VIEWSAFEAREAINSETS(view) ({UIEdgeInsets i; if(@available(iOS 11.0, *)) {i = view.safeAreaInsets;} else {i = UIEdgeInsetsZero;} i;})

使用起来比较简洁

VIEWSAFEAREAINSETS(view).left

VIEWSAFEAREAINSETS(self.view).right

AutoLayout布局方式示例

iOS11以前,我们布局时, 视图的 topbottom 一般参照的是 Top Layout GuideBottom Layout Guide.

iOS11以后, 那两个参照已经 deprecated (过时)了, 而是被Safe Area所取代.

参考自官方文档

总结

还是那句话 我们要做的是让那些不能被遮挡的内容和控件在安全区域范围内显示.

安全区域(Safe Area)概念的引入非常容易解决了异形屏幕布局的问题, 适配原有项目也不会花上很久的时间, 另外iPhone X于10月27日才开始预订, 所以我们有充足的时间来适配, 这一点相信道友们和我一样美滋滋, 最后吐槽一句, 那些黑iPhone X的无脑喷子们, 什么玩王者荣耀按不到XX键, 脑子是个好东西, 但你们不配有, 送你们一句名言 “傻人有傻福, 但傻哔没有 ——–尼古拉斯·LEE”.


自定义BaseNavigationBar : UINavigationBar 里面的内容位置错误, 自己找到内容在重新设置下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)layoutSubviews {
[super layoutSubviews];
/// IOS 11 NavifationBar 问题
self.frame = CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame));
for (UIView *view in self.subviews) {
if([NSStringFromClass([view class]) containsString:@"Background"]) {
view.frame = self.bounds;
}
else if ([NSStringFromClass([view class]) containsString:@"ContentView"]) {
CGRect frame = view.frame;
frame.origin.y = [UIApplication sharedApplication].statusBarFrame.size.height;
frame.size.height = self.bounds.size.height - frame.origin.y;
view.frame = frame;
}
}
}

Swift数组中Map,FlatMap,Filter,Reduce的使用

相关 :http://www.jianshu.com/p/ca21f6ddd20a
From: http://www.jianshu.com/p/7233f140e6c3

map函数能够被数组调用,它接受一个闭包作为参数,作用于数组中的每个元素。闭包返回一个变换后的元素,接着将所有这些变换后的元素组成一个新的数组

1. 比如我们有一个这样的需求遍历一个数组中所有的元素,将每个元素自身与自身相加,最后返回一个保存相加后元素的数组(-_-原谅我这表达能力,下面用代码阐述)

如果我们不使用map函数,那么代码如下

let numbers = [1,2,3]
var sumNumbers = [Int]()
for var number in numbers {
    number += number
    sumNumbers.append(number)
}
// [2,4,6]
print(sumNumbers)

可以看到上面的代码正是我们经常用到的代码,但通过数组的map函数可以帮我们简化上面的代码

// 可以看到我们甚至可以不再定义可变的数组直接用不可变的就可以
let numbers = [1,2,3]
let sumNumbers = numbers.map { (number: Int) -> Int in
    return number + number
}
// 下面介绍简便写法 因为map闭包里面的类型可以自动推断所以可以省略
let sumNumbers1 = numbers.map { number in
    return number + number
}
// 可以省了return 但是循环次数多了一次 目前不知道这是什么原因(循环次数是3次这是4次) 结果是一样的 <如果哪位大神知道麻烦告明小弟>
let sumNumbers2 = numbers.map { number in number + number }
print(sumNumbers2) // [2,4,6]
// 最终简化写法
let sumNumbers3 = numbers.map { $0 + $0 }

2. Map函数返回数组的元素类型不一定要与原数组相同

let fruits = ["apple", "banana", "orange", ""]
// 这里数组中存在一个""的字符串 为了后面来比较 map 和 flatMap
let counts = fruits.map { fruit -> Int? in
    let length = fruit.characters.count
    guard length > 0 else {
        return nil
    }
    return length
}
// [Optional(5), Optional(6), Optional(6), nil]
print(counts)

3. Map还能返回判断数组中的元素是否满足某种条件的Bool值数组

let array = [1,2,3,4,5,6]
// 最洁简的写法
let isEven = array.map { $0 % 2 == 0 }
// 这里在写下完成的写法 下面的例子 将都采用最洁简的写法^_^ 同时也要养成习惯看见上面那种洁简的写法 就要懂它做了些什么 会有什么样的结果
let isEven1 = array.map { num in
    // 写上retrun在Playground中的循环次数是6次 不写是7次 Xcode版本是7.2(7C68) 
    // 不知道这是不是bug 有知道的提醒下我~
    return num % 2 == 0
}
// [false, true, false, true, false, true]
print(isEven)

flatMap 与 map 不同之处是
flatMap返回后的数组中不存在 nil 同时它会把Optional解包;
flatMap 还能把数组中存有数组的数组 一同打开变成一个新的数组 ;
flatMap也能把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积

1. flatMap返回后的数组中不存在nil 同时它会把Optional解包

let fruits = ["apple", "banana", "orange", ""]
let counts = fruits.flatMap { fruit -> Int? in
    let length = fruit.characters.count
    guard length > 0 else {
        return nil
    }
    return length
}
// [5,6,6]
print(counts)

2. flatMap 还能把数组中存有数组的数组 一同打开变成一个新的数组(看代码秒懂~_~)

let array = [[1,2,3], [4,5,6], [7,8,9]]
// 如果用map来获取新的数组
let arrayMap = array.map { $0 }
// [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print(arrayMap)
// 用flatMap
let arrayFlatMap = array.flatMap { $0 }
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(arrayFlatMap)

3. flatMap也能把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积

// 这种情况是把两个不同的数组合并成一个数组 这个合并的数组元素个数是前面两个数组元素个数的乘积
let fruits = ["apple", "banana", "orange"]
let counts = [1, 2, 3]
let fruitCounts = counts.flatMap { count in
    fruits.map { fruit in
//        title + "\(index)"
        // 也可以返回元组
        (fruit, count)
    }
}
// [("apple", 1), ("banana", 1), ("orange", 1), ("apple", 2), ("banana", 2), ("orange", 2), ("apple", 3), ("banana", 3), ("orange", 3)]
print(fruitCounts)
// 这种方法估计用的很少 可以算是一个 flatMap 和 map 的结合使用吧

filter 可以取出数组中符合条件的元素 重新组成一个新的数组

filter可以很好的帮我们把数组中不需要的值都去掉 这个很赞!

let numbers = [1,2,3,4,5,6]
let evens = numbers.filter { $0 % 2 == 0 }
// [2, 4, 6]
print(evens)

map,flatMap和filter方法都是通过一个已存在的数组,生成一个新的、经过修改的数组。然而有时候我们需要把所有元素的值合并成一个新的值 那么就用到了Reduce

1. 比如我们要获得一个数组中所有元素的和

let numbers = [1,2,3,4,5]
// reduce 函数第一个参数是返回值的初始化值
let sum = numbers.reduce(0) { $0 + $1 }
// 这里我写下完整的格式
let sum1 = numbers.reduce(0) { total, num in
    // 这里写不写return在Playground都循环5次 但上面用最洁简的方法显示循环6次。。。 What The Fuck 这是什么鬼!!!
    return total + num
}
// 15
print(sum)

2. 合并成的新值不一定跟原数组中元素的类型相同

let numbers = [1,5,1,8,8,8,8,8,8,8,8]
// reduce 函数第一个参数是返回值的初始化值
let tel = numbers.reduce("") { "\($0)" + "\($1)" }
// 15188888888
print(tel)

3. ruduce 还可以实现 map 和 filter 并且时间复杂度变为O(n) 原来 map 和 filter 的时间复杂度是O(n*n)

extension Array {
    func mMap<U> (transform: Element -> U) -> [U] {
        return reduce([], combine: { $0 + [transform($1)] })
    }
    func mFilter (includeElement: Element -> Bool) -> [Element] {
        return reduce([]) { includeElement($1) ? $0 + [$1] : $0 }
    }
}

iOS知识点大总结

From: http://blog.csdn.net/wenmingzheng/article/details/52180380

记录一些常用和不常用的iOS知识点,防止遗忘丢失。(来源为收集自己项目中用到的或者整理看到博客中的知识点),如有错误,欢迎大家批评指正;如有好的知识点,也欢迎大家联系我,添加上去。谢谢!

一、调用代码使APP进入后台,达到点击Home键的效果。(私有API)

[[UIApplication sharedApplication] performSelector:@selector(suspend)];

suspend的英文意思有:暂停; 悬; 挂; 延缓;

二、带有中文的URL处理。

大概举个例子,类似下面的URL,里面直接含有中文,可能导致播放不了,那么我们要处理一个这个URL,因为他太操蛋了,居然用中文。

1
2
3
http://static.tripbe.com/videofiles/视频/我的自拍视频.mp4
NSString *path = (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (__bridge CFStringRef)model.mp4_url, CFSTR(""),CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));

三、获取UIWebView的高度
个人最常用的获取方法,感觉这个比较靠谱

- (void)webViewDidFinishLoad:(UIWebView *)webView  {  
    CGFloat height = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight"] floatValue];  
    CGRect frame = webView.frame;  
    webView.frame = CGRectMake(frame.origin.x, frame.origin.y, frame.size.width, height);  
}  

四、给UIView设置图片(UILabel一样适用)

  • 第一种方法:
    利用的UIView的设置背景颜色方法,用图片做图案颜色,然后传给背景颜色。

    UIColor *bgColor = [UIColor colorWithPatternImage: [UIImage imageNamed:@”bgImg.png”];

    UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,480)];
    

    [myView setBackGroundColor:bgColor];

  • 第二种方法:

    UIImage *image = [UIImage imageNamed:@”yourPicName@2x.png”];
    yourView.layer.contents = (__bridge id)image.CGImage;
    //设置显示的图片范围
    yourView.layer.contentsCenter = CGRectMake(0.25,0.25,0.5,0.5);//四个值在0-1之间,对应的为x,y,width,height。

五、去掉UITableView多余的分割线

yourTableView.tableFooterView = [UIView new];

六、调整cell分割线的位置,两个方法一起用,暴力解决,防脱发

-(void)viewDidLayoutSubviews {

    if ([self.mytableview respondsToSelector:@selector(setSeparatorInset:)]) {
        [self.mytableview setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0)];

    }
    if ([self.mytableview respondsToSelector:@selector(setLayoutMargins:)])  {
        [self.mytableview setLayoutMargins:UIEdgeInsetsMake(0, 0, 0, 0)];
    }

}

#pragma mark - cell分割线
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([cell respondsToSelector:@selector(setSeparatorInset:)]){
        [cell setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0)];
    }
    if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
        [cell setLayoutMargins:UIEdgeInsetsMake(0, 0, 0, 0)];
    }
}

七、UILabel和UIImageView的交互userInteractionEabled默认为NO。那么如果你把这两个类做为父试图的话,里面的所有东东都不可以点击哦。曾经有一个人,让我帮忙调试bug,他调试很久没搞定,就是把WMPlayer对象(播放器对象)放到一个UIImageView上面。这样imageView addSubView:wmPlayer 后,播放器的任何东东都不能点击了。userInteractionEabled设置为YES即可。

八、UISearchController和UISearchBar的Cancle按钮改title问题,简单粗暴

- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
{
    searchController.searchBar.showsCancelButton = YES;
    UIButton *canceLBtn = [searchController.searchBar valueForKey:@"cancelButton"];
    [canceLBtn setTitle:@"取消" forState:UIControlStateNormal];
    [canceLBtn setTitleColor:[UIColor colorWithRed:14.0/255.0 green:180.0/255.0 blue:0.0/255.0 alpha:1.00] forState:UIControlStateNormal];
    searchBar.showsCancelButton = YES;
    return YES;
}

九、UITableView收起键盘何必这么麻烦
一个属性搞定,效果好(UIScrollView同样可以使用)
以前是不是觉得[self.view endEditing:YES];很屌,这个下面的更屌。
yourTableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;

另外一个枚举为UIScrollViewKeyboardDismissModeInteractive,表示在键盘内部滑动,键盘逐渐下去。

十、NSTimer
1、NSTimer计算的时间并不精确
2、NSTimer需要添加到runLoop运行才会执行,但是这个runLoop的线程必须是已经开启。
3、NSTimer会对它的tagert进行retain,我们必须对其重复性的使用intvailte停止。target如果是self(指UIViewController),那么VC的retainCount+1,如果你不释放NSTimer,那么你的VC就不会dealloc了,内存泄漏了。

十一、UIViewController没用大小(frame)
经常有人在群里问:怎么改变VC的大小啊?
瞬间无语。(只有UIView才能设置大小,VC是控制器啊,哥!)

十二、用十六进制获取UIColor(类方法或者Category都可以,这里我用工具类方法)

+ (UIColor *)colorWithHexString:(NSString *)color
{
    NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];

    // String should be 6 or 8 characters
    if ([cString length] < 6) {
        return [UIColor clearColor];
    }

    // strip 0X if it appears
    if ([cString hasPrefix:@"0X"])
        cString = [cString substringFromIndex:2];
    if ([cString hasPrefix:@"#"])
        cString = [cString substringFromIndex:1];
    if ([cString length] != 6)
        return [UIColor clearColor];

    // Separate into r, g, b substrings
    NSRange range;
    range.location = 0;
    range.length = 2;

    //r
    NSString *rString = [cString substringWithRange:range];

    //g
    range.location = 2;
    NSString *gString = [cString substringWithRange:range];

    //b
    range.location = 4;
    NSString *bString = [cString substringWithRange:range];

    // Scan values
    unsigned int r, g, b;
    [[NSScanner scannerWithString:rString] scanHexInt:&r];
    [[NSScanner scannerWithString:gString] scanHexInt:&g];
    [[NSScanner scannerWithString:bString] scanHexInt:&b];

    return [UIColor colorWithRed:((float) r / 255.0f) green:((float) g / 255.0f) blue:((float) b / 255.0f) alpha:1.0f];
}

十三、获取今天是星期几

+ (NSString *) getweekDayStringWithDate:(NSDate *) date
{
    NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; // 指定日历的算法
    NSDateComponents *comps = [calendar components:NSWeekdayCalendarUnit fromDate:date];

    // 1 是周日,2是周一 3.以此类推

    NSNumber * weekNumber = @([comps weekday]);
    NSInteger weekInt = [weekNumber integerValue];
    NSString *weekDayString = @"(周一)";
    switch (weekInt) {
        case 1:
        {
            weekDayString = @"(周日)";
        }
            break;

        case 2:
        {
            weekDayString = @"(周一)";
        }
            break;

        case 3:
        {
            weekDayString = @"(周二)";
        }
            break;

        case 4:
        {
            weekDayString = @"(周三)";
        }
            break;

        case 5:
        {
            weekDayString = @"(周四)";
        }
            break;

        case 6:
        {
            weekDayString = @"(周五)";
        }
            break;

        case 7:
        {
            weekDayString = @"(周六)";
        }
            break;

        default:
            break;
    }
    return weekDayString;

}

十四、UIView的部分圆角问题

UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(120, 10, 80, 80)];
view2.backgroundColor = [UIColor redColor];
[self.view addSubview:view2];

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view2.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(10, 10)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = view2.bounds;
maskLayer.path = maskPath.CGPath;
view2.layer.mask = maskLayer;
//其中,
byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight
//指定了需要成为圆角的角。该参数是UIRectCorner类型的,可选的值有:
* UIRectCornerTopLeft
* UIRectCornerTopRight
* UIRectCornerBottomLeft
* UIRectCornerBottomRight
* UIRectCornerAllCorners

从名字很容易看出来代表的意思,使用“|”来组合就好了。

十五、设置滑动的时候隐藏navigationBar

navigationController.hidesBarsOnSwipe = Yes;

十六、iOS画虚线
记得先 QuartzCore框架的导入

#import <QuartzCore/QuartzCore.h>

CGContextRef context =UIGraphicsGetCurrentContext();  
CGContextBeginPath(context);  
CGContextSetLineWidth(context, 2.0);  
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);  
CGFloat lengths[] = {10,10};  
CGContextSetLineDash(context, 0, lengths,2);  
CGContextMoveToPoint(context, 10.0, 20.0);  
CGContextAddLineToPoint(context, 310.0,20.0);  
CGContextStrokePath(context);  
CGContextClosePath(context);  

十七、自动布局中多行UILabel,需要设置其preferredMaxLayoutWidth属性才能正常显示多行内容。另外如果出现显示不全文本,可以在计算的结果基础上+0.5。

CGFloat h = [model.message boundingRectWithSize:CGSizeMake([UIScreen mainScreen].bounds.size.width - kGAP-kAvatar_Size - 2*kGAP, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size.height+0.5;

十八、 禁止程序运行时自动锁屏
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];

十九、KVC相关,支持操作符
KVC同时还提供了很复杂的函数,主要有下面这些
①简单集合运算符
简单集合运算符共有@avg, @count , @max , @min ,@sum5种,都表示啥不用我说了吧, 目前还不支持自定义。

@interface Book : NSObject
@property (nonatomic,copy)  NSString* name;
@property (nonatomic,assign)  CGFloat price;
@end
@implementation Book
@end


Book *book1 = [Book new];
book1.name = @"The Great Gastby";
book1.price = 22;
Book *book2 = [Book new];
book2.name = @"Time History";
book2.price = 12;
Book *book3 = [Book new];
book3.name = @"Wrong Hole";
book3.price = 111;

Book *book4 = [Book new];
book4.name = @"Wrong Hole";
book4.price = 111;

NSArray* arrBooks = @[book1,book2,book3,book4];
NSNumber* sum = [arrBooks valueForKeyPath:@"@sum.price"];
NSLog(@"sum:%f",sum.floatValue);
NSNumber* avg = [arrBooks valueForKeyPath:@"@avg.price"];
NSLog(@"avg:%f",avg.floatValue);
NSNumber* count = [arrBooks valueForKeyPath:@"@count"];
NSLog(@"count:%f",count.floatValue);
NSNumber* min = [arrBooks valueForKeyPath:@"@min.price"];
NSLog(@"min:%f",min.floatValue);
NSNumber* max = [arrBooks valueForKeyPath:@"@max.price"];
NSLog(@"max:%f",max.floatValue);

打印结果
2016-04-20 16:45:54.696 KVCDemo[1484:127089] sum:256.000000
2016-04-20 16:45:54.697 KVCDemo[1484:127089] avg:64.000000
2016-04-20 16:45:54.697 KVCDemo[1484:127089] count:4.000000
2016-04-20 16:45:54.697 KVCDemo[1484:127089] min:12.000000

NSArray 快速求总和 最大值 最小值 和 平均值

NSArray *array = [NSArray arrayWithObjects:@"2.0", @"2.3", @"3.0", @"4.0", @"10", nil];
CGFloat sum = [[array valueForKeyPath:@"@sum.floatValue"] floatValue];
CGFloat avg = [[array valueForKeyPath:@"@avg.floatValue"] floatValue];
CGFloat max =[[array valueForKeyPath:@"@max.floatValue"] floatValue];
CGFloat min =[[array valueForKeyPath:@"@min.floatValue"] floatValue];
NSLog(@"%f\n%f\n%f\n%f",sum,avg,max,min);

二十、使用MBProgressHud时,尽量不要加到UIWindow上,加self.view上即可。如果加UIWindow上在iPad上,旋转屏幕的时候MBProgressHud不会旋转。之前有人遇到这个bug,我让他改放到self.view上即可解决此bug。

二十一、强制让App直接退出(非闪退,非崩溃)

- (void)exitApplication {
    AppDelegate *app = [UIApplication sharedApplication].delegate;
    UIWindow *window = app.window;
    [UIView animateWithDuration:1.0f animations:^{
        window.alpha = 0;
    } completion:^(BOOL finished) {
        exit(0);
    }];
}

二十二、Label行间距

NSMutableAttributedString *attributedString =    
  [[NSMutableAttributedString alloc] initWithString:self.contentLabel.text];
   NSMutableParagraphStyle *paragraphStyle =  [[NSMutableParagraphStyle alloc] init];  
  [paragraphStyle setLineSpacing:3];

   //调整行间距       
  [attributedString addAttribute:NSParagraphStyleAttributeName 
                        value:paragraphStyle 
                        range:NSMakeRange(0, [self.contentLabel.text length])];
    self.contentLabel.attributedText = attributedString;

二十三、CocoaPods pod install/pod update更新慢的问题
pod install –verbose –no-repo-update
pod update –verbose –no-repo-update
如果不加后面的参数,默认会升级CocoaPods的spec仓库,加一个参数可以省略这一步,然后速度就会提升不少。

二十四、MRC和ARC混编设置方式
在XCode中targets的build phases选项下Compile Sources下选择 不需要arc编译的文件
双击输入 -fno-objc-arc 即可
MRC工程中也可以使用ARC的类,方法如下:
在XCode中targets的build phases选项下Compile Sources下选择要使用arc编译的文件
双击输入 -fobjc-arc 即可

二十五、把tableview里cell的小对勾的颜色改成别的颜色
_yourTableView.tintColor = [UIColor redColor];

二十六、解决同时按两个按钮进两个view的问题
[button setExclusiveTouch:YES];

二十七、修改textFieldplaceholder字体颜色和大小

textField.placeholder = @"请输入用户名";  
[textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];  
[textField setValue:[UIFont boldSystemFontOfSize:16] forKeyPath:@"_placeholderLabel.font"];

二十八、禁止textField和textView的复制粘贴菜单
这里有一个误区,很多同学直接使用UITextField,然后在VC里面写这个方法,返回NO,没效果。怎么搞都不行,但是如果用UIPasteboard的话,项目中所有的编辑框都不能复制黏贴了,真操蛋。
我们要做的是新建一个类MyTextField继承UITextField,然后在MyTextField的.m文件里重写这个方法,就可以单独控制某个输入框了。

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
     if ([UIMenuController sharedMenuController]) {
       [UIMenuController sharedMenuController].menuVisible = NO;
     }
     return NO;
}

二十九:如何进入我的软件在app store 的页面
先用iTunes Link Maker找到软件在访问地址,格式为itms-apps://ax.itunes.apple.com/…,然后

#define  ITUNESLINK   @"itms-apps://ax.itunes.apple.com/..."
NSURL *url = [NSURL URLWithString:ITUNESLINK];
if([[UIApplication sharedApplication] canOpenURL:url]){
     [[UIApplication sharedApplication] openURL:url];
}

如果把上述地址中itms-apps改为http就可以在浏览器中打开了。可以把这个地址放在自己的网站里,链接到app store。
iTunes Link Maker地址:http://itunes.apple.com/linkmaker

三十、二级三级页面隐藏系统tabbar
1、单个处理

YourViewController *yourVC = [YourViewController new];
yourVC.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:yourVC animated:YES];

2.统一在基类里面处理
新建一个类BaseNavigationController继承UINavigationController,然后重写 -(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated这个方法。所有的push事件都走此方法。

@interface BaseNavigationController : UINavigationController

@end
    -(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
        if (self.viewControllers.count>0) {
    viewController.hidesBottomBarWhenPushed = YES;
    }
        [super pushViewController:viewController animated:animated];
    }

三十一、取消系统的返回手势

self.navigationController.interactivePopGestureRecognizer.enabled = NO;

三十二、修改UIWebView中字体的大小,颜色

1、UIWebView设置字体大小,颜色,字体:
UIWebView无法通过自身的属性设置字体的一些属性,只能通过html代码进行设置
在webView加载完毕后,在

- (void)webViewDidFinishLoad:(UIWebView *)webView方法中加入js代码  
    NSString *str = @"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '60%'";  
    [_webView stringByEvaluatingJavaScriptFromString:str]; 

或者加入以下代码

NSString *jsString = [[NSString alloc] initWithFormat:@"document.body.style.fontSize=%f;document.body.style.color=%@",fontSize,fontColor];   
        [webView stringByEvaluatingJavaScriptFromString:jsString];   

三十三、NSString处理技巧
使用场景举例:可以用在处理用户用户输入在UITextField的文本

//待处理的字符串
NSString *string = @" A B  CD   EFG\n MN\n";

//字符串替换,处理后的string1 = @"ABCDEF\nMN\n";
NSString *string1 = [string stringByReplacingOccurrencesOfString:@" " withString:@""];

//去除两端空格(注意是两端),处理后的string2 = @"A B  CD   EFG\n MN\n";
NSString *string2 = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];

//去除两端回车(注意是两端),处理后的string3 = @" A B  CD   EFG\n MN";
NSString *string3 = [string stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];

//去除两端空格和回车(注意是两端),处理后的string4 = @"A B  CD   EFG\n MN";
NSString *string4 = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

三十四、主线程操作UI(对UI进行更新只能在主线程进行)
解释:所谓的在主线程更新UI、操作UI,大致的意思就是设置UILabel的text或者设置tabbar的badgeValue,设置UIImageView的image等等。

回到主线程方式1:

[self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];

performSelectorOnMainThread方法是NSObject的分类方法,每个NSObject对象都有此方法,
它调用的selector方法是当前调用控件的方法,例如使用UIImageView调用的时候selector就是UIImageView的方法
Object:代表调用方法的参数,不过只能传递一个参数(如果有多个参数请使用对象进行封装)
waitUntilDone:是否线程任务完成执行

回到主线程方式2:

dispatch_async(dispatch_get_main_queue(), ^{
        //更新UI的代码
    });

这个不多解释,GCD的方法,注意不要在主线程掉用。

三十五、判断模拟器

if (TARGET_IPHONE_SIMULATOR) {
        NSLog(@"是模拟器");
    }else{
        NSLog(@"不是模拟器");
    }

三十六、真机测试报 TCWeiboSDK 93 duplicate symbols for architecture armv7

这是因为在项目中引用的两个相同的类库引起了,在我的项目中是因为引入的两个不同指令集引起的;

三十七、AFnetWorking报”Request failed: unacceptable content-type: text/html”错误

AFURLResponseSerialization.m文件设置

self.acceptableContentTypes = [NSSetsetWithObjects:@"application/json", @"text/html",@"text/json",@"text/javascript", nil];

加上@”text/html”,部分,其实就是添加一种服务器返回的数据格式。

三十八、隐藏navigation跳转后的返回按钮

//隐藏头部左边的返回
self.navigationItem.hidesBackButton=YES;

三十九、两种方法删除NSUserDefaults所有记录

//方法一
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];

//方法二
- (void)resetDefaults {
    NSUserDefaults * defs = [NSUserDefaults standardUserDefaults];
    NSDictionary * dict = [defs dictionaryRepresentation];
    for (id key in dict) {
        [defs removeObjectForKey:key];
    }
    [defs synchronize];
}

四十、UITableView设置Section间距
在使用UITableViewStyleGrouped类型的UITableView的时候,经常很奇怪的出现多余的section间距,那可能是因为你只设置了footer或者header的间距中的其中一个,那么另一个默认为20个高度,只需要设置返回0.001的CGFlot的浮点数就可以解决这个多余的间距。

//Header底部间距
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section  
{  
    return 40;//section头部高度  
}  

//footer底部间距  
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section  
{  
    return 0.001;  
}  

四十一、NSLog 输出格式集合

• %@     对象
• %d, %i    整数
• %u      无符整形
• %f       浮点/双字
• %x, %X   二进制整数
• %o      八进制整数
• %zu     size_t
• %p      指针
• %e      浮点/双字 (科学计算)
• %g      浮点/双字
• %s       C 字符串
• %.*s      Pascal字符串
• %c       字符
• %C       unichar
• %lld      64位长整数(long long)
• %llu      无符64位长整数
%Lf       64位双字

四十二、常用GCD总结

为了方便地使用GCD,苹果提供了一些方法方便我们将block放在主线程 或 后台线程执行,或者延后执行。使用的例子如下:

//  后台执行: 
 dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
      // something 
 }); 
 // 主线程执行: 
 dispatch_async(dispatch_get_main_queue(), ^{ 
      // something 
 }); 
 // 一次性执行: 
 static dispatch_once_t onceToken; 
 dispatch_once(&onceToken, ^{ 
     // code to be executed once 
 }); 
 // 延迟2秒执行: 
 double delayInSeconds = 2.0; 
 dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 
 dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 
     // code to be executed on the main queue after delay 
 }); 

dispatch_queue_t 也可以自己定义,如要要自定义queue,可以用dispatch_queue_create方法,示例如下:

dispatch_queue_t urls_queue = dispatch_queue_create("blog.devtang.com", NULL); 
dispatch_async(urls_queue, ^{ 
     // your code 
}); 
dispatch_release(urls_queue); // 如果你部署的最低目标是 iOS 6.0 or Mac OS X 10.8 或者更高的 不用调用

另外,GCD还有一些高级用法,例如让后台2个线程并行执行,然后等2个线程都结束后,再汇总执行结果。这个可以用dispatch_group, dispatch_group_async 和 dispatch_group_notify来实现,示例如下:

dispatch_group_t group = dispatch_group_create(); 
dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ 
      // 并行执行的线程一 
 }); 
 dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{ 
      // 并行执行的线程二 
 }); 
 dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{ 
      // 上面的线程走完成后,最后通知走次block,保证这部分代码最后执行 
 }); 

四十三、 iOS中的随机数

生成0-x之间的随机正整数

int value =arc4random_uniform(x + 1);

生成随机正整数

int value = arc4random() 

通过arc4random() 获取0到x-1之间的整数的代码如下:

int value = arc4random() % x; 

获取1到x之间的整数的代码如下:

int value = (arc4random() % x) + 1; 

最后如果想生成一个浮点数,可以在项目中定义如下宏:

#define ARC4RANDOM_MAX      0x100000000 

然后就可以使用arc4random() 来获取0到100之间浮点数了(精度是rand()的两倍),代码如下:

double val = floorf(((double)arc4random() / ARC4RANDOM_MAX) * 100.0f);

四十四、系统自带的UITableViewCell,其中cell.accessoryView可以自定义控件

if (indexPath.section == 2 && indexPath.row == 0) {
       cell.accessoryView = [[UISwitch alloc] init];
   } else {
       cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
   }

四十五、isKindOfClass, isMemberOfClass的用法区分

-(BOOL) isKindOfClass: classObj判断是否是这个类或者这个类的子类的实例
-(BOOL) isMemberOfClass: classObj 判断是否是这个类的实例

实例一:

Person *person = [[Person alloc] init];      //父类
Teacher *teacher = [[Teacher alloc] init];  //子类

//YES   
if ([teacher isMemberOfClass:[Teacher class]]) {  
     NSLog(@"teacher Teacher类的成员");  
}  
//NO   
if ([teacher isMemberOfClass:[Person class]]) {  
    NSLog(@"teacher Person类的成员");  
}  
//NO   
if ([teacher isMemberOfClass:[NSObject class]]) {  
    NSLog(@"teacher NSObject类的成员");  
}  

实例二:

Person *person = [[Person alloc] init];  
Teacher *teacher = [[Teacher alloc] init];  

//YES   
if ([teacher isKindOfClass:[Teacher class]]) {  
    NSLog(@"teacher 是 Teacher类或Teacher的子类");  
}  
//YES   
if ([teacher isKindOfClass:[Person class]]) {  
    NSLog(@"teacher 是 Person类或Person的子类");  
}  
//YES   
if ([teacher isKindOfClass:[NSObject class]]) {  
    NSLog(@"teacher 是 NSObject类或NSObject的子类");  
}  

isMemberOfClass判断是否是属于这类的实例,是否跟父类有关系他不管,所以isMemberOfClass指到父类时才会为NO;

四十六、关于UIScreen

UIScreen对象包含了整个屏幕的边界矩形。当构造应用的用户界面接口时,你应该使用该对象的属性来获得推荐的矩形大小,用以构造你的程序窗口。

CGRect bound = [[UIScreen mainScreen] bounds]; // 返回的是带有状态栏的Rect
CGRect frame = [[UIScreen mainScreen] applicationFrame]; // 返回的是不带有状态栏的Rect
float scale = [[UIScreen mainScreen] scale]; // 得到设备的自然分辨率

对于scale属性需要做进一步的说明:

以前的iphone 设备屏幕分辨率都是320*480,后来apple 在iPhone 4中采用了名为Retina的显示技术,iPhone 4采用了960x640像素分辨率的显示屏幕。由于屏幕大小没有变,还是3.5英寸,分辨率的提升将iPhone 4的显示分辨率提升至iPhone 3GS的四倍,每英寸的面积里有326个像素。

scale属性的值有两个:
scale = 1; 的时候是代表当前设备是320480的分辨率(就是iphone4之前的设备)
scale = 2; 的时候是代表分辨率为640
960的分辨率

// 判断屏幕类型,普通还是视网膜  
    float scale = [[UIScreen mainScreen] scale];  
    if (scale == 1) {  
        bIsRetina = NO;  
        NSLog(@"普通屏幕");  
    }else if (scale == 2) {  
        bIsRetina = YES;  
        NSLog(@"视网膜屏幕");  
    }else{  
        NSLog(@"unknow screen mode !");  
    } 

四十七、UIView的clipsTobounds属性

view2添加view1到中,如果view2大于view1,或者view2的坐标不全在view1的范围内,view2是盖着view1的,意思就是超出的部份也会画出来,UIView有一个属性,clipsTobounds 默认情况下是NO。如果,我们想要view2把超出的那部份现实出来,就得改变它的父视图也就view1的clipsTobounds属性值。view1.clipsTobounds = YES;
可以很好地解决覆盖的问题

四十八、百度坐标跟火星坐标相互转换

//百度转火星坐标
+ (CLLocationCoordinate2D )bdToGGEncrypt:(CLLocationCoordinate2D)coord
{
    double x = coord.longitude - 0.0065, y = coord.latitude - 0.006;
    double z = sqrt(x * x + y * y) - 0.00002 * sin(y * M_PI);
    double theta = atan2(y, x) - 0.000003 * cos(x * M_PI);
    CLLocationCoordinate2D transformLocation ;
    transformLocation.longitude = z * cos(theta);
    transformLocation.latitude = z * sin(theta);
    return transformLocation;
}

//火星坐标转百度坐标
+ (CLLocationCoordinate2D )ggToBDEncrypt:(CLLocationCoordinate2D)coord
{
    double x = coord.longitude, y = coord.latitude;

    double z = sqrt(x * x + y * y) + 0.00002 * sin(y * M_PI);
    double theta = atan2(y, x) + 0.000003 * cos(x * M_PI);

    CLLocationCoordinate2D transformLocation ;
    transformLocation.longitude = z * cos(theta) + 0.0065;
    transformLocation.latitude = z * sin(theta) + 0.006;

    return transformLocation;
}

四十九、绘制1像素的线

#define SINGLE_LINE_WIDTH           (1 / [UIScreen mainScreen].scale)
#define SINGLE_LINE_ADJUST_OFFSET   ((1 / [UIScreen mainScreen].scale) / 2)

代码如下:

UIView *view = [[UIView alloc] initWithFrame:CGrect(x - SINGLE_LINE_ADJUST_OFFSET, 0, SINGLE_LINE_WIDTH, 100)];

注意:如果线宽为偶数Point的话,则不要去设置偏移,否则线条也会失真

五十、UILabel显示HTML文本(IOS7以上)

NSString * htmlString = @"<html><body> Some html string \n <font size=\"13\" color=\"red\">This is some text!</font> </body></html>";
    NSAttributedString * attrStr = [[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUnicodeStringEncoding] options:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];
    UILabel * myLabel = [[UILabel alloc] initWithFrame:self.view.bounds];
    myLabel.attributedText = attrStr;
    [self.view addSubview:myLabel];

五十一、添加pch文件的步聚

1:创建新文件 ios->other->PCH file,创建一个pch文件:“工程名-Prefix.pch”:
2:将building setting中的precompile header选项的路径添加“$(SRCROOT)/项目名称/pch文件名”(例如:$(SRCROOT)/LotteryFive/LotteryFive-Prefix.pch)
3:将Precompile Prefix Header为YES,预编译后的pch文件会被缓存起来,可以提高编译速度

五十二、兼容字体大小6plue跟它以下的区别

#define FONT_COMPATIBLE_SCREEN_OFFSET(_fontSize_)  [UIFont systemFontOfSize:(_fontSize_ *([UIScreen mainScreen].scale) / 2)]
在iPhone4~6中,缩放因子scale=2;在iPhone6+中,缩放因子scale=3

运用时:

myLabel.font=FONT_COMPATIBLE_SCREEN_OFFSET(15);

五十三、APP虚拟器可以运行,在真机调试时报这个问题,因为把项目名称设成中文导致

App installation failed
There was an internal API error.
Build Settings中的Packaging的Product Name设置成中文

五十四、关于Masonry

a:make.equalTo 或 make.greaterThanOrEqualTo (至多) 或 make.lessThanOrEqualTo(至少)

make.left.greaterThanOrEqualTo(label);
make.left.greaterThanOrEqualTo(label.mas_left);

//width >= 200 && width <= 400
make.width.greaterThanOrEqualTo(@200);
make.width.lessThanOrEqualTo(@400)
b:masequalTo 和 equalTo 区别:masequalTo 比equalTo多了类型转换操作,一般来说,大多数时候两个方法都是 通用的,但是对于数值元素使用mas_equalTo。对于对象或是多个属性的处理,使用equalTo。特别是多个属性时,必须使用equalTo

c:一些简便赋值

// make top = superview.top + 5, left = superview.left + 10,
// bottom = superview.bottom - 15, right = superview.right - 20
make.edges.equalTo(superview).insets(UIEdgeInsetsMake(5, 10, 15, 20))

// make width and height greater than or equal to titleLabel
make.size.greaterThanOrEqualTo(titleLabel)

// make width = superview.width + 100, height = superview.height - 50
make.size.equalTo(superview).sizeOffset(CGSizeMake(100, -50))

// make centerX = superview.centerX - 5, centerY = superview.centerY + 10
make.center.equalTo(superview).centerOffset(CGPointMake(-5, 10))

d:and关键字运用

make.left.right.and.bottom.equalTo(superview); 
make.top.equalTo(otherView);
e:优先;优先权(.priority,.priorityHigh,.priorityMedium,.priorityLow)

.priority允许您指定一个确切的优先级
.priorityHigh 等价于UILayoutPriorityDefaultHigh
.priorityMedium 介于高跟低之间
.priorityLow 等价于UILayoutPriorityDefaultLow

实例:
make.left.greaterThanOrEqualTo(label.mas_left).with.priorityLow();
make.top.equalTo(label.mas_top).with.priority(600);
g:使用mas_makeConstraints创建constraint后,你可以使用局部变量或属性来保存以便下次引用它;如果创建多个constraints,你可以采用数组来保存它们

// 局部或者全局
@property (nonatomic, strong) MASConstraint *topConstraint;

// 创建约束并赋值
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
    make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];

// 过后可以直接访问self.topConstraint
[self.topConstraint uninstall];

h:mas_updateConstraints更新约束,有时你需要更新constraint(例如,动画和调试)而不是创建固定constraint,可以使用mas_updateConstraints方法


- (void)updateConstraints {
    [self.growingButton mas_updateConstraints:^(MASConstraintMaker *make) {
        make.center.equalTo(self);
        make.width.equalTo(@(self.buttonSize.width)).priorityLow();
        make.height.equalTo(@(self.buttonSize.height)).priorityLow();
        make.width.lessThanOrEqualTo(self);
        make.height.lessThanOrEqualTo(self);
    }];

    //调用父updateConstraints
    [super updateConstraints];
}

i:mas_remakeConstraints更新约束,mas_remakeConstraints与mas_updateConstraints比较相似,都是更新constraint。不过,mas_remakeConstraints是删除之前constraint,然后再添加新的constraint(适用于移动动画);而mas_updateConstraints只是更新constraint的值。


- (void)changeButtonPosition {
    [self.button mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.size.equalTo(self.buttonSize);

        if (topLeft) {
       make.top.and.left.offset(10);
        } else {
       make.bottom.and.right.offset(-10);
        }
    }];
}

五十五、iOS中的round/roundf/ceil/ceilf/floor/floorf

round:如果参数是小数,则求本身的四舍五入。
ceil:如果参数是小数,则求最小的整数但不小于本身(向上取,ceil的英文意思有天花板的意思)
floor:如果参数是小数,则求最大的整数但不大于本身(向下取,floor的英文意思有地板的意思)

Example:如果值是3.4的话,则
3.4 – round 3.000000
– ceil 4.000000
– floor 3.00000

五十六、中文输入法的键盘上有联想、推荐的功能,所以可能导致文本内容长度上有些不符合预期,导致越界

* Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘NSMutableRLEArray replaceObjectsInRange:withObject:length:: Out of bounds’
处理方式如下(textView.markedTextRange == nil)

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    if (textView.text.length >= self.textLengthLimit && text.length > range.length) {
        return NO;
    }

    return YES;
}

- (void)textViewDidChange:(UITextView *)textView
{
    self.placeholder.hidden = (self.textView.text.length > 0);

    if (textView.markedTextRange == nil && self.textLengthLimit > 0 && self.text.length > self.textLengthLimit) {
        textView.text = [textView.text substringToIndex:self.textLengthLimit];
    }
}

五十七、关于导航栏透明度的设置及顶部布局起点位置设置

属性:translucent

关闭

self.navigationController.navigationBar.translucent = NO;

开启

self.navigationController.navigationBar.translucent = YES;

属性:automaticallyAdjustsScrollViewInsets

当 automaticallyAdjustsScrollViewInsets 为 NO 时,tableview 是从屏幕的最上边开始,也就是被 导航栏 & 状态栏覆盖

当 automaticallyAdjustsScrollViewInsets 为 YES 时,也是默认行为

五十八、UIScrollView偏移64问题

在一个VC里如果第一个控件是UIScrollView,注意是第一个控件,就是首先addsubview在VC.view上。接着加到scrollView上的View就会在Y点上发生64的偏移(也就是navigationBar的高度44+电池条的高度20)。
这个在iOS7以后才会出现。

解决办法:
self.automaticallyAdjustsScrollViewInsets = false; self是你当前那个VC。

如果这个scrollView不是第一个加到self.view上的。也不会发生64的偏移。

五十九、UIWebView在IOS9下底部出现黑边解决方式

UIWebView底部的黑条很难看(在IOS8下不会,在IOS9会出现),特别是在底部还有透明控件的时候,隐藏的做法其实很简单,只需要将opaque设为NO,背景色设为clearColor即可

六十、tabBarController跳转到另一个一级页面

当我们用tabBarController时,若已经到其中一个TabBar的子页,又要跳转到某一个一级的页面时,如果这样写,导致底部出现黑边,引起tabbar消失的bug

[self.navigationController popToRootViewControllerAnimated:YES];

((AppDelegate *)AppDelegateInstance).tabBarController.selectedIndex = 2;

解决方法一:删除动画

 [self.navigationController popToRootViewControllerAnimated:NO];

((AppDelegate *)AppDelegateInstance).tabBarController.selectedIndex = 2;

解决方法二:延迟执行另一个系统操作

 [self.navigationController popToRootViewControllerAnimated:NO];

 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
((AppDelegate *)AppDelegateInstance).tabBarController.selectedIndex = 2;

    });

六十一、UIWebView获取Html的标题title

titleLabel.text = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];

六十二、汉字转为拼音

- (NSString *)Charactor:(NSString *)aString getFirstCharactor:(BOOL)isGetFirst
{
    //转成了可变字符串
    NSMutableString *str = [NSMutableString stringWithString:aString];
    //先转换为带声调的拼音
    CFStringTransform((CFMutableStringRef)str,NULL, kCFStringTransformMandarinLatin,NO);
    //再转换为不带声调的拼音
    CFStringTransform((CFMutableStringRef)str,NULL, kCFStringTransformMandarinLatin,NO);
    CFStringTransform((CFMutableStringRef)str, NULL, kCFStringTransformStripDiacritics, NO);
    NSString *pinYin = [str capitalizedString];
    //转化为大写拼音
    if(isGetFirst)
    {
        //获取并返回首字母
        return [pinYin substringToIndex:1];
    }
    else
    {
        return pinYin;
    }
}

六十三、属性名以new开头解决方式
因为new为OC关键词,类似的还有alloc
@property (nonatomic,copy) NSString *new_Passwd;

像上面这样写法会报错,可以替换成

@property (nonatomic,copy,getter = theNewPasswd) NSString *new_Passwd;

六十四、去除编译器警告

a:方法弃用告警

#pragma clang diagnostic push  

#pragma clang diagnostic ignored "-Wdeprecated-declarations"      
//会报警告的方法,比如SEL 
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]];  

#pragma clang diagnostic pop  

b:未使用变量

#pragma clang diagnostic push   
#pragma clang diagnostic ignored "-Wunused-variable"   

int a;   

#pragma clang diagnostic pop 

六十五、self.navigationController.viewControllers修改
主要解决那些乱七八糟的跳转逻辑,不按顺序来的问题;

var controllerArr = self.navigationController?.viewControllers//获取Controller数组
controllerArr?.removeAll()//移除controllerArr中保存的历史路径
    //重新添加新的路径
controllerArr?.append(self.navigationController?.viewControllers[0])
controllerArr?.append(C)
controllerArr?.append(B)
    //这时历史路径为(root -> c -> b)
    //将组建好的新的跳转路径 set进self.navigationController里
self.navigationController?.setViewControllers(controllerArr!, animated: true)
//直接写入,完成跳转B页面的同时修改了之前的跳转路径

六十六、数组逆序遍历

1、枚举法

NSArray *array = @[@"1",@"2",@"3",@"5",@"6"];
    [array enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLog(@"%@",obj);
    }];

2、for循环

       NSArray*array=@[@"1",@"2",@"3",@"5",@"6"];

for (NSInteger index = array.count-1; index>=0; index--) {
       NSLog(@"%@",array[index]);
   }

六十七、获取iPhone手机上安装的所有应用程序的信息

信息包括,bundle identitifer,name、版本号,私有API,上架会被拒

Class c =NSClassFromString(@”LSApplicationWorkspace”);
下面这句代码是卸载模拟器上的app的。
[[c new] performSelector:@selector(uninstallApplication:withOptions:) withObject:@”come.ihk.RCIM” withObject:nil];

id s = [(id)c performSelector:NSSelectorFromString(@"defaultWorkspace")];
NSArray *array = [s performSelector:NSSelectorFromString(@"allInstalledApplications")];
for (id item in array)
{
    NSLog(@"%@",[item performSelector:NSSelectorFromString(@"applicationIdentifier")]);
    NSLog(@"%@",[item performSelector:NSSelectorFromString(@"bundleIdentifier")]);

// NSLog(@”%@”,[item performSelector:NSSelectorFromString(@”bundleVersion”)]);
// NSLog(@”%@”,[item performSelector:NSSelectorFromString(@”shortVersionString”)]);
NSLog(@”%@”,[item performSelector:NSSelectorFromString(@”itemName”)]);

}

六十八、获取一个类的所有子类,就是老王有几个儿子的问题
比如获取AVAsset的所有子类

int numClasses;
Class *classes = NULL;
numClasses = objc_getClassList(NULL,0);

if (numClasses >0 )
{
    classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
    numClasses = objc_getClassList(classes, numClasses);
    for (int i = 0; i < numClasses; i++) {
        if (class_getSuperclass(classes[i]) == [AVAsset class]){
            NSLog(@"%@", NSStringFromClass(classes[i]));
        }
    }  
    free(classes);  
}

结果:AVAsset的子类有AVAssetProxy、AVComposition、AVDataAsset、AVURLAsset。其中AVAssetProxy和AVDataAsset为系统私有API类,开发者可用的为AVComposition和AVURLAsset
PS:AVComposition的一个子类为AVMutableComposition,算是AVAsset的孙子了。上述方法获得的是直接子类,也就是儿子,孙子获取不到。如果需要获取,那么继续调用上面的方法。


UITableView 分割线位置调整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
if ([cell respondsToSelector:@selector(setLayoutMargins:)]) {
[cell setLayoutMargins:UIEdgeInsetsZero];
}
if ([cell respondsToSelector:@selector(setSeparatorInset:)]){
[cell setSeparatorInset:UIEdgeInsetsZero];
}
}
-(void)viewDidLayoutSubviews {
if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsZero];
}
if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) {
[self.tableView setLayoutMargins:UIEdgeInsetsZero];
}
}

顺路记录
iOS 11系统下tableView顶部多出一些留白的解决方法

最后发现是因为没有设置tableView的头视图的问题;

以前如果不设置默认为空,现在要专门设置为空才行解决方法如下

#pragma mark 此方法加上是为了适配iOS 11出现的问题

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{ return nil; }
有时候tableview的底部视图也会出现此现象对应的修改就好了

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{ return nil; }


UICollectionView 自适应高度获取

最后用 self.collectionView.collectionViewLayout.collectionViewContentSize.height 获取的高度是正确的

From: https://gxnotes.com/article/39898.html

问题描述

我有一个UICollectionViewUICollectionViewFlowLayout,我想计算其内容大小(为了通过AutoLayout调整其高度所需的intrinsicContentSize返回)。

问题是:即使我对所有单元都有一个固定和相等的高度,我也不知道在UICollectionView中有多少”rows” /线。由于表示数据项的单元格宽度不同,所以我也无法确定该数量的数量,因此UICollectionView的一行中的项目数量也会随之变化。

由于在官方文件中找不到关于这个主题的任何提示,谷歌搜索并没有给我带来任何进一步的帮助和想法,所以不会非常感激。

最佳解决方案

哇!由于某些原因,经过几个小时的研究,我现在发现了一个很简单的答案,我的问题是:我完全搜索错误的地方,挖掘所有可以在UICollectionView找到的文档。

简单易用的解决方案在于底层布局:只需在myCollectionView.collectionViewLayout属性上调用collectionViewContentSize即可获得内容的高度和宽度为CGSize。这样很简单。

次佳解决方案

如果您使用自动布局,则可以创建UICollectionView的子类

如果您使用下面的代码,那么您不必为集合视图指定任何高度约束,因为它将根据集合视图的内容而有所不同。

以下是实施:

@interface DynamicCollectionView : UICollectionView

@end

@implementation DynamicCollectionView

- (void) layoutSubviews
{
    [super layoutSubviews];

    if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize]))
    {
        [self invalidateIntrinsicContentSize];
    }
}

- (CGSize)intrinsicContentSize
{
    CGSize intrinsicContentSize = self.contentSize;

    return intrinsicContentSize;
}

@end

第三种解决方案

viewDidAppear你可以得到它:

float height = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height;

也许当你重新加载数据,然后需要用新的数据计算一个新的高度,然后你可以得到它:添加观察者监听,当你的CollectionView完成重新加载数据在viewdidload

[self.myCollectionView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld context:NULL];

然后添加波纹管功能获取新的高度或在collectionview完成重新加载后执行任何操作:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary  *)change context:(void *)context
{
    //Whatever you do here when the reloadData finished
    float newHeight = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height;    
}

不要忘了删除观察者:

[self.myCollectionView removeObserver:self forKeyPath:@"contentSize" context:NULL];

第四种方案

user1046037在Swift中回答

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize() {
            invalidateIntrinsicContentSize()
        }
    }

    override func intrinsicContentSize() -> CGSize {
        return self.contentSize
    }
}

第五种方案

用户1046037的Swift 3代码回答

import UIKit

class DynamicCollectionView: UICollectionView {

    override func layoutSubviews() {
        super.layoutSubviews()
        if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
            self.invalidateIntrinsicContentSize()
        }

    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

}

参考文献

Podfile 说明

From: http://www.jianshu.com/p/8a0fd6150159

前言

iOS开发会经常用到cocoapods管理第三方,简单、方便、高效。如何集成cocoapods在cocoapods官网Podfile语法说明会有详细介绍,本文我想介绍的是关于集成cocoapods时会用到的一个文件Podfile文件。

什么是Podfile

Podfile是一个规范,描述了一个或多个一套工程目标的依赖项

一个简单写法:

target 'MyApp' do
  pod 'AFNetworking', '~> 3.0'
end

这是最简单最普遍的写法,针对MyApp这个target引入AFNetworking这个依赖库,也是大家平时用的最多的一种方式。

下面是个更复杂的一个例子:

# 下面两行是指明依赖库的来源地址
source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/Artsy/Specs.git'

# 说明平台是ios,版本是9.0
platform :ios, '9.0'

# 忽略引入库的所有警告(强迫症者的福音啊)
inhibit_all_warnings!

# 针对MyApp target引入AFNetworking
# 针对MyAppTests target引入OCMock,
target 'MyApp' do 
    pod 'AFNetworking', '~> 3.0' 
    target 'MyAppTests' do
       inherit! :search_paths 
       pod 'OCMock', '~> 2.0.1' 
    end
end
# 这个是cocoapods的一些配置,官网并没有太详细的说明,一般采取默认就好了,也就是不写.
post_install do |installer|       
   installer.pods_project.targets.each do |target| 
     puts target.name 
   end
end

主配置

install! 这个命令是cocoapods声明的一个安装命令,用于安装引入Podfile里面的依赖库。
install! 这个命令还有一些个人设置选项,例如:

install! 'cocoapods', 
  :deterministic_uuids => false, 
  :integrate_targets => false

还支持其他的选项:

Supported Keys:

:clean

:deduplicate_targets

:deterministic_uuids

:integrate_targets

:lock_pod_sources

:share_schemes_for_development_pods

关于以上的配置,官网也没有一个确切的说明,以为我们只需用系统默认即可。

Dependencies(依赖项)

Podfile指定每个target的依赖项

  • pod指定特定的依赖库
  • podspec可以提供一个API来创建podspecs
  • target通过target指定依赖范围

pod - 指定项目的依赖项

依赖项规范是由Pod的名称和一个可选的版本组合一起。
1> 如果后面不写依赖库的具体版本号,那么cocoapods会默认选取最新版本。

pod 'SSZipArchive'

2> 如果你想要特定的依赖库的版本,就需要在后面写上具体版本号,格式:

pod 'Objection', '0.9'

3> 也可以指定版本范围

  • > 0.1 高于0.1版本(不包含0.1版本)的任意一个版本
  • >= 0.1 高于0.1版本(包含0.1版本)的任意一个版本
  • < 0.1 低于0.1版本(不包含0.1版本)的任意一个
  • <= 0.1低于0.1版本(包含0.1版本)的任意一个
  • ~> 0.1.2 版本 0.1.2的版本到0.2 ,不包括0.2。这个基于你指定的版本号的最后一个部分。这个例子等效于>= 0.1.2并且 <0.2.0,并且始终是你指定范围内的最新版本。

关于版本形式规范详情请参考下面链接:
语义化版本

Build configurations(编译配置)

默认情况下, 依赖项会被安装在所有target的build configuration中。为了调试或者处于其他原因,依赖项只能在给定的build configuration中被启用。
下面写法指明只有在Debug和Beta模式下才有启用配置

pod 'PonyDebugger', :configurations => ['Debug', 'Beta']

或者,可以弄白名单只指定一个build configurations。

pod 'PonyDebugger', :configuration => 'Debug'

注意:默认情况下如果不指定具体生成配置,那么会包含在所有的配置中,如果你想具体指定就必须手动指明。

Subspecs

一般情况我们会通过依赖库的名称来引入,cocoapods会默认安装依赖库的所有内容。
我们也可以指定安装具体依赖库的某个子模块,例如:

# 仅安装QueryKit库下的Attribute模块
pod 'QueryKit/Attribute'

# 仅安装QueryKit下的Attribute和QuerySet模块
pod 'QueryKit', :subspecs => ['Attribute', 'QuerySet']

Using the files from a local path (使用本地文件)

我们也可以指定依赖库的来源地址。如果我们想引入我们本地的一个库,可以这样写:

pod 'AFNetworking', :path => '~/Documents/AFNetworking'

使用这个选项后,Cocoapods会将给定的文件夹认为是Pod的源,并且在工程中直接引用这些文件。这就意味着你编辑的部分可以保留在CocoaPods安装中,如果我们更新本地AFNetworking里面的代码,cocoapods也会自动更新。

被引用的文件夹可以来自你喜爱的SCM,甚至当前仓库的一个git子模块

注意:Pod的podspec文件也应该被放在这个文件夹当中

From a podspec in the root of a library repository (引用仓库根目录的podspec)

有时我们需要引入依赖库指定的分支或节点,写法如下。

需要特别注意的是,虽然这样将会满足任何在Pod中的依赖项通过其他Pods 但是podspec必须存在于仓库的根目录中。

从外部引入podspec引入

podspec可以从另一个源库的地址引入

pod 'JSONKit', :podspec => 'https://example.com/JSONKit.podspec'

podspec

使用给定podspec文件中定义的代码库的依赖关系。如果没有传入任何参数,podspec优先使用根目录,如果是其他情况必须在后面指明。(一般使用默认设置即可)例如:

# 不指定表示使用根目录下的podspec,默认一般都会放在根目录下
podspec
# 如果podspec的名字与库名不一样,可以通过这样来指定
podspec :name => 'QuickDialog'
# 如果podspec不是在根目录下,那么可以通过:path来指定路径
podspec :path => '/Documents/PrettyKit/PrettyKit.podspec'

target

在给定的块内定义pod的target(Xcode工程中的target)和指定依赖的范围。一个target应该与Xcode工程的target有关联。默认情况下,target会包含定义在块外的依赖,除非指定不使用inherit!来继承(说的是嵌套的块里的继承问题)

  • 定义一个简单target ZipApp引入SSZipArchive

    target ‘ZipApp’ do
    pod ‘SSZipArchive’
    end

  • 定义一个ZipApptarget仅引入SSZipArchive库,定义ZipAppTeststarget 引入Nimble的同时也会继承ZipApptarget里面的SSZipArchive

    target ‘ZipApp’ do
    pod ‘SSZipArchive’
    target ‘ZipAppTests’ do
    inherit! :search_paths
    pod ‘Nimble’
    end
    end

  • target块中嵌套多个子块

    target ‘ShowsApp’ do

    ShowsApp 仅仅引入ShowsKit

    pod ‘ShowsKit’

    引入 ShowsKit 和 ShowTVAuth

    target ‘ShowsTV’ do

    pod 'ShowTVAuth' 
    

    end

    引入了Specta和Expecta以及ShowsKit

    target ‘ShowsTests’ do

    inherit! :search_paths 
    pod 'Specta' 
    pod 'Expecta' 
    

    end
    end

抽象target

定义一个新的抽象目标,它可以方便的用于目标依赖继承。

  • 简单写法

    abstract_target ‘Networking’ do
    pod ‘AlamoFire’
    target ‘Networking App 1’
    target ‘Networking App 2’
    end

  • 定义一种abstract_target包含多个target

    注意:这是个抽象的target也就是说在工程中并没有这个target引入ShowsKit

    abstract_target ‘Shows’ do
    pod ‘ShowsKit’

    ShowsiOS target会引入ShowWebAuth库以及继承自Shows的ShowsKit库

    target ‘ShowsiOS’ do

    pod 'ShowWebAuth'
    

    end

    ShowsTV target会引入ShowTVAuth库以及继承自Shows的ShowsKit库

    target ‘ShowsTV’ do

    pod 'ShowTVAuth'
    

    end

    ShowsTests target引入了Specta和Expecta库,并且指明继承Shows,所以也会引入ShowsKit

    target ‘ShowsTests’ do

    inherit! :search_paths 
    pod 'Specta' 
    pod 'Expecta' 
    

    end
    end

abstract! 和 inherit!

  • abstract! 指示当前的target是抽象的,因此不会直接链接Xcode target。
  • inherit! 设置当前target的继承模式。例如:

    target ‘App’ do
    target ‘AppTests’ do

    inherit! :search_paths 
    

    end
    end

Target configuration (目标项配置)

使用target 配置来控制的cocoapods生成project。
开始时详细说明您正在使用什么平台上。工程文件里允许您具体说明哪些项目的链接。

platform

platform用于指定应建立的静态库的平台。CocoaPods提供了默认的平台版本配置:

  • iOS->4.3
  • OS X->10.6
  • tvOS->9.0
  • watchOS->2.0

如果部署目标需要iOS < 4.3,armv6体系结构将被添加到ARCHS。
例如:

#指定具体平台和版本
platform :ios, '4.0'
platform :ios

project

如果没有显示的project被指定,那么会默认使用target的父target指定的project作为目标。如果如果没有任何一个target指定目标,那么就会使用和Podefile在同一目录下的project。同样也能够指定是否这些设置在release或者debug模式下生效。为了做到这一点,你必须指定一个名字和:release/:debuge关联起来

Examples:
Specifying the user project

# MyGPSApp这个target引入的库只能在FastGPS工程中引用
target 'MyGPSApp' do 
    project 'FastGPS' 
    ...
end
# 原理同上
target 'MyNotesApp' do 
    project 'FastNotes' 
    ...
end

使用自定义的编译配置

project 'TestProject', 'Mac App Store' => :release, 'Test' => :debug

inhibit_all_warnings!(强迫症者的福音)

inhibit_all_warnings! 屏蔽所有来自于cocoapods依赖库的警告。你可以全局定义,也能在子target里面定义,也可以指定某一个库:

# 隐藏SSZipArchive的警告而不隐藏ShowTVAuth的警告
pod 'SSZipArchive', :inhibit_warnings => true
pod 'ShowTVAuth', :inhibit_warnings => false

use_frameworks!

通过指定use_frameworks!要求生成的是framework而不是静态库。
如果使用use_frameworks!命令会在Pods工程下的Frameworks目录下生成依赖库的framework
如果不使用use_frameworks!命令会在Pods工程下的Products目录下生成.a的静态库

Workspace

默认情况下,我们不需要指定,直接使用与Podfile所在目录的工程名一样就可以了。如果要指定另外的名称,而不是使用工程的名称,可以这样指定:

workspace 'MyWorkspace'

Source

source是指定pod的来源。如果不指定source,默认是使用CocoaPods官方的source。(建议使用默认设置)

CocoaPods Master Repository
# 使用其他来源地址
source 'https://github.com/artsy/Specs.git'
# 使用官方默认地址(默认)
source 'https://github.com/CocoaPods/Specs.git'

Hooks

Podfile提供了hook机制,它将在安装过程中调用。hook是全局性的,不存储于每个target中。

Plugin

指定应在安装期间使用的插件。使用此方法指定应在安装期间使用的插件,以及当它被调用时,应传递给插件的选项。例如:

# 指定在安装期间使用cocoapods-keys和slather这两个插件
plugin 'cocoapods-keys', :keyring => 'Eidolon'
plugin 'slather'

pre_install

当我们下载完成,但是还没有安装之时,可以使用hook机制通过pre_install指定要做更改,更改完之后进入安装阶段。
格式如下:

pre_install do |installer| 
    # 做一些安装之前的更改
end

post_install

当我们安装完成,但是生成的工程还没有写入磁盘之时,我们可以指定要执行的操作。
比如,我们可以在写入磁盘之前,修改一些工程的配置:

post_install do |installer| installer.pods_project.targets.each do |target| 
        target.build_configurations.each do |config| 
            config.build_settings['GCC_ENABLE_OBJC_GC'] = 'supported' 
        end 
    end
end

def

我们还可以通过def命令来声明一个pod集:

def 'CustomPods'
   pod 'IQKeyboardManagerSwift'
end

然后,我们就可以在需要引入的target处引入:

target 'MyTarget' do 
   CustomPods
end

这么写的好处是:如果有多个target,而不同target之间并不全包含,那么可以通过这种方式来分开引入。

总结

本文主要介绍Podfile文件的一些要素,也是自己的一个学习记录过程,由于本人水平有限,难免会有纰漏之处,还望指出。

UITableView 一些点点滴滴

From: http://www.jianshu.com/p/bb21b4d45a8d

UITableViewCell的选中时的颜色设置

1.系统默认的颜色设置

//无色
cell.selectionStyle = UITableViewCellSelectionStyleNone;

//蓝色
cell.selectionStyle = UITableViewCellSelectionStyleBlue;

//灰色
cell.selectionStyle = UITableViewCellSelectionStyleGray;

2.自定义颜色和背景设置

改变UITableViewCell选中时背景色:

UIColor *color = [[UIColoralloc]initWithRed:0.0 green:0.0 blue:0.0 alpha:1];//通过RGB来定义自己的颜色

cell.selectedBackgroundView = [[UIView alloc] initWithFrame:cell.frame];//这句不可省略
cell.selectedBackgroundView.backgroundColor = [UIColor xxColor];

3.自定义UITableViewCell选中时背景

cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cellart.png"]] ; 
 //还有字体颜色 
cell.textLabel.highlightedTextColor = [UIColor xxxcolor];  [cell.textLabel setTextColor:color];//设置cell的字体的颜色

4.设置tableViewCell间的分割线的颜色

[theTableView setSeparatorColor:[UIColor xxColor]];

5、设置cell中字体的颜色

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  if(indexPath.row == 0)
  {
    cell.textLabel.textColor = ...;
    cell.textLabel.highlightedTextColor = ...;
  }
}

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
// tableviewcell 选中图标
override func layoutSubviews() {
self.selectedBackgroundView?.frame = self.bounds
super.layoutSubviews()
if #available(iOS 8 , *){
// ios7 没效果 第一次也不出现,还是得子定义
for control in self.subviews{
if control.isMember(of: NSClassFromString("UITableViewCellEditControl")!){
for v in control.subviews{
if v.isKind(of: UIImageView.classForCoder()){
//var img = v as! UIImageView
// v = UIImageView(image: UIImage(named: "activity_close"))
if self.isSelected{
(v as! UIImageView).image = UIImage(named: "bjzl_gx")//2x 为40 v2_4_btn_set_selected
}else{
//(v as! UIImageView).image = UIImage(named: "activity_new")//必须原先的图 因为效果不能一直有
}
}
}
}
}
}else{
for view in self.subviews{
if view.isMember(of: NSClassFromString("UITableViewCellScrollView")!){
printLog("views \(view.subviews)")
let views2 = view.subviews
for control in views2{
if control.isMember(of: NSClassFromString("UITableViewCellEditControl")!){
for v in control.subviews{
if v.isKind(of: UIImageView.classForCoder()){
let img = v as! UIImageView
if self.isSelected{
img.image = UIImage(named: "bjzl_gx")
}else{
//img.image = UIImage(named: "activity_new")//必须原先的图 因为效果不能一直有
}
}
}
}
}
}
}
}
}

—– 遇到一个坑爹的问题– 顶部留白20像素 —-

1 不是 automaticallyAdjustsScrollViewInsets 问题 已经设置false
2 不是ios 11 的问题 已经设置不自动计算
tab

1
2
3
self.estimatedRowHeight = 0
self.estimatedSectionFooterHeight = 0;
self.estimatedSectionHeaderHeight = 0;

3 最后, 原因 UITableViewWrapperView 和 UItableView frame 不一致造成的,
据说是 navigationBar.alpha 不为1 会出现,自动下移, 在布局后 重置下内容位置

1
2
3
4
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews();
self.tableView.contentInset = UIEdgeInsets.zero;
}

滑动到顶部 或者底部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (void)scrollToBottom
{
CGFloat yOffset = 0; //设置要滚动的位置 0最顶部 CGFLOAT_MAX最底部
if (self.tableView.contentSize.height > self.tableView.bounds.size.height) {
yOffset = self.tableView.contentSize.height - self.tableView.bounds.size.height;
}
[self.tableView setContentOffset:CGPointMake(0, yOffset) animated:NO];
}
// 可能会有bug ,用下面的方式
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.messageModelArray.count-1 inSection:0];
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

使用KVC自定义UISearchBar的UI

KVC 如果名称错误会崩溃
按钮在输入文本没有获得响应者是isEnabled = false 的, 所以需要处理下
动态改变取消按钮文本有问题 待解决

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
self.searchBar.resignFirstResponder()
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+0.1) {
(self.searchBar.value(forKey: "cancelButton") as! UIButton).isEnabled = true
}
```
From: <http://www.jianshu.com/p/a4cfdcf0f7e1>
在日常工作中对于UISearchBar的操作主要集中在:
* 设置UISearchBar中textfiled的边框及圆角
* 设置占位文字的大小及颜色
* 设置输入文字的大小及颜色
* 修改搜索图标
* 修改取消按钮的文本
* ....
实现的方式有很多,就设置边框而言:可以设置背景图片实现效果;或者直接用UITextField来代替UISearchBar等等。但是以上的操作主要是集中在UISearchBar内部的UITextField上,因此获取到UISearchBar内部的UITextField便可以实现以上的效果:
* 通过KVC获取子视图
UISearchBar *searchBar = [[UISearchBar alloc]init];
// 获取内部子视图
UITextField *searchField = [searchBar valueForKey:@"_searchField"];
UIView *backgroundView = [searchBar valueForKey:@"_background"];
UIButton *cancelButton = [searchBar valueForKey:@"_cancelButton"];
* 改变输入框文本
UITextField *searchField = [searchBar valueForKey:@"_searchField"];
// 设置输入文字的大小及颜色
searchField.font = font(12);
searchField.textColor = UIColorRgb(32,32,32);
// 设置占位文字的大小及颜色
[searchField setValue:UIColorRgb(197,197,197) forKeyPath:@"_placeholderLabel.textColor"];
* 设置输入框边框及圆角
UITextField *searchField = [searchBar valueForKey:@"_searchField"];
// 设置边框及圆角
searchField.layer.cornerRadius = 4.0f;
searchField.layer.masksToBounds = YES;
searchField.layer.borderWidth = 0.5f;
searchField.layer.borderColor = UIColorRgbAlpha(95,96,108,0.2).CGColor;
* 设置输入框内搜索图标
UITextField *searchField = [searchBar valueForKey:@"_searchField"];
// 设置searchField上的搜索图标
UIImage *image = [UIImage imageNamed:@"search"];
UIImageView *iView = [[UIImageView alloc] initWithImage:image];
iView.frame = CGRectMake(0, 0, 15, 15);
searchField.leftView = iView;
* 改变取消按钮的title
UIButton*cancelButton = [searchBar valueForKey:@"_cancelButton"];
[cancelButton setTitle:@"Close"forState:UIControlStateNormal];
---
KVC或者textField的clear属性
```
UIButton *button = [textField valueForKey:@"_clearButton"];
[button setImage:[UIImage imageNamed:@"icon_blueclear"] forState:UIControlStateNormal];
field.clearButtonMode = UITextFieldViewModeWhileEditing;
//swift 3
let clearBtn = self.textField.value(forKey: "_clearButton") as! UIButton
clearBtn.setImage(UIImage(named:"sy-ss"), for: UIControlState.normal)
clearBtn.setImage(UIImage(named:"sy-ss"), for: UIControlState.highlighted)