CC's blog

CC的技术博客 专注于 it 互联网的技术爱好者

分类 iOS开发 下的文章

对于原生定位的封装

摘要

此封装可用于IOS8.0以上. 注意: // 兼容iOS8.0版本 / Info.plist里面手动加上2项 NSLocationAlwaysUsageDescription YES NSLocationWhenInUseUsageDescription YES

.h

//
//  LBSManager.h
//  ingage
//
//  Created by bear on 16/9/19.
//  Copyright © 2016年 com. All rights reserved.
//

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

@interface LBSManager : NSObject<CLLocationManagerDelegate> {
    CLLocationManager *manager;
    
    // block的申明 定义
    void (^locationCallback) (double lat, double lng);
}

// 申明一个 cb的对象
+ (void) geLocaltion:(  void (^)(double lat, double lng) )cb;
+ (void) stop;

@end

.m

//
//  LBSManager.m
//  ingage
//
//  Created by bear on 16/9/19.
//  Copyright © 2016年 com. All rights reserved.
//

#import "LBSManager.h"



@implementation LBSManager



+ (id) sharedLBSManager{
    static id s;
    if (s == nil) {
        s = [[LBSManager alloc] init];
    }
    return s;
}
- (id)init {
    self = [super init];
    if (self) {
        // 打开定位 然后得到数据
        manager = [[CLLocationManager alloc] init];
        manager.delegate = self;
        manager.desiredAccuracy = kCLLocationAccuracyBest;
        
        // 兼容iOS8.0版本
        /* Info.plist里面加上2项
         NSLocationAlwaysUsageDescription      Boolean YES
         NSLocationWhenInUseUsageDescription   Boolean YES
         */
        
        // 请求授权 requestWhenInUseAuthorization用在>=iOS8.0上
        // respondsToSelector: 前面manager是否有后面requestWhenInUseAuthorization方法
        // 1. 适配 动态适配
        if ([manager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
            [manager requestWhenInUseAuthorization];
            [manager requestAlwaysAuthorization];
        }
        // 2. 另外一种适配 systemVersion 有可能是 8.1.1
        float osVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
        if (osVersion >= 8) {
            [manager requestWhenInUseAuthorization];
            [manager requestAlwaysAuthorization];
        }
    }
    return self;
}
- (void) geLocaltion:(  void (^)(double lat, double lng) )cb {
    if ([CLLocationManager locationServicesEnabled] == FALSE) {
        return;
    }
    // block一般赋值需要copy
    locationCallback = [cb copy];
    
    // 停止上一次的
    [manager stopUpdatingLocation];
    // 开始新的数据定位
    [manager startUpdatingLocation];
}

+ (void) geLocaltion:(  void (^)(double lat, double lng) )cb {
    [[LBSManager sharedLBSManager] geLocaltion:cb];
}

- (void) stop {
    [manager stopUpdatingLocation];
}
+ (void) stop {
    [[LBSManager sharedLBSManager] stop];
}

// 每隔一段时间就会调用
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    for (CLLocation *loc in locations) {
        CLLocationCoordinate2D l = loc.coordinate;
        double lat = l.latitude;
        double lnt = l.longitude;
        
        // 使用blocks 调用blocks
        if (locationCallback) {
            locationCallback(lat, lnt);
        }
    }
}

@end

使用

调用方式:
//只获取一次定位
        __block  BOOL isOnece = YES;
        [LBSManager geLocaltion:^(double lat, double lng) {
            isOnece = NO;
            
            //只打印一次经纬度
            NSLog(@"lat lng (%f, %f)", lat, lng);
            
            if (!isOnece) {
                [LBSManager stop];
            }
        }];

 //如果要一直持续获取定位则
    [LBSManager geLocaltion:^(double lat, double lng) {
        
        //不断的打印经纬度
        NSLog(@"lat lng (%f, %f)", lat, lng);
    }];
阅读全文
none

一些关于iOS10适配方面的问题

一些关于iOS10适配方面的问题,如果有错误,欢迎指出.

系统判断方法失效:

在你的项目中,当需要判断系统版本的话,不要使用下面的方法:

#define isiOS10 ([[[[UIDevice currentDevice] systemVersion] substringToIndex:1] intValue]>=10)

它会永远返回NO,substringToIndex:1在iOS 10 会被检测成 iOS 1了,
应该使用下面的这些方法:
Objective-C 中这样写:

#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

或者使用:

if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){.majorVersion = 9, .minorVersion = 1, .patchVersion = 0}]) { NSLog(@"Hello from > iOS 9.1");}
if ([NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9,3,0}]) { NSLog(@"Hello from > iOS 9.3");}

或者使用:

