欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

iOS开发笔记(IOS7回归)

程序员文章站 2024-03-23 08:14:04
...



<---点击左上角目录,可以快速查找这里是否有你遇到的问题条目<---


1,iOS中如何设置项目/文件是否支持ARC

旧工程配置arc方案:

1,直接在targets->build phases中修改compiler Flags,是否支持arc。添加:-fobjc-arc,就可以让旧项目支持arc。如果想让原来支持arc的不使用arc则添加-fno-objc-arc

iOS开发笔记(IOS7回归)

因为在build phases中可以改变是否支持arc,所以应该在代码中添加判断是否支持arc,这样不管以后.m的arc是否改变,都不用再次调整代码。

下面是一个.h文件(附件中也上传了.h),整合了arc的各种属性、release判断,直接#import在你想使用arc的类中即可。

#ifndef paixiu_PXISARC_h
#define paixiu_PXISARC_h

#ifndef PX_STRONG
#if __has_feature(objc_arc)
#define PX_STRONG strong
#else
#define PX_STRONG retain
#endif
#endif

#ifndef PX_WEAK
#if __has_feature(objc_arc_weak)
#define PX_WEAK weak
#elif __has_feature(objc_arc)
#define PX_WEAK unsafe_unretained
#else
#define PX_WEAK assign
#endif
#endif

#if __has_feature(objc_arc)
#define PX_AUTORELEASE(expression)expression
#define PX_RELEASE(expression)expression
#define PX_RETAIN(expression)expression
#else
#define PX_AUTORELEASE(expression) [expressionautorelease]
#define PX_RELEASE(expression) [expressionrelease]
#define PX_RETAIN(expression) [expressionretain]
#endif

#endif

说明:在arc中,strong对应原来的retain与copy,weak对应原来的assign。

EX:举例使用autorelease:

NSArray*testArray =PX_AUTORELEASE([[NSArrayalloc]init]);
//如果支持arctestArray就只是alloc init,release的事情由系统来做。

//如果不支持arc,那这条语句相当于:
NSArray*testArray = [[[NSArray alloc] init] autorelease];



这样不管以后改不改arc,都不会内存泄漏了 .

所以,arc的使用有两点:

A:在build phases中修改compiler Flags值。
B:在代码中判断是否支持arc,包括对属性(property)、释放(release)的判断。

3,在dealloc中需要这样做:

类如果注册了通知(观察者模式),需要remove掉。这个不管是否支持arc,都必须要做的。

- (void)dealloc {

[[NSNotificationCenterdefaultCenter]removeObserver:self];//如果注册了通知的话。

[selfremoveObserver:selfforKeyPath:keyPath];//如果注册了kvo的话。

#if !__has_feature(objc_arc) //在这里也需要判断是否支持arc,支持的话就执行旧工程中该release的语句.
[array release]; //array代表alloc但没有autorelease的变量
[superdealloc];
#endif
}

4,另外加点block的判断,这个是在4.0以后有的,当然也可以不进行判断,因为现在大多数都4.0以后了。

#if NS_BLOCKS_AVAILABLE

#endif

总结:
1,arc的设置是在build phases中修改compiler Flags的值。
2,如果使用了arc,在你的代码中不可以使用retain, release, autorelease,如果使用的话会报错。
3,如果使用了arc,在@property声明中,用strong代替retain。在支持__unsafe_unretained的情况下,__unsafe_unretained相当于assign。
4,如果使用了arc,NSAutoReleasePool也不能使用,测试发现,用@autoreleasepool代替,不会编译报错。
总之,一切你之前“背过”的那几条内存管理规则,你都不用去管了。而且,个人感觉,用arc代码清晰很多,而且效率也提高了些。

——————————————————————————————————

对于arc属性可能写的不太清楚,这里附加点:

1,不管在不在arc下,object对象都有强引用、弱引用之分,当需要保持(拥有)其他对象的时候,需要retain。
2,在arc中,使用strong、weak修饰的变量,当对象不再存在的时候会被置为nil。而[align=-webkit-left]__unsafe_unretained不会被置为nil,会成为野指针,是不安全的,再次访问可能造成错误。
[align=-webkit-left]
3,引用关键字:arc中,变量声明默认为_strong.
出自:http://www.cocoachina.com/bbs/read.php?tid=122591


2,iOS设备的硬件适配 (关于armv6, armv7, armv7s )

armv6、armv7、armv7s是arm CPU的指令集,原则上是向下兼容的

armv6:iPhone 2G/3G,iPod 1G/2G

armv7:iPhone 3GS/4/4s,iPod 3G/4G,iPad 1G/2G/3G

armv7s:iPhone5

如果引用到第三方的库,以前在iphone4s下编译没有问题,但是换成iphone5之后,提示:


Undefined symbols for architecture armv7s:
"_OBJC_CLASS_$_AMapView", referenced from:
objc-class-ref in libMAMapKit.a(MAMapView.o)
ld: symbol(s) not found for architecture armv7s

也就是说,引用自XX.a静态库的XX类不支持armv7s指令。你引用的静态库不支持armv7s,要想顺利编译通过,要么通知开发修改,等待支持了之后再测;要么在target的build settings中的valid Architectures 将armv7s先暂时去掉,编译就可以成功。(暂时的办法)


3,关于Build Active Architecture Only属性

这个属性设置为yes,是为了debug的时候编译速度更快,它只编译当前的architecture版本。
而设置为no时,会编译所有的版本。
这个是设备对应的architecture:
armv6:iPhone 2G/3G,iPod 1G/2G
armv7:iPhone 3GS/4/4s,iPod 3G/4G,iPad 1G/2G/3G
armv7s:iPhone5, iPod5

