iOS开发小技巧(持续更新)

1.去除导航栏底部线条和去除Tabbar顶部的线条

我们自定义NavigationController和自定义UITabbar的时候,想去掉他们自带的细线,通过设置下面的可以达到效果

//去除导航栏底部线条
  [self.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
  [self.navigationBar setShadowImage:[UIImage new]];

//如果上面的达不到效果
//那绘制一个空白的image来设置,亲测两种方法都可以
  //去掉下面线条
    CGRect rect1 = CGRectMake(0, 0, HFScreenWidth, HFScreenHieght);
    UIGraphicsBeginImageContext(rect1.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
    CGContextFillRect(context, rect1);
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [self.navigationBar setBackgroundImage:img forBarMetrics:UIBarMetricsDefault];
    [self.navigationBar setShadowImage:img];

//去除Tabbar顶部的线条
 [self.tabBar setBackgroundImage:[UIImage new]];
 [self.tabBar setShadowImage:[UIImage new]];

//如果上面的达不到效果
//那绘制一个空白的image来设置,亲测两种方法都可以
  //去掉下面线条
    CGRect rect1 = CGRectMake(0, 0, HFScreenWidth, HFScreenHieght);
    UIGraphicsBeginImageContext(rect1.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [[UIColor clearColor] CGColor]);
    CGContextFillRect(context, rect1);
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    [self.tabBar setBackgroundImage: img];
    [self.tabBar setShadowImage: img];

2.图片处理

有时候网络加载回来的图片比例有点不适应,这时候就需要我们剪裁处理了,不然的话图片就会被压缩变形
我们可以给UIImageView添加一个类别,然后在类别里面设置contentMode达到效果

//imageview的content适应图片的比例大小,然后再剪裁掉多余的部分
-(void)fitImageView
{
    [self setContentScaleFactor:[[UIScreen mainScreen] scale]];
    self.contentMode =  UIViewContentModeScaleAspectFill;
    self .clipsToBounds  = YES;
}
//如果图片的frame小于原图大小,显示原图的大小,然后再剪裁掉多余的部分,如果图片的frame大于原图大小,在图片中间显示原图大小
-(void)fitCenterImageView
{
    [self setContentScaleFactor:[[UIScreen mainScreen] scale]];
    self.contentMode =  UIViewContentModeCenter;
    self .clipsToBounds  = YES;
}

3.导航栏标题和Tabbar标题的设置
  //这个单独设置tabbar的title
  self.navigationController.title = @"yoyoyo";
  //这个单独设置navgation的title
  self.navigationItem.title = @"哟哟";
  //这个是tabbar和navgation一块设置的
  self.title = @"啃爹";

4.给view绘制指定的圆角

使用赛贝尔曲线绘制需要的圆角
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};

    CGRect frame = self.backImageView.bounds;
    frame.size.width = self.frame.size.width;
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:frame byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(5, 5)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = frame;
    maskLayer.path = maskPath.CGPath;
    self.backImageView.layer.mask = maskLayer;
5.scrollView传递点击事件

有时候我们会在tableView的cell上面嵌进来scrollview,这时候scrollview就会截取掉,这样tableViewcell的点击事件就无法响应,所以我们需要他点击事件传递下去。给scrollview添加类别,然后重写响应事件,传递到父视图即可

#import "UIScrollView+TouchEvent.h"

@implementation UIScrollView (TouchEvent)

//tableviewcell的scrollview会截取到点击事件,将这个事件传递下去给cell
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

{
    //因为collectionView是继承自scrollview,所以如果把collectionview的点击事件也传递下去了,collectionview的点击事件就被其superview接受,没有点击事件了
    if ([self isKindOfClass:[UICollectionView class]] || [self isKindOfClass:[UITableView class]]) {
        return;
    }
    [self.superview touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

{
    if ([self isKindOfClass:[UICollectionView class]]||[self isKindOfClass:[UITableView class]]) {
        return;
    }
    [self.superview touchesEnded:touches withEvent:event];

}
@end

6.UITextField和UItextview

1.UITextField的placeholder的字体改变

   [self.nameTextView setValue:[UIColor blackColor] forKeyPath:@"_placeholderLabel.color"];

2.改变键盘的类型和return键

 self.textField.keyboardType = UIKeyboardTypeNumberPad;
 self.textView.returnKeyType = UIReturnKeyDone;

3.捕捉return键响应进行操作

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    if ([text isEqualToString:@"\n"]){ //判断输入的字是否是回车,即按下return
        //在这里做你响应return键的代码
        [textView resignFirstResponder];
        return NO; //这里返回NO,就代表return键值失效,即页面上按下return,不会出现换行,如果为yes,则输入页面会换行
    }
    return YES;
}

7.横竖屏的转换
//强制横屏
- (void)forceOrientationLandscape
{
    AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    appdelegate.allowRotation = 1;
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector = NSSelectorFromString(@"setOrientation:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        int val = UIInterfaceOrientationLandscapeRight;
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }

}

//强制竖屏
- (void)forceOrientationPortrait
{
    _isDirectionScreen = NO;
    AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
      appdelegate.allowRotation = 0;
    [appdelegate application:[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:self.view.window];
    //设置屏幕的转向为竖屏
    [[UIDevice currentDevice] setValue:@(UIDeviceOrientationPortrait) forKey:@"orientation"];
    //刷新
    [UIViewController attemptRotationToDeviceOrientation];
}

8.调试时打印,正式环境打包不打印
/**
 *  调试时打印,正式环境打包不打印
*/
// 日志输出
#ifdef DEBUG
#define HFLog(...) NSLog(__VA_ARGS__)
#else
#define HFLog(...)
#endif
9.常见的基础概念
  • Class:定义Objective-C类
  • Ivar定义对象的实例变量,包括类型和名字。
  • Protocol:定义正式协议。
  • objc_property_t:定义属性。叫这个名字可能是为了防止和Objective-C 1.0中的用户类型冲突,那时候还没有属性。
  • Method:定义对象方法或类方法。这个类型提供了方法的名字(就是选择器)、参数数量和类型,以及返回值(这些信息合起来称为方法的签名),还有一个指向代码的函数指针(也就是方法的实现)。
  • SEL:定义选择器。选择器是方法名的唯一标识符。
  • IMP:定义方法实现。这只是一个指向某个函数的指针,该函数接受一个对象、一个选择器和一个可变长参数列表(varargs),返回一个对象
10.缓存URL图片和读取
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSData *data = [NSData dataWithContentsOfURL:[NSURL  URLWithString:urlString]];
        UIImage *image = [UIImage imageWithData:data]; // 取得图片
        // 本地沙盒目录
        NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        // 得到本地沙盒中名为"MyImage"的路径,"MyImage"是保存的图片名
        NSString *imageFilePath = [path stringByAppendingPathComponent:@"watermark.png"];
        NSLog(@"file:%@",imageFilePath);
        // 将取得的图片写入本地的沙盒中 UIImageJPEGRepresentation,UIImagePNGRepresentation两种格式存储
        BOOL success = [UIImagePNGRepresentation(image) writeToFile:imageFilePath atomically:YES];
        if (success){
           dispatch_async(dispatch_get_main_queue(), ^{
              //  获取图片
               NSData *data = [[NSData alloc] initWithContentsOfFile:imageFilePath];
               UIImage *waterMarkImage = [UIImage imageWithData:data];
            });
        }
    });
11.获取沙河目录

Documents:保存应用运行时生成的需要持久化的数据,iTunes 同步设备时会备份该目录。例如,游戏应用可 将游戏存档保存在该目录。 tmp:保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。应用没有运行时,系统也 可能会清除该目录下的文件。iTunes 同步设备时不会备份该目录。 Library/Caches:保存应用运行时生成的需要持久化的数据,iTunes 同步设备时不会备份该目录。一般存储体 积大、不需要备份的非重要数据。
Library/Preference:保存应用的所有偏好设置,iOS 的 Settings(设置)应用会在该目录中查找应用的设置信息。 iTunes 同步设备时会备份该目录。
总结:
1、体积大(itunes 不会备份)
(1) tmp(里面的内容可能会被系统随机清除)
(2) Library/Caches
2、体积小(itunes 会备份)
(1) Documents
(2) Library/Preference
Home目录

NSString *homeDirectory = NSHomeDirectory();  

Document目录

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

Cache目录

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

Libaray目录

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);   
NSString *path = [paths objectAtIndex:0];  
12.openURL