if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_9_0) { // do stuff for iOS 9 and newer} else { // do stuff for older versions than iOS 9}

有时候会缺少一些常量,NSFoundationVersionNumber是在NSObjCRuntime.h中定义的,作为Xcode7.3.1的一部分,我们设定常熟范围从iPhone OS 2到#define NSFoundationVersionNumber_iOS_8_4 1144.17,在iOS 10(Xcode 8)中,苹果补充了缺少的数字,设置有未来的版本.

#define NSFoundationVersionNumber_iOS_9_0 1240.1
#define NSFoundationVersionNumber_iOS_9_1 1241.14
#define NSFoundationVersionNumber_iOS_9_2 1242.12
#define NSFoundationVersionNumber_iOS_9_3 1242.12
#define NSFoundationVersionNumber_iOS_9_4 1280.25
#define NSFoundationVersionNumber_iOS_9_x_Max 1299

Swift中这样写:

if NSProcessInfo().isOperatingSystemAtLeastVersion(NSOperatingSystemVersion(majorVersion: 10, minorVersion: 0, patchVersion: 0)) { 
         // 代码块
}

或者使用

if #available(iOS 10.0, *) { 
         // 代码块
} else { 
         // 代码块
}

隐私数据访问问题:

这是因为iOS对用户的安全和隐私的增强,在申请很多私有权限的时候都需要添加描述,但是,在使用Xcode 8之前的Xcode还是使用系统的权限通知框.
要想解决这个问题,只需要在info.plist添加NSContactsUsageDescription的key, value自己随意填写就可以,这里列举出对应的key(Source Code模式下):

<!-- 相册 --> 
<key>NSPhotoLibraryUsageDescription</key> 
<string>App需要您的同意,才能访问相册</string> 
<!-- 相机 --> 
<key>NSCameraUsageDescription</key> 
<string>App需要您的同意,才能访问相机</string> 
<!-- 麦克风 --> 
<key>NSMicrophoneUsageDescription</key> 
<string>App需要您的同意,才能访问麦克风</string> 
<!-- 位置 --> 
<key>NSLocationUsageDescription</key> 
<string>App需要您的同意,才能访问位置</string> 
<!-- 在使用期间访问位置 --> 
<key>NSLocationWhenInUseUsageDescription</key> 
<string>App需要您的同意,才能在使用期间访问位置</string> 
<!-- 始终访问位置 --> 
<key>NSLocationAlwaysUsageDescription</key> 
<string>App需要您的同意,才能始终访问位置</string> 
<!-- 日历 --> 
<key>NSCalendarsUsageDescription</key> 
<string>App需要您的同意,才能访问日历</string> 
<!-- 提醒事项 --> 
<key>NSRemindersUsageDescription</key> 
<string>App需要您的同意,才能访问提醒事项</string> 
<!-- 运动与健身 --> 
<key>NSMotionUsageDescription</key> <string>App需要您的同意,才能访问运动与健身</string> 
<!-- 健康更新 --> 
<key>NSHealthUpdateUsageDescription</key> 
<string>App需要您的同意,才能访问健康更新 </string> 
<!-- 健康分享 --> 
<key>NSHealthShareUsageDescription</key> 
<string>App需要您的同意,才能访问健康分享</string> 
<!-- 蓝牙 --> 
<key>NSBluetoothPeripheralUsageDescription</key> 
<string>App需要您的同意,才能访问蓝牙</string> 
<!-- 媒体资料库 --> 
<key>NSAppleMusicUsageDescription</key> 
<string>App需要您的同意,才能访问媒体资料库</string>
如果不起作用,可以请求后台权限,类似于这样:

<key>UIBackgroundModes</key>
<array> 
<!-- 在这里写上你在后台模式下要使用权限对应的key --> 
<string>location</string>
...
</array>

或者在Xcode里选中当前的target,选择Capabilities,找到Background Modes,打开它,在里面选择对应权限

UIColor的问题

官方文档中说:大多数core开头的图形框架和AVFoundation都提高了对扩展像素和宽色域色彩空间的支持.通过图形堆栈扩展这种方式比以往支持广色域的显示设备更加容易。现在对UIKit扩展可以在sRGB的色彩空间下工作,性能更好,也可以在更广泛的色域来搭配sRGB颜色.如果你的项目中是通过低级别的api自己实现图形处理的,建议使用sRGB,也就是说在项目中使用了RGB转化颜色的建议转换为使用sRGB,在UIColor类中新增了两个api:

- (UIColor *)initWithDisplayP3Red:(CGFloat)displayP3Red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha NS_AVAILABLE_IOS(10_0);
+ (UIColor *)colorWithDisplayP3Red:(CGFloat)displayP3Red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha NS_AVAILABLE_IOS(10_0);

真彩色的显示

真彩色的显示会根据光感应器来自动的调节达到特定环境下显示与性能的平衡效果,如果需要这个功能的话,可以在info.plist里配置(在Source Code模式下):

<key>UIWhitePointAdaptivityStyle</key>
它有五种取值,分别是:

<string>UIWhitePointAdaptivityStyleStandard</string> // 标准模式
<string>UIWhitePointAdaptivityStyleReading</string> // 阅读模式
<string>UIWhitePointAdaptivityStylePhoto</string> // 图片模式
<string>UIWhitePointAdaptivityStyleVideo</string> // 视频模式
<string>UIWhitePointAdaptivityStyleStandard</string> // 游戏模式