编译出的版本是向下兼容的,比如你设置此值为yes,用iphone4编译出来的是armv7版本的,iphone5也可以运行,但是armv6的设备就不能运行。

所以,一般debug的时候可以选择设置为yes,release的时候要改为no,以适应不同设备。

Build Phases中的Architectures和Valid Architectures的区别

Architectures 这代表,在这个项目里你想要Xcode编译的目标设备列表。

Valid Architectures 一般来说是不需要更改的,和Architectures一样就可以。

在Xcode5.0里的Valid Architectures设置里,有2个选项:

  1. 默认为standard architectures (including 64-bit)(armv7,armv7s,arm64),这样设置,你的Deployment target最低只能设置为 6.0,(在Xcode5.0.1 之后,最低能够兼容IOS 5.1.1);
  2. standard architectures (armv7,armv7s),这样设置,你的Deployment target最低能设置为 4.3;

使用standard architectures (including 64-bit)(armv7,armv7s,arm64)参数,
则打的包里面有32位、64位两份代码,
在iPhone5s(iPhone5s的cpu是64位的)下,会首选运行64位代码包,
其余的iPhone(其余iPhone都是32位的,iPhone5c也是32位),
只能运行32位包,
但是包含两种架构的代码包,只有运行在ios6,ios7系统上。
这也就是说,这种打包方式,对手机几乎没啥要求,但是对系统有要求,即ios6以上。

而使用standard architectures (armv7,armv7s)参数,
则打的包里只有32位代码,
iPhone5s的cpu是64位,但是可以兼容32位代码,即可以运行32位代码。但是这会降低iPhone5s的性能,原因下面的参考有解释。
其余的iPhone对32位代码包更没问题,
而32位代码包,对系统也几乎也没什么限制。

所以总结如下:
要发挥iPhone5s的64位性能,就要包含64位包,那么系统最低要求为ios6。
如果要兼容ios5以及更低的系统,只能打32位的包,系统都能通用,但是会丧失iPhone5s的性能。

所有IOS设备详情列表 List of iOS devices - Wikipedia, the free encyclopedia

armv6:iPhone 2G/3G,iPod 1G/2G
armv7:iPhone 3GS/4/4s,iPod 3G/4G,iPad 1G/2G/3G ,iPad Mini 1
armv7s:iPhone5 ,iPhone5C ,iPad4
armv8:iPhone5S ,iPad5(iPad Air), iPad Mini 2(iPad Mini Retina)

iOS 7: 如何为iPhone 5S编译64位应用。

Xcode 5编译的iOS 7程序包含了32位和64位两套二进制代码,在32位的iOS系统上会调用32位的二进制代码,在64位系统上会调用64位的二进制代码,以此来解决向后兼容的问题。

同时,考虑到很多32位的程序可能在没有重新编译的情况下部署到64位系统上,64位的iOS系统中带有两套FrameWork,一套是32位的,一套是64位的。
当64位的iOS系统运行原来的32位程序时,系统会调用32位的FrameWork作为底层支撑,当系统运行64位程序时,系统会调用64位的FrameWork作为底层支撑。

也就是说,当一个iPhone 5S上同时运行32位程序和64位程序时,系统同时将32位和64位两套FrameWork载入了内存中,所以消耗的内存也比较多。

如果一台64位的iOS设备上运行的所有程序都是为64位系统编译过的,iOS系统将只载入64位的FrameWork,这将节省好多内存。所以,如果大家都可以快速将程序传换成64位的,iOS将跑得更快。真的是“大家好才是真的好”。


出处:http://my.oschina.net/shede333/blog/172785#OSC_h3_10


Xcode 6更新默认不支持armv7s架构http://www.cocoachina.com/ios/20141013/9897.html


4,在ARC下保留dealloc的原因

dealloc 方法中 我们不再被允许调用 [release] 了, 也不允许调用 [super dealloc]。
唯一一个留着 dealloc 方法的原因就是, 你需要释放一些不在 ARC 控制下的资源。 例如 Core Foundation 对象中调用 CFRelease(), 对那些通过 malloc() 分配的内存调用 free(), 注销通知,停止 Tiner, 等等。
如果你是一个对象的代理的话,有时必须显式的断开和它的连接,但通常这都是自动的。 大部分情况下,代理都是弱引用, 当一个即将被释放的对象是其他对象的代理的话, 当这个对象被销毁时,代理指针将会被自动设置为 nil。 弱指针在这之后会被自动清楚。
另外, 在你的 dealloc 方法中, 你仍然可以使用实例变量, 因为他们在这时候还没被释放掉。 在 dealloc 返回之前,都不会被释放。

http://www.2cto.com/kf/201405/299286.html


5,iOS中,在类的源文件(.m)中,@interface部分的作用?

此@interface部分为类扩展(extension)。
其被设计出来就是为了解决两个问题的,其一,定义类私有方法的地方。其二,实现public readonly,private readwrite的property(意思是在h头文件中定义一个属性对外是readonly的,但在类的内部希望是可读写的,所以可以在m源文件中的@interface部分重新定义此属性为readwrite,此时此属性对外是只读的,对内是读写的)。
此外,也可在此部分申明变量和属性,但申明的变量,属性和方法均为私有的,只能够被当前类访问,相当于private。

iOS开发笔记(IOS7回归)iOS开发笔记(IOS7回归)

所以在类中看到类似如下的描述:

iOS开发笔记(IOS7回归)

只是类别(category)的名字叫:prairie,并非跟@private私有 有任何关系。,

6,Objective-C中public、protected、private的使用

