iOS開發筆記(IOS7迴歸)



<---點擊左上角目錄,可以快速查找這裏是否有你遇到的問題條目<---


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

舊工程配置arc方案:

1,直接在targets->build phases中修改compiler Flags,是否支持arc。添加:-fobjc-arc,就可以讓舊項目支持arc。如果想讓原來支持arc的不使用arc則添加-fno-objc-arc


因爲在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) [expression autorelease]
#define PX_RELEASE(expression) [expression release]
#define PX_RETAIN(expression) [expression retain]
#endif

#endif

說明:在arc中,strong對應原來的retain與copy,weak對應原來的assign。

EX:舉例使用autorelease:

NSArray *testArray = PX_AUTORELEASE([[NSArray allocinit]);
 //如果支持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 {

[[NSNotificationCenterdefaultCenterremoveObserver:self];//如果註冊了通知的話。

[self removeObserver:self forKeyPath:keyPath];//如果註冊了kvo的話。

#if !__has_feature(objc_arc)  //在這裏也需要判斷是否支持arc,支持的話就執行舊工程中該release的語句.
    [array release]; //array代表alloc但沒有autorelease的變量
    [super dealloc];
#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。


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


只是類別(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文件,需要手動添加,添加步驟如下:


newFile-Other-empty


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


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


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];
效果圖如下:


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.


解決辦法:選中項目-Target-Build Phases-Compile Sources,刪除相應不存在的的文件



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
    }
    
    
    
}

效果:



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;


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;
}

看一下效果:


當我們輸入符合規定的字符後,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;
    }
}


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文件夾下,如下圖:


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


讀取:

    NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults];
    
    NSData *data =  [userDefaults objectForKey:"test"];
    
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
讀取自定義對象時,先獲取到NSData,然後使用NSKeyedUnarchiver解檔爲自定義的對象

LOG輸出,查看結果:

2014-12-10 16:31:11.815 ESO_Etws[1463:60b] alias:Q611-0334
2014-12-10 16:31:11.815 ESO_Etws[1463:60b] mobile:13841040334
2014-12-10 16:31:11.815 ESO_Etws[1463:60b] signtime:1394529151000
2014-12-10 16:31:11.816 ESO_Etws[1463:60b] endtime:1426065151000
2014-12-10 16:31:11.816 ESO_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)

在菜單中依次選擇 證書助理——從證書頒發機構請求證書:


在打開的窗口輸入電子郵件和常用名稱,並選擇存儲到磁盤以及讓我指定密鑰對信息


單擊繼續,在打開的窗口設定文件名稱和位置,點擊繼續,密鑰大小選擇2048位,算法選擇RSA


點擊繼續,則在之前設定的位置生成了.CSR的簽名文件。默認名稱是CertificateSigningRequest.certSigningRequest

然後在https://developer.apple.com 上傳這個文件就好了,上傳成功後,apple服務器便會生成一個識別本計算機的Development或distribution證書:ios_distribution.cer,點擊下載到電腦上,雙擊就可以自動安裝到鑰匙串了,至此私鑰生成完畢,接下來創建Provisioning Profiles的時候,別忘記勾選上我們自己的Development或是distribution證書:



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://stackoverflow.com/questions/25800830/archive-validation-error

4,最後一個辦法,也是終極大法,找MAC下可用的VPN,然後切換VPN網絡(翻牆),再嘗試上傳;


這個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,UIButton titlelabel.text 不起作用?

button.titlelabel.text=@"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上的內容,效果如下:


解決辦法,先說原理ViewC之所以透明 是因爲父視圖的alpha值會影響到子視圖的alpha值 使得子視圖透明,uiview的alpha值會被傳遞,但是color不會被傳遞,所以:

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

方法二:把ViewC的父視圖改成ViewA(並且添加在ViewB之後),這樣ViewB相當於只是一個背景夾層,它的alpha值並不會影響到ViewC

效果如下圖:





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章