也就是说如果你的项目是阅读类的,就选择UIWhitePointAdaptivityStyleReading这个模式,五种模式的显示效果是从上往下递减,也就是说如果你的项目是图片处理类的,你选择的是阅读模式,给选择太好的效果会影响性能.

ATS的问题

1.在iOS 9的时候,默认非HTTS的网络是被禁止的,我们可以在info.plist文件中添加NSAppTransportSecurity字典,将NSAllowsArbitraryLoads设置为YES来禁用ATS;
2.从2017年1月1日起,,所有新提交的 app 默认不允许使用NSAllowsArbitraryLoads来绕过ATS的限制,默认情况下你的 app 可以访问加密足够强的(TLS V1.2以上)HTTPS内容;
3.可以选择使用NSExceptionDomains设置白名单的方式对特定的域名开放HTTP内容来通过审核,比如说你的应用集成了第三方的登录分享SDK,可以通过这种方式来做,下面以新浪SDK作为示范(Source Code 模式下):

 <key>NSAppTransportSecurity</key>
 <dict>
  <key>NSExceptionDomains</key>
  <dict>
   <key>sina.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>weibo.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>weibo. com</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>sinaimg.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>sinajs.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
   <key>sina.com.cn</key>
   <dict>
    <key>NSThirdPartyExceptionMinimumTLSVersion</key>
    <string>TLSv1.0</string>
    <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
    <false/>
    <key>NSIncludesSubdomains</key>
    <true/>
   </dict>
  </dict>
 </dict>

在iOS 10 中info.plist文件新加入了

NSAllowsArbitraryLoadsInWebContent键,允许任意web页面加载,同时苹果会用 ATS 来保护你的app;

安全传输不再支持SSLv3, 建议尽快停用SHA1和3DES算法;

UIStatusBar的问题:

在iOS10中,如果还使用以前设置UIStatusBar类型或者控制隐藏还是显示的方法,会报警告,方法过期,如下图:

上面方法到 iOS 10 不能使用了,要想修改UIStatusBar的样式或者状态使用下图中所示的属性或方法:

@property(nonatomic, readonly) UIStatusBarStyle preferredStatusBarStyle NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to UIStatusBarStyleDefault
@property(nonatomic, readonly) BOOL prefersStatusBarHidden NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to NO
- (UIStatusBarStyle)preferredStatusBarStyle NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to UIStatusBarStyleDefault
- (BOOL)prefersStatusBarHidden NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to NO
// Override to return the type of animation that should be used for status bar changes for this view controller. This currently only affects changes to prefersStatusBarHidden.
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED; // Defaults to UIStatusBarAnimationFade

UITextField

在iOS 10 中,UITextField新增了textContentType字段,是UITextContentType类型,它是一个枚举,作用是可以指定输入框的类型,以便系统可以分析出用户的语义.是电话类型就建议一些电话,是地址类型就建议一些地址.可以在#import <UIKit/UITextInputTraits.h>文件中,查看textContentType字段,有以下可以选择的类型:

UIKIT_EXTERN UITextContentType const UITextContentTypeName                      NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeNamePrefix                NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeGivenName                 NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeMiddleName                NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeFamilyName                NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeNameSuffix                NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeNickname                  NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeJobTitle                  NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeOrganizationName          NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeLocation                  NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeFullStreetAddress         NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeStreetAddressLine1        NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeStreetAddressLine2        NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeAddressCity               NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeAddressState              NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeAddressCityAndState       NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeSublocality               NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeCountryName               NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypePostalCode                NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeTelephoneNumber           NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeEmailAddress              NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeURL                       NS_AVAILABLE_IOS(10_0);
UIKIT_EXTERN UITextContentType const UITextContentTypeCreditCardNumber          NS_AVAILABLE_IOS(10_0);

UserNotifications(用户通知)

iOS 10 中将通知相关的 API 都统一了,在此基础上很多用户定义的通知,并且可以捕捉到各个通知状态的回调.以前通知的概念是:大家想接受的提前做好准备,然后一下全两分发,没收到也不管了,也不关心发送者,现在的用户通知做成了类似于网络请求,先发一个request得到response的流程,还封装了error,可以在各个状态的方法中做一些额外的操作,并且能获得一些字段,比如发送者之类的.这个功能的头文件是:#import <UserNotifications/UserNotifications.h>
主要有以下文件:

#import <UserNotifications/NSString+UserNotifications.h>
#import <UserNotifications/UNError.h>
#import <UserNotifications/UNNotification.h>
#import <UserNotifications/UNNotificationAction.h>
#import <UserNotifications/UNNotificationAttachment.h>
#import <UserNotifications/UNNotificationCategory.h>
#import <UserNotifications/UNNotificationContent.h>
#import <UserNotifications/UNNotificationRequest.h>
#import <UserNotifications/UNNotificationResponse.h>
#import <UserNotifications/UNNotificationSettings.h>
#import <UserNotifications/UNNotificationSound.h>
#import <UserNotifications/UNNotificationTrigger.h>
#import <UserNotifications/UNUserNotificationCenter.h>
#import <UserNotifications/UNNotificationServiceExtension.h>