@public,@protected,@private
@public 是共有成员,在类本身极其外部都可以访问到,@protected 只有子类和本身可以访问该对象,默认就是这个;@private,这里声明的就是私有成员,只有本类可以访问。

static方法

当方法前是使用"+"来修饰并且声明在头文件中,则说明该方法相当于c++中的static方法,通过类直接调用。但是需要注意的是,虽然这样的方法可以通过类直接调用,但是不可以通过对象调用。
public方法
当方法前是使用"-"来修饰并且声明在头文件中,则该方法可以通过类的对象进行调用。
private方法
Objective-C中的private方法是通过category实现的,在实现文件中我们声明一个类的category,在这里面的方法就是private方法。类的对象是不可以进行调用的,同样由于该方法的声名是在类的实现文件中,所以子类也是不能重写该方法的。

.h文件

#import <Foundation/Foundation.h>

@interface Grammar : NSObject {
 

   @public
        NSString* publicString;
   
    @protected
        NSString* protectedString;
   
    @private
        NSString* privateString;
}

NSString* staticString;

@property (nonatomic, retain) NSString* publicString;

+ (void)staticMethod;
- (void)publicMethod;

@end

.m文件

#import "Grammar.h"

//私有方法以category方式实现
#pragma mark -
#pragma mark Grammar(private)

@interface Grammar(private)

- (void)privateMethod;

@end



#pragma mark -
#pragma mark Grammar

@implementation Grammar

@synthesize publicString;


#pragma mark -
#pragma mark Public Method

+ (void)staticMethod
{
}

- (void)publicMethod
{
}

#pragma mark -
#pragma mark Private Method

- (void)privateMethod
{
}

@end

7,Prefix.pch的作用和用法

TestDemo_Prefix.pch:扩展名.pch表示"precompiled header",这是一个你工程要用到的来自于外部框架的头文件列表。xcode将编译这些头到文件,这将减少在选择BuildBuild and Go时编译项目的时间。通常用到的头文件已经自动包含到了pch(比如:UIKit、Foundation),系统编译每个cpp文件前,都会先include这个文件。这样就节省了添加include的时间。还有就是可以再这里面放入宏,在整个工程中都可以用。

在XCode6中, 默认是没有pch文件的,如果我们想使用pch文件,需要手动添加,添加步骤如下:

iOS开发笔记(IOS7回归)

newFile-Other-empty

iOS开发笔记(IOS7回归)

(1)、找工程的Targets->Build Settings->Apple LLVM 6.0 - Language

iOS开发笔记(IOS7回归)

(2)在Prefix Header下面的Debug和Release下添加$(SRCROOT)/工程名/pch文件

iOS开发笔记(IOS7回归)

8,initWithNibName和initWithCoder

创建了一个nib文件,没有和其他可被实例化的类有直接或间接关系的时候,这个类或这些类(一个nib文件俺也可能包含多个类)是没有机会被实例化的,所以这种情况只是通过ib创建了一个类,而没有实例化.真正的实例化还需要通过在Xcode用代码来读取这个nib文件。所以initWithNibName这个方法是某一个和IB关联的Controller的类,通过Xcode实例化controller的时候会调用的。

initWithCoder是一个类在IB中创建但在xocdde中被实例化时被调用的.比如,通过IB创建一个controller的nib文件,然后在xocde中通过initWithNibName来实例化这个controller,那么这个controller的initWithCoder会被调用.

9,dealloc函数中 super dealloc 的使用时机

我们定义的全局变量都是在 - (void)dealloc 函数中释放的,里面继承了一个[super dealloc]方法,
有些同学平时自己释放内存都是写在 [super dealloc]的后面,但是在Objective-c 中不能这样写,所有的释放都必须写在 [super dealloc]的前面。

-------错误的写法--------
- (void)dealloc
{
[super dealloc];
[XXX release];
......
}
-------正确的写法--------
- (void)dealloc
{
[XXX release];
[super dealloc];
......
}
原因是:“你所创建的每个类都是从父类,根类继承来的,有很多实例变量也会继承过来,这部分变量有时候会在你的程序内使用,它们不会自动释放内存,你需要调用父类的 dealloc方法来释放,然而在此之前你需要先把自己所写类中的变量内存先释放掉,否则就会造成你本类中的内存积压,造成泄漏”。


10,通过GestureRecognizer实现点击任意区域隐藏键盘

基本思想如下:
1. 在ViewController载入的时候,将键盘显示和消失的Notification添加到self.view里。
2. 分别在键盘显示和消失时添加和删除TapGestureRecognizer

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setKeyBoardAutoHidden];
}
- (void)setKeyBoardAutoHidden{
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    //SingleTap Gesture
    UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backgroundTapDismissKeyboard:)];
    
    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    
    //UIKeyboardWillShowNotification
    [notificationCenter addObserverForName:UIKeyboardWillShowNotification object:nil queue:mainQueue usingBlock:^(NSNotification *note) {
        [self.view addGestureRecognizer:singleTapGesture];
    }];
    
    //UIKeyboardWillHideNotification
    [notificationCenter addObserverForName:UIKeyboardWillHideNotification object:nil queue:mainQueue usingBlock:^(NSNotification *note) {
        [self.view addGestureRecognizer:singleTapGesture];
    }];

}
- (void) backgroundTapDismissKeyboard:(UIGestureRecognizer *) gestureRecognizer{
    //将self.view里所有的subview的first responder 都resign掉
    [self.view endEditing:YES];
}


11,NavigationBar中通过code方式对背景颜色和title字体颜色更改

背景颜色:

[self.navigationController.navigationBar setBarTintColor:[UIColor colorWithRed:20/255.0 green:155/255.0 blue:213/255.0 alpha:1.0]];
字体属性修改:

    [self.navigationController.navigationBar setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
                                                                     [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0],
                                                                     UITextAttributeTextColor,
                                                                     [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8],
                                                                     UITextAttributeTextShadowColor,
                                                                     [NSValue valueWithUIOffset:UIOffsetMake(0, -1)],
                                                                     UITextAttributeTextShadowOffset,
                                                                     [UIFont fontWithName:@"Arial-Bold" size:0.0],
                                                                     UITextAttributeFont,nil]]; 
有关此属性的官方链接:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationBar_Class/index.html#//apple_ref/doc/uid/TP40006887

12,如何理解 File's Owner 与 First Responder

xib和nib

简单地说,就是xib和nib都是一些对象的描述,而前者是xml格式,后者是一种二进制格式。二者的使用上没有什么区别,xcode/IB是两种格式都支持的。xib比nib有个很明显的好处,就是xib可以很方便地进行diff操作。xib是文本文件,所以在版本控制方面比nib有优势。可能有人会说,反序列化的时候,xib肯定比nib慢很多吧。这个不需要担心的,因为在build的时候,xcode会把xib都转换为nib。最终用户使用的将会是nib内容,而不是xib。

File's Owner

File's Owner 表示视图控制器。UIViewController(或其子类)在生成的时候,首先会寻找相应的.xib去生成,于是controller的实例(instance)就把.xib载入内存,并成为FIle's Owner。所以我们定义的controller是这个.xib的custom class。并且需要把这个FIle Owner上的outlet连到某个控件上去。(Action也同样道理)

它不一定就是某个viewcontroller,换个角度,如果我们看.xib文件,发现它有个File Owner。其实就是我们用来设定,究竟是那个Object来读取并载入这个.xib文件,也就是说,谁own这个文件。

[[NSBundle mainBundle] loadNibNamed:@"UnitTableCell" owner:self options:nil] IB里面创造都是一些对象 filer'owner意思是这些对象创建出来后要挂载到那个对象里面 (owner:self)

First Responder

First Responder 在用户与屏幕交互时变化。例如,假设有一个表单。当用户触摸表单中的某个文本域时,那个文本域将成为活动文本域,并担当 First Responder 的角色。如实现触摸某个位置关闭当前键盘的事件中:[currentTextField resignFirstResponder];就是告诉receiver,“currentTextField以完成任务,请求辞去 First Responder 的职务。

13,ScrollView怎样才能上下或左右滑动?

scrollerView能否划动是由它的contentSize 和frame.size来决定的, 只有内容比实际的显示尺寸大scroll才可以划动。

contentSize

   [self.mScrollView setContentSize:CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height)];<span style="font-size: 14px;">//设置scrollView的滚动范围</span>
<pre name="code" class="objc" style="color: rgb(51, 51, 51); font-size: 14px; line-height: 26px;">contentOffset 


   scrollView.contentOffset = CGPointMake(0, 200);<span style="font-family: Arial;">// 设置scrollView的滚动偏移量</span>

contentInset默认 UIEdgeInsetsZero,用来设置scrollView的额外滚动区域。
   scrollView.contentInset = UIEdgeInsetsMake(100, 0, 0, 0);<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">// 设置scrollView额外顶部滚动区域:(UIEdgeInsetsMake是逆时针设置,上左下右)</span>
bounces
   scrollView.bounces = NO;//<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14px; line-height: 26px;">默认为YES,用来设置scrollView的弹簧效果</span>



14,如何创建CGColorRef在view.layer.borderColor上使用?

方法一:写RGB转换方法

+(CGColorRef) getColorFromRed:(int)red Green:(int)green Blue:(int)blue Alpha:(int)alpha  
{  
    CGFloat r = (CGFloat) red/255.0;  
    CGFloat g = (CGFloat) green/255.0;  
    CGFloat b = (CGFloat) blue/255.0;  
    CGFloat a = (CGFloat) alpha/255.0;    
    CGFloat components[4] = {r,g,b,a};  
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();  
  
    CGColorRef color = (CGColorRef)[(id)CGColorCreate(colorSpace, components) autorelease];  
    CGColorSpaceRelease(colorSpace);  
      
    return color;  
}

方法二:直接利用UIColor的CGColor属性

    textview.layer.borderColor = [UIColor darkGrayColor].CGColor;
    
    UIColor *customColor  = [UIColor colorWithRed:123/255.0 green:123/255.0 blue:123/255.0 alpha:1.0];
    textview.layer.borderColor = customColor.CGColor;


15,ios中实现多行输入的UITextField

UITextField本身不支持多行输入,我们也没必要去重写重绘,简单点来说用UITextView便可实现:

    UITextView *textview = [[UITextView alloc] initWithFrame:CGRectMake(50, 120, 300, 200)];
    textview.layer.cornerRadius = 6;
    textview.layer.masksToBounds = YES;
    //textview.layer.borderColor = [UIColor darkGrayColor].CGColor;
    UIColor *customColor  = [UIColor colorWithRed:123/255.0 green:123/255.0 blue:123/255.0 alpha:1.0];
    textview.layer.borderColor = customColor.CGColor;
    textview.layer.borderWidth = 2.0;
    [self.view addSubview:textview];
效果图如下:

iOS开发笔记(IOS7回归)

16,UITextField、UITextView 键盘遮挡住输入框,如何上移View使之显示

将输入框所对应的ViewController.h设置实现了UITextFieldDelegate或UITextViewDelegate协议

在ViewController.m文件中实现UITextFieldDelegate的三个方法:

#pragma -mark UITextField Delegate
-(void)textFieldDidBeginEditing:(UITextField *)textField{
    
    CGRect frame = textField.frame;
    
	//在这里我多加了62,(加上了输入中文选择文字的view高度)这个依据自己需求而定
    int offset = (frame.origin.y+62)-(self.view.frame.size.height-216.0);//键盘高度216

    [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
    
    [UIView setAnimationDuration:0.30f];//动画持续时间
    
    if (offset>0) {
	//将视图的Y坐标向上移动offset个单位,以使下面腾出地方用于软键盘的显示
        self.view.frame = CGRectMake(0.0f, -offset, self.view.frame.size.width, self.view.frame.size.height);
        [UIView commitAnimations];
    }
    
}

/**
 *当用户按下return键或者按回车键,我们注销KeyBoard响应,它会自动调用textFieldDidEndEditing函数
 */
-(BOOL)textFieldShouldReturn:(UITextField *)textField{

    [textField resignFirstResponder];
    
    return YES;
}


-(void)textFieldDidEndEditing:(UITextField *)textField{
	//输入框编辑完成以后,当键盘即将消失时,将视图恢复到原始状态
    self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
    
}


17,终端中执行purge命令:Unable to purge disk buffers: Operation not permitted

OSX 10.9 Mavericks系统下的内存清理命令改了,执行:sudo purge 即可 执行时会提示要求输入管理员密码。



18,设置RGB颜色一个非常有用的宏

#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
使用方式:

[[UINavigationBar appearance] setBarTintColor:UIColorFromRGB(0x067AB5)];

19,Interface Builder could not open the document "xxx.xib" because it does not exist.

iOS开发笔记(IOS7回归)

解决办法:选中项目-Target-Build Phases-Compile Sources,删除相应不存在的的文件

iOS开发笔记(IOS7回归)


20,UITextField、UITextView 响应 键盘的return(完成键)

实现UITextFieldDelegate:

#pragma -mark UITextFieldDelegate
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
    
    NSString *userIdStr = [userIdField text];
    [self doLogin:userIdStr];
    
    return YES;
}
但是 UITextView的代理UITextViewDelegate 里面并没有这样的回调。
但是有别的方法可以实现,UITextViewDelegate里面有这样一个代理函数:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text

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

    return YES;
}

21,iOS7实现带文本输入框的UIAlertView及获取TextField文本内容

实现:

    if (customAlertView==nil) {
        customAlertView = [[UIAlertView alloc] initWithTitle:@"自定义服务器地址" message:nil delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
    }
    [customAlertView setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
    
    UITextField *nameField = [customAlertView textFieldAtIndex:0];
    nameField.placeholder = @"请输入一个名称";
    
    UITextField *urlField = [customAlertView textFieldAtIndex:1];
    [urlField setSecureTextEntry:NO];
    urlField.placeholder = @"请输入一个URL";
    urlField.text = @"http://";
    
    [customAlertView show];

实现UIAlertViewDelegate

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{

    if (buttonIndex == alertView.firstOtherButtonIndex) {
        UITextField *nameField = [alertView textFieldAtIndex:0];
        UITextField *urlField = [alertView textFieldAtIndex:1];
        //TODO
    }
    
    
    
}

效果:

iOS开发笔记(IOS7回归)


22,viewDidLoad, viewWillDisappear, viewWillAppear等区别及各自的加载顺序

当一个视图控制器被创建,并在屏幕上显示的时候。 代码的执行顺序


1、 alloc 创建对象,分配空间


2、init (initWithNibName) 初始化对象,初始化数据


3、loadView 从nib载入视图 ,通常这一步不需要去干涉。除非你没有使用xib文件创建视图


4、viewDidLoad 载入完成,可以进行自定义数据以及动态创建其他控件


5、viewWillAppear 视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了


6、viewDidAppear 视图已在屏幕上渲染完成


当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反


1、viewWillDisappear 视图将被从屏幕上移除之前执行


2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了


3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放


23,ios7中viewDidLoad和viewWillAppear中self.view.frame的size不一致

在viewDidLoad里打印self.view.frame.size.height是568,在viewWillAppear方法打印self.view.frame.size.height都是480,很奇怪,原来是因为我用xib创建了view,xib那里size设置的是retina4,但是模拟器(真机)是retina3.5;

iOS开发笔记(IOS7回归)iOS开发笔记(IOS7回归)

viewdidload里面初始化的是4寸的view 568高度 但是在显示之前 就是viewwillappear这个函数里面发现是3.5的手机 然后他就自适应成了3.5的尺寸 也就是480的高度了之后所有的地方都是480了 。


24,UIAlertView和UIAlertController通过文本框内容判断让按钮动态可用或不可用

先说UIAlertView中如何实现

    customAlertView = [[UIAlertView alloc] initWithTitle:@“title” message:nil delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];
    [customAlertView setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
    
    UITextField *nameField = [customAlertView textFieldAtIndex:0];
    nameField.placeholder = getString(@"inputalias");
    
    UITextField *urlField = [customAlertView textFieldAtIndex:1];
    [urlField setSecureTextEntry:NO];
    urlField.text = @"http://";
    
    [customAlertView show];

需要实现UIAlertViewDelegate

#pragma -mark UIAlertViewDelegate
-(BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView{

    UITextField *aliasfield = [alertView textFieldAtIndex:0];
    UITextField *urlfield =  [alertView textFieldAtIndex:1];
    
    //alias和url都有值才可以保存
    if (aliasfield.text.length>0 && urlfield.text.length>7) {
        return YES;
    }
    
    return NO;
}

看一下效果:

iOS开发笔记(IOS7回归)

当我们输入符合规定的字符后,alertViewShouldEnableFirstOtherButton(每当有事件发生,会不断回调此函数)中做判断,返回YES的话,右侧OK的按钮就可用了。

UIAlertController

如果我们想要实现UIAlertView中的委托方法alertViewShouldEnableOtherButton:方法的话可能会有一些复杂。假定我们要让“登录”文本框中至少有3个字符才能**“好的”按钮。很遗憾的是,在UIAlertController中并没有相应的委托方法,因此我们需要向“登录”文本框中添加一个Observer。Observer模式定义对象间的一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。我们可以在构造代码块中添加如下的代码片段来实现。

[alertController addTextFieldWithConfigurationHandler:^(UITextField *textField){
    ...
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(alertTextFieldDidChange:) name:UITextFieldTextDidChangeNotification object:textField];
}];

当视图控制器释放的时候我们需要移除这个Observer,我们通过在每个按钮动作的handler代码块(还有其他任何可能释放视图控制器的地方)中添加合适的代码来实现它。比如说在okAction这个按钮动作中:

UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    ...
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UITextFieldTextDidChangeNotification object:nil];
}];