UIApplication 有个功能十分强大的 openURL:方法

- (BOOL)openURL:(NSURL*)url;

openURL:方法的部分功能:
(1)打电话

UIApplication *app = [UIApplication sharedApplication];
[app openURL:[NSURL URLWithString:@"tel://10086"]];

(2)发短信

[app openURL:[NSURL URLWithString:@"sms://10086"]];

(3)发邮件

[app openURL:[NSURL URLWithString:@"mailto://[email protected]"]]; 

(4)打开一个网页资源(自动跳转到浏览器打开)

[app openURL:[NSURL URLWithString:@"http://ios.itcast.cn"]];

(5)打开其他 app 程序

[app openURL:[NSURL URLWithString:@"........"]];
13.自定义弹出的键盘
self.inputField.inputView = myView

按文本框弹出的键盘不再是普通文字输入键盘,而是我们设置的 myView。一般把这个方法写在 viewDiDLoad 方法中。

也可以在键盘上方增加一个 View:

self. inputField.inputAcessoryView = myView;

之后就可以在键盘上侧显示 myView。

14.判断系统版本
if([[UIDevice currentDevice].systemVersion doubleValue]>=7.0) {
     //是 IOS7 至以上版本
 }else{
    //IOS7 以下版本
 }
15.禁止左划手势
 if([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.navigationController.interactivePopGestureRecognizer.enabled = false;
    }

自定义导航栏左划返回手势

    self.interactivePopGestureRecognizer.delegate = (id)self;

16.KVO

viewcontroller本身不能使用KVO监听其持有的array变化

17.导航栏半透明

导航栏半透明时改变坐标原点

 self.automaticallyAdjustsScrollViewInsets = YES;
 self.edgesForExtendedLayout = UIRectEdgeNone;

这样设置之后导航栏和tabbar会变灰色而不是半透明,再加上这两个设置就好了

self.navigationController.navigationBar.translucent = NO;
self.tabBarController.tabBar.translucent = NO;

18 oc文件转成c,c++语言文件,后缀名为cpp

cd到你所要转成c语言文件的目录里:

clang  -rewrite-objc 目标文件(例如 main.m)

成功会在你所cd的目录下面多了一个后缀名是cpp的文件。

在arc模式下,使用weak修饰符时clang -rewrite-objc xxx.m时会提示编译错误:

cannot create __weak reference because the current deployment target does
      not support weak references
    __attribute__((objc_ownership(weak))) NSObject *obj2 = obj;

解决方法:

clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations 目标文件(例如 main.m)

19.load和initialize

问:Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?
答:Category中有load方法,load方法在程序启动装载类信息的时候就会调用。load方法可以继承。调用子类的load方法之前,会先调用父类的load方法

问:load、initialize的区别,以及它们在category重写的时候的调用的次序。
答:区别在于调用方式和调用时刻
调用方式:load是根据函数地址直接调用,initialize是通过objc_msgSend调用
调用时刻:load是runtime加载类、分类的时候调用(只会调用1次),initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)

调用顺序:先调用类的load方法,先编译那个类,就先调用load。在调用load之前会先调用父类的load方法。分类中load方法不会覆盖本类的load方法,先编译的分类优先调用load方法。initialize先初始化父类,之后再初始化子类。如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次),如果分类实现了+initialize,就覆盖类本身的+initialize调用。

20.事件传递和事件响应

首先UIApplication会捕获到事件(先进行传递再进行响应),传递到最上层(根据subviews)
然后从最上层开始做响应事件,判断最上层是否能响应事件,如果可以,就响应。无就传递到下一层(nextResponder)往下传递

事件传递:下 ---> 上
事件响应: 上 ---> 下

你可能感兴趣的:(iOS开发小技巧(持续更新))