UICollectionViewCell的的优化

在iOS 10 之前,UICollectionView上面如果有大量cell,当用户活动很快的时候,整个UICollectionView的卡顿会很明显,为什么会造成这样的问题,这里涉及到了iOS 系统的重用机制,当cell准备加载进屏幕的时候,整个cell都已经加载完成,等待在屏幕外面了,也就是整整一行cell都已经加载完毕,这就是造成卡顿的主要原因,专业术语叫做:掉帧.
要想让用户感觉不到卡顿,我们的app必须帧率达到60帧/秒,也就是说每帧16毫秒要刷新一次.

iOS 10 之前UICollectionViewCell的生命周期是这样的:
1.用户滑动屏幕,屏幕外有一个cell准备加载进来,把cell从reusr队列拿出来,然后调用prepareForReuse方法,在这个方法里面,可以重置cell的状态,加载新的数据;
2.继续滑动,就会调用cellForItemAtIndexPath方法,在这个方法里面给cell赋值模型,然后返回给系统;
3.当cell马上进去屏幕的时候,就会调用willDisplayCell方法,在这个方法里面我们还可以修改cell,为进入屏幕做最后的准备工作;
4.执行完willDisplayCell方法后,cell就进去屏幕了.当cell完全离开屏幕以后,会调用didEndDisplayingCell方法.
iOS 10 UICollectionViewCell的生命周期是这样的:
1.用户滑动屏幕,屏幕外有一个cell准备加载进来,把cell从reusr队列拿出来,然后调用prepareForReuse方法,在这里当cell还没有进去屏幕的时候,就已经提前调用这个方法了,对比之前的区别是之前是cell的上边缘马上进去屏幕的时候就会调用该方法,而iOS 10 提前到cell还在屏幕外面的时候就调用;
2.在cellForItemAtIndexPath中创建cell,填充数据,刷新状态等操作,相比于之前也提前了;
3.用户继续滑动的话,当cell马上就需要显示的时候我们再调用willDisplayCell方法,原则就是:何时需要显示,何时再去调用willDisplayCell方法;
4.当cell完全离开屏幕以后,会调用didEndDisplayingCell方法,跟之前一样,cell会进入重用队列.
在iOS 10 之前,cell只能从重用队列里面取出,再走一遍生命周期,并调用cellForItemAtIndexPath创建或者生成一个cell.
在iOS 10 中,系统会cell保存一段时间,也就是说当用户把cell滑出屏幕以后,如果又滑动回来,cell不用再走一遍生命周期了,只需要调用willDisplayCell方法就可以重新出现在屏幕中了.
iOS 10 中,系统是一个一个加载cell的,二以前是一行一行加载的,这样就可以提升很多性能;
iOS 10 新增加的Pre-Fetching预加载
这个是为了降低UICollectionViewCell在加载的时候所花费的时间,在 iOS 10 中,除了数据源协议和代理协议外,新增加了一个
UICollectionViewDataSourcePrefetching协议,这个协议里面定义了两个方法:

- (void)collectionView:(UICollectionView *)collectionView prefetchItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths NS_AVAILABLE_IOS(10_0);

- (void)collectionView:(UICollectionView *)collectionView cancelPrefetchingForItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths  NS_AVAILABLE_IOS(10_0);

在ColletionView prefetchItemsAt indexPaths这个方法是异步预加载数据的,当中的indexPaths数组是有序的,就是item接收数据的顺序;
CollectionView cancelPrefetcingForItemsAt indexPaths这个方法是可选的,可以用来处理在滑动中取消或者降低提前加载数据的优先级.
注意:这个协议并不能代替之前读取数据的方法,仅仅是辅助加载数据.
Pre-Fetching预加载对UITableViewCell同样适用.

UIRefreshControl的使用

在iOS 10 中, UIRefreshControl可以直接在UICollectionView和UITableView中使用,并且脱离了UITableViewController.现在RefreshControl是UIScrollView的一个属性.
使用方法:

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
    [refreshControl addTarget:self action:@selector(loadData) forControlEvents:UIControlEventValueChanged];
    collectionView.refreshControl = refreshControl;
阅读全文
none

Runtime 详解

什么是Runtime

  • 我们写的代码在程序运行过程中都会被转化成runtime的C代码执行,

例如

[target doSomething];

会被转化成:

objc_msgSend(target, @selector(doSomething));

OC中一切都被设计成了对象,我们都知道一个类被初始化成一个实例,这个实例是一个对象。实际上一个类本质上也是一个对象,在runtime中用结构体表示。