在显示对话框之前,我们要冻结“好的”按钮

okAction.enabled = NO;

接下来,在通知观察者(notification observer)中,我们需要在**按钮状态前检查“登录”文本框的内容。

- (void)alertTextFieldDidChange:(NSNotification *)notification{
    UIAlertController *alertController = (UIAlertController *)self.presentedViewController;
    if (alertController) {
        UITextField *login = alertController.textFields.firstObject;
        UIAlertAction *okAction = alertController.actions.lastObject;
        okAction.enabled = login.text.length > 2;
    }
}
iOS开发笔记(IOS7回归)


25,iOS:关于获取网络类型和运营商信息

Apple的Reachability Sample看起来不错,但是只可以判断是否连接到互联网和是否连接Wifi,但是无法判断运营商网络类型(2G/3G等)。

第一种方法就是尝试从状态栏中获取网络类型,代码如下:【私有API】

+(NSString *)getNetWorkStates{
UIApplication *app = [UIApplication sharedApplication];
NSArray *children = [[[app valueForKeyPath:@"statusBar"]valueForKeyPath:@"foregroundView"]subviews];
    NSString *state = [[NSString alloc]init];
    int netType = 0;
//获取到网络返回码
    for (id child in children) {
if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {
            //获取到状态栏
            netType = [[child valueForKeyPath:@"dataNetworkType"]intValue];

            switch (netType) {
                case 0:
                    state = @"无网络";
                    //无网模式
                    break;
                case 1:
                    state = @"2G";
                    break;
                case 2:
                    state = @"3G";
                    break;
                case 3:
                    state = @"4G";
                    break;
                case 5:
                {
                    state = @"WIFI";
                }
                    break;
                default:
                    break;
            }
        }
    }
//根据状态选择
    return state;
}
基本原理是从UIApplication类型中通过valueForKey获取内部属性statusBar。然后筛选一个内部类型(UIStatusBarDataNetworkItemView),最后返回他的dataNetworkType属性,根据状态栏获取网络状态,可以区分2G、3G、4G、WIFI,系统的方法,比较快捷,不好的是万一连接的WIFI没有联网的话,识别不到。

第二种方法是通过SoftwareUpdateServices.framework中的SUNetworkMonitor类型来获取,参考SO链接。同样也是私有API。

第三种方法是iOS 7中的公有API,在CTTelephonyNetworkInfo类型中,官方文档相关API的说明https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/CTTelephonyNetworkInfo/index.html

实现起来就是使用CTTelephonyNetworkInfo类型的currentRadioAccessTechnology方法,代码如下:

#import <CoreTelephony/CTTelephonyNetworkInfo.h>
@property (strong, nonatomic)CTTelephonyNetworkInfo *networkInfo;
self.networkInfo = [[CTTelephonyNetworkInfo alloc] init];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkChanged) name:CTRadioAccessTechnologyDidChangeNotification object:nil];
- (void)networkChanged{

    //网络改变,做相关的操作,注意非UI线程
    dispatch_async(dispatch_get_main_queue(), ^{
        self.networkLabel.text = state;
    });

}


26,使用NSMutableURLRequest以form表单方式POST请求

直接举例:

    NSURL* nsurl = [NSURL URLWithString:@"http://www.test.com/login"];
    NSMutableURLRequest* request = [[NSMutableURLRequest alloc]init];
    [request setURL:nsurl];
把下面的代码,拷贝到您的项目中即可:

/**
 * 设置POST以Form表单方式请求
 **/
+ (void)setFormDataRequest:(NSMutableURLRequest *)request fromData:(NSDictionary *)formdata{
    
    NSString *boundary = @"12436041281943726692693274280";
    
    //设置请求体中内容
    NSMutableString *bodyString = [[NSMutableString alloc]init];
    int count = (int)([[formdata allKeys] count]-1);
    for (int i=count; i>=0; i--) {
        
        NSString *key = [formdata allKeys][i];
        NSString *value = [formdata allValues][i];
        if ([key isEqualToString:@"accessToken"]) {
            value = [value substringToIndex:32];
        }
        
        [bodyString appendFormat:@"-----------------------------%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n",boundary,key,value];
    }
    
    [bodyString appendFormat:@"-----------------------------%@--\r\n", boundary];
    NSMutableData *bodyData = [[NSMutableData alloc]initWithLength:0];
    NSData *bodyStringData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];
    [bodyData appendData:bodyStringData];
    
    NSString *contentLength = [NSString stringWithFormat:@"%ld",(long)[bodyData length]];
    
    NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=---------------------------%@", boundary];

    [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
    [request setValue:contentLength forHTTPHeaderField:@"Content-Length"];
    [request setHTTPBody:bodyData];
    [request setHTTPMethod:@"POST"];
    
}

请求的时候我们会发现,HTTPBody的内容是这样:

-----------------------------12436041281943726692693274280
Content-Disposition:form-data;name="from"

0
-----------------------------12436041281943726692693274280
Content-Disposition:form-data;name="role"

0
-----------------------------12436041281943726692693274280
Content-Disposition:form-data;name="password"

000000
-----------------------------12436041281943726692693274280
Content-Disposition:form-data;name="username"

13800001380
-----------------------------12436041281943726692693274280--


而Content-Type是这样:

multipart/form-data; boundary=---------------------------12436041281943726692693274280,contentLength:525


27,Xcode6 使用NSUserDefault 的 plist文件存储位置

在Xcode5甚者之前,我们知道,如果用模拟器运行APP,想知道NSUserDefault的plist存放路径是这样的:/Users/username/Library/Application Support/iPhone Simulator/模拟器版本/Applications/UDID/Library 的Preferences文件夹下,自己程序命名.plist

在Xcode6中,程序对使用NSUserDefault方式创建的plist文件的位置进行了更换,具体路径为:/Users/username/Library/Developer/CoreSimulator/Devices/模拟器UDID/data/Library,Preferences文件夹下,如下图:

iOS开发笔记(IOS7回归)

28,使用NSUserDefaults 读取和写入自定义对象(Attempt to set a non-property-list object as an NSUserDefaults value for 错误)

众所周知,NSUserDefaults只能保存诸如NSArray、NSDictionary、NSData、NSNumber等基本数据类型,如果我们强制保存自定义的类,就会出现这个错误:Attempt to set a non-property-list object as an NSUserDefaults value for ,解释起来:【试图将一个非属性列表对象设置为 NSUserDefaults】接下来就说说如何吧自定义的对象保存到NSUserDefaults中去。

自定义的类实现<NSCoding>协议中的- (id) initWithCoder: (NSCoder *)coder方法和- (void) encodeWithCoder: (NSCoder *)coder方法

#pragma mark NSCoding
- (id)initWithCoder:(NSCoder *)aDecoder{

    if (self == [super init]) {
        alias = [aDecoder decodeObjectForKey:JSON_NAME];
        mobile = [aDecoder decodeObjectForKey:JSON_MOBILE];
        signtime = [[aDecoder decodeObjectForKey:JSON_TIMESTAMP] longValue];
        endtime = [[aDecoder decodeObjectForKey:JSON_END_TIME] longValue];
        cmobile = [aDecoder decodeObjectForKey:JSON_CMOBILE];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{

    [aCoder encodeObject:alias forKey:JSON_NAME];
    [aCoder encodeObject:mobile forKey:JSON_MOBILE];
    [aCoder encodeObject:[NSNumber numberWithLong:signtime] forKey:JSON_TIMESTAMP];
    [aCoder encodeObject:[NSNumber numberWithLong:endtime] forKey:JSON_END_TIME];
    [aCoder encodeObject:cmobile forKey:JSON_CMOBILE];

}

保存到NSUSerDefault:

    Terminal *terminal = [[Terminal alloc] init];
    
    terminal.alias = [dict objectForKey:JSON_NAME];
    terminal.mobile = [dict objectForKey:JSON_MOBILE];
    terminal.signtime = [[dict objectForKey:JSON_TIMESTAMP] longValue];
    terminal.endtime = [[dict objectForKey:JSON_END_TIME] longValue];
    terminal.cmobile = [dict objectForKey:JSON_CMOBILE];
    
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:terminal];
    
    [userDefaults setObject:data forKey:"test"];
    
    [userDefaults synchronize];

也就是说,我们保存自定义对象时,是使用NSKeyedArchiver 把数据归档为NSData对象,然后把NSData存储到UserDefault中,NSData相当于Model

iOS开发笔记(IOS7回归)

读取:

    NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
    
    NSData *data =  [userDefaults objectForKey:"test"];
    
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
读取自定义对象时,先获取到NSData,然后使用NSKeyedUnarchiver解档为自定义的对象

LOG输出,查看结果:

2014-12-1016:31:11.815ESO_Etws[1463:60b]alias:Q611-0334
2014-12-1016:31:11.815ESO_Etws[1463:60b]mobile:13841040334
2014-12-1016:31:11.815ESO_Etws[1463:60b]signtime:1394529151000
2014-12-1016:31:11.816ESO_Etws[1463:60b]endtime:1426065151000
2014-12-1016:31:11.816ESO_Etws[1463:60b]cmobile:

PS:
APP升级后,UserDefaults中原有的plist是不会删除的,除非用户卸载APP
清除整个UserDefaults数据的方法:
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];

29,ios APP初次安装以及版本更新后,判断是否需要显示引导页的实现方法

获取APP版本号,将版本号作为Key(比如Bool类型),存储在NSuserDefault中,初此安装打开时,key是不存在的,即进入引导页面,之后将此key保存起来(保证前面的判断不会再进入)

app升级后,判断新版本号的key,发现没有,即显示新版本的引导页面,然后将Key保存起来,以此类推。


30,配置iOS项目的设备系统目标设置:Base SDK和Deployment Target

Xcode为开发者提供了两个可配置的设置:第一个是Base SDK,第二个是iOS的Deployment Target。通过配置这两个参数可定制应用的功能以及可运行的设备和操作系统版本。


打开配置界面的操作如下:

1, 打开工程,然后选择工程导航面板上的工程文件;
2,在编辑器面板上选择**TARGETS**,再选择**Build Settings**选项卡,Base SDK设置通常是这里的第三个选项,Deployment Target在Deployment下,但在这个面板 上寻找设置的最简单办法是在搜索条中搜索。