相关的定义:

    /// 描述类中的一个方法
    typedef struct objc_method *Method;

    /// 实例变量
    typedef struct objc_ivar *Ivar;

    /// 类别Category
    typedef struct objc_category *Category;

    /// 类中声明的属性
    typedef struct objc_property *objc_property_t;

类在runtime中的表示

  //类在runtime中的表示
  struct objc_class {
      Class isa;//指针,顾名思义,表示是一个什么,
      //实例的isa指向类对象,类对象的isa指向元类

  #if !__OBJC2__
      Class super_class;  //指向父类
      const char *name;  //类名
      long version;
      long info;
      long instance_size
      struct objc_ivar_list *ivars //成员变量列表
      struct objc_method_list **methodLists; //方法列表
      struct objc_cache *cache;//缓存
      //一种优化,调用过的方法存入缓存列表,下次调用先找缓存
      struct objc_protocol_list *protocols //协议列表
      #endif
  } OBJC2_UNAVAILABLE;
  /* Use `Class` instead of `struct objc_class *` */

获取列表

有时候会有这样的需求,我们需要知道当前类中每个属性的名字(比如字典转模型,字典的Key和模型对象的属性名字不匹配)。

我们可以通过runtime的一系列方法获取类的一些信息(包括属性列表,方法列表,成员变量列表,和遵循的协议列表)。

  unsigned int count;
    //获取属性列表
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]);
    }

    //获取方法列表
    Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Method method = methodList[i];
        NSLog(@"method---->%@", NSStringFromSelector(method_getName(method)));
    }

    //获取成员变量列表
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Ivar myIvar = ivarList[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
    }

    //获取协议列表
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
    }

在Xcode上跑一下看看输出吧,需要给你当前的类写几个属性,成员变量,方法和协议,不然获取的列表是没有东西的。

注意,调用这些获取列表的方法别忘记导入头文件#import。

方法调用

让我们看一下方法调用在运行时的过程(参照前文类在runtime中的表示)

如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作。
如果调用的是类方法,就会到类对象的isa指针指向的对象(也就是元类对象)中操作。

首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应实现并执行。
如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行
如果没找到,去父类指针所指向的对象中执行1,2.
以此类推,如果一直到根类还没找到,转向拦截调用。
如果没有重写拦截调用的方法,程序报错。
以上的过程给我带来的启发:

重写父类的方法,并没有覆盖掉父类的方法,只是在当前类对象中找到了这个方法后就不会再去父类中找了。
如果想调用已经重写过的方法的父类的实现,只需使用super这个编译器标识,它会在运行时跳过在当前的类对象中寻找方法的过程。
拦截调用

在方法调用中说到了,如果没有找到方法就会转向拦截调用。

那么什么是拦截调用呢。

拦截调用就是,在找不到调用的方法程序崩溃之前,你有机会通过重写NSObject的四个方法来处理。

+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;
//后两个方法需要转发到其他的类处理
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;

第一个方法是当你调用一个不存在的类方法的时候,会调用这个方法,默认返回NO,你可以加上自己的处理然后返回YES。
第二个方法和第一个方法相似,只不过处理的是实例方法。
第三个方法是将你调用的不存在的方法重定向到一个其他声明了这个方法的类,只需要你返回一个有这个方法的target。
第四个方法是将你调用的不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。
动态添加方法

重写了拦截调用的方法并且返回了YES,我们要怎么处理呢?
有一个办法是根据传进来的SEL类型的selector动态添加一个方法。

首先从外部隐式调用一个不存在的方法:

//隐式调用方法
[target performSelector:@selector(resolveAdd:) withObject:@"test"];
然后,在target对象内部重写拦截调用的方法,动态添加方法。

void runAddMethod(id self, SEL _cmd, NSString *string){
    NSLog(@"add C IMP ", string);
}
+ (BOOL)resolveInstanceMethod:(SEL)sel{

    //给本类动态添加一个方法
    if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {
        class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
    }
    return YES;
}

其中class_addMethod的四个参数分别是:

Class cls 给哪个类添加方法,本例中是self
SEL name 添加的方法,本例中是重写的拦截调用传进来的selector。
IMP imp 方法的实现,C方法的方法实现可以直接获得。如果是OC方法,可以用+ (IMP)instanceMethodForSelector:(SEL)aSelector;获得方法的实现。
“v@:*”方法的签名,代表有一个参数的方法。
关联对象

现在你准备用一个系统的类,但是系统的类并不能满足你的需求,你需要额外添加一个属性。
这种情况的一般解决办法就是继承。

但是,只增加一个属性,就去继承一个类,总是觉得太麻烦类。

这个时候,runtime的关联属性就发挥它的作用了。

//首先定义一个全局变量,用它的地址作为关联对象的key
static char associatedObjectKey;
//设置关联对象
objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串属性", OBJC_ASSOCIATION_RETAIN_NONATOMIC); //获取关联对象
NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);
NSLog(@"AssociatedObject = %@", string);
objc_setAssociatedObject的四个参数:

id object给谁设置关联对象。
const void *key关联对象唯一的key,获取时会用到。
id value关联对象。
objc_AssociationPolicy关联策略,有以下几种策略:

enum {

 OBJC_ASSOCIATION_ASSIGN = 0,
 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
 OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
 OBJC_ASSOCIATION_RETAIN = 01401,
 OBJC_ASSOCIATION_COPY = 01403 
};

如果你熟悉OC,看名字应该知道这几种策略的意思了吧。

objc_getAssociatedObject的两个参数。

id object获取谁的关联对象。
const void *key根据这个唯一的key获取关联对象。
其实,你还可以把添加和获取关联对象的方法写在你需要用到这个功能的类的类别中,方便使用。

//添加关联对象
- (void)addAssociatedObject:(id)object{
    objc_setAssociatedObject(self, @selector(getAssociatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//获取关联对象
- (id)getAssociatedObject{
    return objc_getAssociatedObject(self, _cmd);
}

注意:这里面我们把getAssociatedObject方法的地址作为唯一的key,_cmd代表当前调用方法的地址。

方法交换

方法交换,顾名思义,就是将两个方法的实现交换。例如,将A方法和B方法交换,调用A方法的时候,就会执行B方法中的代码,反之亦然。
话不多说,这是参考Mattt大神在NSHipster上的文章自己写的代码。

#import "UIViewController+swizzling.h"
#import <objc/runtime.h>

@implementation UIViewController (swizzling)

//load方法会在类第一次加载的时候被调用
//调用的时间比较靠前,适合在这个方法里做方法交换
+ (void)load{
    //方法交换应该被保证,在程序中只会执行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

        //获得viewController的生命周期方法的selector
        SEL systemSel = @selector(viewWillAppear:);
        //自己实现的将要被交换的方法的selector
        SEL swizzSel = @selector(swiz_viewWillAppear:);
        //两个方法的Method
        Method systemMethod = class_getInstanceMethod([self class], systemSel);
        Method swizzMethod = class_getInstanceMethod([self class], swizzSel);

        //首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败
        BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
        if (isAdd) {
            //如果成功,说明类中不存在这个方法的实现
            //将被交换方法的实现替换到这个并不存在的实现
            class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
        }else{
            //否则,交换两个方法的实现
            method_exchangeImplementations(systemMethod, swizzMethod);
        }

    });
}

- (void)swiz_viewWillAppear:(BOOL)animated{
    //这时候调用自己,看起来像是死循环
    //但是其实自己的实现已经被替换了
    [self swiz_viewWillAppear:animated];
    NSLog(@"swizzle");
}

@end
在一个自己定义的viewController中重写viewWillAppear

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    NSLog(@"viewWillAppear");
}
阅读全文

将日期转换成星期