1. 配置Base SDK设置
Base SDK,指的是当前编译所用的SDK 版本。
可以将值改为“Latest iOS SDK”或者是开发机器上安装的任意版本的SDK。Base SDK设置会引导编译器使用该版本的SDK编译和构建应用,也就是说,它会直接控制应用使用哪些API。默认情况下,Xcode中创建的新工程总是使用最新版本的SDK,而苹果会处理API的废弃。除非你有充分的理由,否则你应该使用这个默认值。


2. 配置Deployment Target设置
Deployment Target,它控制着运行应用需要的最低操作系统版本。
如果你将它设成了特定版本,比如5.0,App Store会自动阻止运行早期操作系统的用户下载或安装这个应用。要满足较多用户的需求,我建议至少向后兼容操作系统的上一个版本。举个例子,如果iOS 6是最新的版本,那么至少应该支持iOS 5。可以在设置Base SDK所在的Building Settings选项卡中设置Deployment Target。
如果你使用iOS 6 SDK中可用的功能,又想支持早期版本,可以将Base SDK设置为最新的SDK(iOS 6),而将Deployment Target至少设置为iOS 5。不过,如果你的应用运行在iOS 5设备上,一些框架和功能可能不能用。开发人员的职责就是让其应用适应这种情况,能够正确工作而不会崩溃。

31,ios 真机调试时出现CopyPngFile error问题

有时我们在模拟器上调试程序,发现没有问题,到了真机,出现了CopyPngFile Error的错误,简单来说,回想自己是否最近有修改过图片资源?最常见的现象来自于直接把jpg格式的图片文件后缀改为png,就会出现这个问题。

解决办法,把直接修改后缀的图片删除掉,双击jpg,使用系统预览功能,编辑图片,然后导出为PNG,然后再导入到工程里,这个问题就解决了。


32,通过系统的钥匙串访问,创建Development或Production私钥证书(Certificates)

在菜单中依次选择 证书助理——从证书颁发机构请求证书:

iOS开发笔记(IOS7回归)

在打开的窗口输入电子邮件和常用名称,并选择存储到磁盘以及让我指定**对信息

iOS开发笔记(IOS7回归)

单击继续,在打开的窗口设定文件名称和位置,点击继续,**大小选择2048位,算法选择RSA

iOS开发笔记(IOS7回归)

点击继续,则在之前设定的位置生成了.CSR的签名文件。默认名称是CertificateSigningRequest.certSigningRequest

然后在https://developer.apple.com 上传这个文件就好了,上传成功后,apple服务器便会生成一个识别本计算机的Development或distribution证书:ios_distribution.cer,点击下载到电脑上,双击就可以自动安装到钥匙串了,至此私钥生成完毕,接下来创建Provisioning Profiles的时候,别忘记勾选上我们自己的Development或是distribution证书:

iOS开发笔记(IOS7回归)


32,ios6上传APP错误:iTunes Store operation failed和Archive validation error错误

一般情况下出现此错误是因为连接apple服务器出了问题,建议稍后多尝试几次,如果一直都不行的话,如下方法:

1,更换本机IP地址(如果是DHCP),然后重新Validate或Submit,我就是这么成功的;

2,更换WiFi网络环境尝试;

3,实在不行使用Xcode-Open Developer Tool-Application Loader工具进行上传,具体使用方法:Build应用程序,生成的APP文件在(Users/Library/Developer/Xcode/DerivedData/APPUUID名称/Build/Products/Debug-iphoneos/APP.app),压缩App.app,然后通过Application Loader导入就可以了,验证流程和Organizer一样,不过也可能会出现同样的错误提示;http://*.com/questions/25800830/archive-validation-error

4,最后一个办法,也是终极大法,找MAC下可用的VPN,然后切换VPN网络(),再尝试上传;

iOS开发笔记(IOS7回归)

这个WARNING是说,我的APP没有支持64位,可以在工程Build Settings Architectures设置。


33, NSDictionary如何判断是否包含某个key?

   NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"yang",@"name","man",@"sex", nil];
    if ([dict objectForKey:@"age"]) {
        NSLog(@"字典包含key:age");
    }
    else{
        NSLog(@"字典不包含key:age");
    }

34,UIButtontitlelabel.text 不起作用?

aaa@qq.com"name";设置后运行发现该按钮没有显示name,原因是button设置title的同时,还要设置相应的state,如下:

[button setTitle:@"name" forState:UIControlStateNormal];

35,如果UIView B为半透明, 如何让加载上边的UIView C不透明?

有一个UIView A, 然后上边加载了UIView B 并设置Alpha 为0.8左右的半透明, 现在我想在B上边再加个UIView C 并设置为不透明. 可是无论我设置Alpha 为1也好 还是打了opaque也好 都还是能看到A view上的内容,效果如下:

iOS开发笔记(IOS7回归)

解决办法,先说原理ViewC之所以透明 是因为父视图的alpha值会影响到子视图的alpha值 使得子视图透明,uiview的alpha值会被传递,但是color不会被传递,所以:

方法一:UIViewB的透明度不要使用alpha,换成uiviewb.backgroundColor = [UIColor colorWithWhite:0 alpha:0.8] 这样一样可以做出透明的效果

方法二:把ViewC的父视图改成ViewA(并且添加在ViewB之后),这样ViewB相当于只是一个背景夹层,它的alpha值并不会影响到ViewC

效果如下图:

iOS开发笔记(IOS7回归)




版权声明:本文为博主原创文章,未经博主允许不得转载。