NSDate *today=  [[NSDate alloc]init];
NSDateComponents*componets = [[NSCalendarautoupdatingCurrentCalendar]                
[components:NSCalendarUnitWeekdayfromDate:today];
NSIntegerweekday = [componetsweekday];
NSLog(@"%zd--------",weekday);
阅读全文
none

ffmpeg 桌面 摄像头 麦克风 推流 直播

<p>

<span style="color:#666666;font-family:Arial;font-size:medium;white-space:normal;background-color:#FFFFFF;">首先查看ffmpeg是否支持对应的设备,在OSX下面,Video和Audio设备使用的是avfoundation,所以可以使用avfoundation来查看</span> 

</p>
<p>

ffmpeg -f avfoundation -list_devices true -i ""

</p>
<p>

&nbsp;ffmpeg version 3.0.2 Copyright (c) 2000-2016 the FFmpeg developers

</p>

 
 built with Apple LLVM version 7.3.0 (clang-703.0.29)
  configuration: --prefix=/usr/local/Cellar/ffmpeg/3.0.2 --enable-shared --enable-pthreads --enable-gpl --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-opencl --enable-libx264 --enable-libmp3lame --enable-libxvid --enable-vda
  libavutil      55. 17.103 / 55. 17.103
  libavcodec     57. 24.102 / 57. 24.102
  libavformat    57. 25.100 / 57. 25.100
  libavdevice    57.  0.101 / 57.  0.101
  libavfilter     6. 31.100 /  6. 31.100
  libavresample   3.  0.  0 /  3.  0.  0
  libswscale      4.  0.100 /  4.  0.100
  libswresample   2.  0.101 /  2.  0.101
  libpostproc    54.  0.100 / 54.  0.100
[AVFoundation input device @ 0x7fd602e00280] AVFoundation video devices:
[AVFoundation input device @ 0x7fd602e00280] [0] FaceTime HD Camera
[AVFoundation input device @ 0x7fd602e00280] [1] Capture screen 0
[AVFoundation input device @ 0x7fd602e00280] AVFoundation audio devices:
[AVFoundation input device @ 0x7fd602e00280] [0] Built-in Microphone


<span style="color:#666666;font-family:Arial;font-size:16px;white-space:normal;background-color:#FFFFFF;">通过输出内容可以看到,video部分支持的时两个设备</span>

<span style="color:#666666;font-family:Arial;font-size:16px;white-space:normal;background-color:#FFFFFF;">如果希望将桌面录制或者分享,可以使用命令行如下:</span>

ffmpeg -f avfoundation -i "1" -vcodec libx264 -preset ultrafast -acodec libfaac -f flv
<p style="margin-top:0px;margin-bottom:0px;font-size:11px;line-height:normal;font-family:Menlo;">
    flv rtmp://localhost:5920/rtmplive/room
</p>

<p style="margin-top:0px;margin-bottom:0px;font-size:11px;line-height:normal;font-family:Menlo;">
    <span style="color:#666666;font-family:Arial;font-size:16px;white-space:normal;background-color:#FFFFFF;">如果需要桌面+麦克风,比如一般做远程教育分享ppt或者桌面,有音频讲解</span><span style="font-family:'sans serif', tahoma, verdana, helvetica;font-size:12px;"> </span> 
</p>

<p style="margin-top:0px;margin-bottom:0px;font-size:11px;line-height:normal;font-family:Menlo;">
    <span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">ffmpeg </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">f avfoundation </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">i </span><span style="font-size:12px;word-wrap:break-word;color:#FF00FF;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">"1:0"</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;"> </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">vcodec libx264 </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">preset ultrafast </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">acodec libfaac </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">f flv</span><span style="font-family:'sans serif', tahoma, verdana, helvetica;font-size:12px;">   </span>rtmp://localhost:5920/rtmplive/room
</p>

<p style="margin-top:0px;margin-bottom:0px;font-size:11px;line-height:normal;font-family:Menlo;">
    <span style="color:#666666;font-family:Arial;font-size:16px;white-space:normal;background-color:#FFFFFF;">如果需要桌面+麦克风,并且还要摄像头拍摄到自己,比如一般用于互动主播,游戏主播,命令行如下</span> 
</p>
<span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">ffmpeg </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">f avfoundation </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">i </span><span style="font-size:12px;word-wrap:break-word;color:#FF00FF;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">"1:0"</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;"> </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">f avfoundation </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">i </span><span style="font-size:12px;word-wrap:break-word;color:#FF00FF;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">"0"</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;"> </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">filter_complex </span><span style="font-size:12px;word-wrap:break-word;color:#FF00FF;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">'overlay=10:main_h-overlay_h-10'</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;"> </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">vcodec libx264 </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">b</span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">:</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">v 800k </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">r</span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">:</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">v 25 </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">s 480x320 </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">preset ultrafast </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">acodec libfaac </span><span style="font-size:12px;word-wrap:break-word;color:#0000CC;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;">-</span><span style="font-size:12px;font-family:Consolas, monospace;letter-spacing:0.10000000149011612px;white-space:normal;background-color:#FFFFFF;">f flv </span>
rtmp://localhost:5920/rtmplive/room
阅读全文
none

tableview 头部视图图片下拉放大

原理 给tableview添加一个背景图 (Runtime 追加成员变量) ,header默认透明//

demo1.gif


//
//  url:https://www.xiongcaichang.com
//  Created by bear on 16/3/31.
//  Copyright © 2016年 bear. All rights reserved.
//
#import <UIKit/UIKit.h>


static const CGFloat MaxHeight = 200;



@interface ScalableCover : UIImageView

@property (nonatomic, strong) UIScrollView *scrollView;

@end




@interface UIScrollView (ScalableCover)

@property (nonatomic, weak) ScalableCover *scalableCover;

- (void)addScalableCoverWithImage:(UIImage *)image;
- (void)removeScalableCover;

@end
//
//
//
//  url:https://www.xiongcaichang.com
//  Created by bear on 16/3/31.
//  Copyright © 2016年 bear. All rights reserved.
//



#import "UIScrollView+ScalableCover.h"
#import 

static NSString * const kContentOffset = @"contentOffset";
static NSString * const kScalableCover = @"scalableCover";

@implementation UIScrollView (ScalableCover)

- (void)setScalableCover:(ScalableCover *)scalableCover
{
    [self willChangeValueForKey:kScalableCover];
    objc_setAssociatedObject(self, @selector(scalableCover),
                             scalableCover,
                             OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self didChangeValueForKey:kScalableCover];
}

- (ScalableCover *)scalableCover
{
    return objc_getAssociatedObject(self, &kScalableCover);
}

- (void)addScalableCoverWithImage:(UIImage *)image
{
    ScalableCover *cover = [[ScalableCover alloc] initWithFrame:CGRectMake(0, 0, self.bounds.size.width, MaxHeight)];
    
    cover.backgroundColor = [UIColor clearColor];
    cover.image = image;
    cover.scrollView = self;
    
    [self addSubview:cover];
    [self sendSubviewToBack:cover];
    
    self.scalableCover = cover;
}

- (void)removeScalableCover
{
    [self.scalableCover removeFromSuperview];
    self.scalableCover = nil;
}


@end




@interface ScalableCover (){

}



@end


@implementation ScalableCover

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.contentMode = UIViewContentModeScaleAspectFill;
        self.clipsToBounds = YES;

    }

    return self;
}


- (void)setScrollView:(UIScrollView *)scrollView
{

    [_scrollView removeObserver:scrollView forKeyPath:kContentOffset];
    _scrollView = scrollView;
    [_scrollView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
}




- (void)removeFromSuperview
{
    [_scrollView removeObserver:self forKeyPath:@"contentOffset"];
    [super removeFromSuperview];
}




- (void)layoutSubviews
{
    [super layoutSubviews];
    
    if (self.scrollView.contentOffset.y < 0) {
 CGFloat offset = -self.scrollView.contentOffset.y;
 self.frame = CGRectMake(-offset, -offset, _scrollView.bounds.size.width + offset * 2, MaxHeight + offset);
 } else {
 self.frame = CGRectMake(0, 0, _scrollView.bounds.size.width, MaxHeight); 
}
 } 
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
[self setNeedsLayout]; 
} 
@end 


未完代码详见demo  点击下载:TableViewScaleableHeader.zip

阅读全文
none

2016-04-07 Google Host (Host定期更新,还请亲们注意保存书签)

使用方法

Widonws系统:

到C:\windows\system32\drivers\etc下查找hosts文件并以记事本打开,然后复制以下谷歌-Hosts代码进去,最后保存。(记得文件是无后缀的哦!)

Linux系统

请修改/etc/hosts这个文件

安卓

同上,但要ROOT

MAC系统

如果是苹果电脑请打开你的文件管理器(也就是finder)然后,请按快捷键组合 Shift+Command+G三个组合按键查找文件,并输入Hosts文件的所在路径:/etc/hosts文件。  用sublime或者非系统文本编辑器将下载的内容粘贴进去  保存
阅读全文
none

两行代码搞定UI主流框架

CCNavTab

CCNavTab适用于快速搭建NavigationController和TabBarController相结合的框架 


https://github.com/xiongcaichang/CCNavTab

点击链接直接下载:下载

效果图

使用方法

准备工作: 导入CCNavTab进您的项目

step1: 

            新建总管理(MainController)控制器继承自 CCNavTabController

step2: 

            在总管理控制器(MainController)中配置导航栏的颜色 和 子控制器的数组


//----------1.第一步:配置导航栏的颜色    我这里使用的是随机色---------------------------------//
self.navigationBackgroundColor=[UIColor colorWithRed:arc4random_uniform(255)/255.0 green:arc4random_uniform(255)/255.0 blue:arc4random_uniform(255)/255.0 alpha:1];


//----------2.第二步:添加子控制器配置数组   可以添加任意个,但是最好不要超过6个---------------------------------//
self.childControllerAndIconArr=@[
                                 /************第一个控制器配置信息*********************/
                                 @{
                                     VIEWCONTROLLER : [[OneController alloc]init],  //控制器对象
                                     NORMAL_ICON : @"icon_classTable",             //正常状态的Icon 名称
                                     SELECTED_ICON : @"icon_classTable_selected",  //选中状态的Icon 名称
                                     TITLE : @"表"                                 //Nav和Tab的标题
                                     },
                                 /************第二个控制器配置信息*********************/
                                 @{
                                     VIEWCONTROLLER : [[TwoController alloc]init],
                                     NORMAL_ICON : @"icon_me",
                                     SELECTED_ICON : @"icon_me_selected",
                                     TITLE : @"校园"
                                     },
                                 ];


阅读全文
none

cocoapods 安装

pod setup失败的解决方案 解压master.zip,将master文件夹拖到/Users/用户名/.cocoapods/repos目录下即可。

时间宝贵长话短说。
CocoaPods是一个负责管理iOS项目中第三方开源代码的工具。如果你没听说过,也不想用,那就别往下看了。

1.CocoaPods的安装
需要用到ruby,Mac系统自带ruby,但如果不是最新的系统,最好更新一下。
ruby的软件源rubygems.org被墙了,所以先换一下源,命令行下依次执行3条命令
$ gem sources --remove https://rubygems.org/
$ gem sources -a http://ruby.taobao.org/
$ gem sources -l

然后升级gem
$ sudo gem update --system

完了就开始安装CocoaPods
$ sudo gem install cocoapods
$ pod setup

出现Setting up CocoaPods master repo,半天没有任何反应。原因无他,因为那堵墙阻挡了cocoapods.org。。。
gitcafe和oschina都是国内的服务器,可以用它们CocoaPods索引库的镜像:
$ pod repo remove master
$ pod repo update
如果想用oschina的镜像也可以把第二条命令 换成 http://git.oschina.net/akuandev/Specs.git 即可

第二条命令执行的时候会比较耗时,这个时候要去把整个specs仓库clone一下,下载到 ~/.cocoapods里;
cd  到该目录里,用du -sh *命令来查看文件大小,每隔一会看看,最终大小是190多M。
阅读全文