顯示具有 iPhone SDK 標籤的文章。 顯示所有文章
顯示具有 iPhone SDK 標籤的文章。 顯示所有文章

2015年5月23日 星期六

如何修改App Store中App Preview所顯示的語言

 




最近發現很多遊戲在App Store上架時,
不論本身的內容或介面是否本地化與否,
在App Preview頁面中所顯示的"語言"都會標示為英文居多;

剛巧有朋友在問這個問題,
所以打在Blog中記錄一下。

其實這個參數的顯示,
是取決於ipa的內容,
開發者是不能在iTunesConnect網站上修改的,
即是說要修改這部份就一定要提交新的ipa才可以;

而做法很簡單,
只要加入一個文件夾即可,
文件夾內容如下:




繁體中文:zh-Hant.lproj
簡體中文:zh-Hans.lproj

只要把想要的"語言"文件夾加到.app之中,
再生成ipa並提交到Apple,
上架時App Preview頁面中所顯示的"語言"就會顯示為你想要的語言了
(P.S. Mac App做法也是一樣)


2012年1月21日 星期六

設定UILocalNotification重複星期日至六

iOS內的時間App重複設定可以設為星期日至六,
我想它是用UILocalNotification設定重複提示所做成的。
以下是實現代碼
// 獲得當前的時分秒
NSDate* now = [NSDate date];
NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"HH:mm:ss"];
NSString* nowTime = [formatter stringFromDate:now];
NSDate* nowDate = [formatter dateFromString:nowTime];
[formatter release];

NSArray* strArr = [NSArray arrayWithObjects:@"Sunday", @"Monday", @"Tuesday", @"Wednesday", @"Thursday", @"Friday", @"Saturday", nil];
for(NSUInteger i = 0; i < 7; i++)
{
  UILocalNotification* localNotification = [[UILocalNotification alloc] init];
  if (localNotification == nil)
    continue;
  // 設定提示日期,本來日期已過Local Notification是不可被schedule的,
  // 重複是例外,我以1970年1月1日為單位加3天就是星期天,
  // 再加上今天我時間差,就可以每天都是在同一時間來重複提示
  localNotification.fireDate = [NSDate dateWithTimeIntervalSince1970:60*60*24*(3+i)+[nowDate timeIntervalSince1970]];
  localNotification.alertBody = [strArr objectAtIndex:i];

  // 設定每星期也重複
  localNotification.repeatCalendar = [NSCalendar currentCalendar];   localNotification.repeatInterval = NSWeekdayCalendarUnit;

  localNotification.alertAction = @"View";
  localNotification.soundName = UILocalNotificationDefaultSoundName;   [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];   [localNotification release];
}

2011年10月9日 星期日

iOS NSString to NSDate/NSDate to NSString

NSString to NSDate
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
// 把時間設定成世界時間
//[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate* date = [dateFormatter dateFromString:@"Date String"];
[dateFormatter release];

NSDate to NSString
NSDateFormatter* outputFormatter = [[NSDateFormatter alloc] init];
[outputFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString* dateString = [outputFormatter stringFromDate:date];
[outputFormatter release];

2011年10月3日 星期一

iOS Local Notification

又是時候把學習了的總理一下,
再記錄下來了,
今次的是Local Notification

1.即時的 Local Notification
UIBackgroundTaskIdentifier bgTask;
- (void)applicationDidEnterBackground:(UIApplication *)application {
if(bgTask == UIBackgroundTaskInvalid)
{
UIApplication* app = [UIApplication sharedApplication];

// 開啟了BackgroundTask就要以令以下的queue在Background/Foreground Task都可以運行
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"System Expiration End Background Task");
[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];

// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UILocalNotification* notification = [[UILocalNotification alloc] init];
notification.alertBody = @"Test";
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
[notification release];

[app endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
});
}
}

2.Schedule Local Notification
UILocalNotification* notification = [[UILocalNotification alloc] init];
notification.alertBody = @"Test";
// 在當前時間10秒後發動
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:10];
// Alert View 不會顯示 View button
notification.hasAction = FALSE;
notification.userInfo = [NSDictionary dictionaryWithObject:@"Key" forKey:@"Key"];
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
[notification release];

3.Cancel Local Notification
// 在 scheduled 的 Local Notification Array 找出要的 Local Notification Object
NSUInteger idx = [[UIApplication sharedApplication].scheduledLocalNotifications indexOfObjectPassingTest:
^(id obj, NSUInteger idx, BOOL *stop)
{
NSDictionary* userData = ((UILocalNotification*)obj).userInfo;
NSInteger key = [[userData valueForKey:@"Key"] integerValue];
if([key isEqualToString:@"Key"])
{
*stop = TRUE;
return YES;
}
return NO;
}];

if(idx != NSNotFound)
{
UILocalNotification* notification = [[UIApplication sharedApplication].scheduledLocalNotifications objectAtIndex:idx];
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}

2011年8月16日 星期二

令UIImageView擁有簡單的Move, Zoom, Delete, Rotate功能

- (void)viewDidLoad
{
UIImageView* iconView = [[UIImageView alloc] initWithImage:icon];
[iconView setCenter:CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2)];
UILongPressGestureRecognizer* longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[iconView addGestureRecognizer:longPressGesture];
[longPressGesture release];
UIRotationGestureRecognizer* rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[iconView addGestureRecognizer:rotationGesture];
[rotationGesture release];
UIPinchGestureRecognizer* pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[iconView addGestureRecognizer:pinchGesture];
[pinchGesture release];
UIPanGestureRecognizer* panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[iconView addGestureRecognizer:panGesture];
[panGesture release];
iconView.userInteractionEnabled = TRUE;
[self.view addSubview:iconView];
}

#pragma mark -
#pragma mark Gesture Callback
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer.state == UIGestureRecognizerStateBegan)
{
UIView *piece = gestureRecognizer.view;
CGPoint locationInView = [gestureRecognizer locationInView:piece];
CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];

piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
}
}

-(void)handleGesture:(UIGestureRecognizer*)gestureRecognizer
{
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];

// Rotate
if([gestureRecognizer isMemberOfClass:[UIRotationGestureRecognizer class]])
{
UIRotationGestureRecognizer* rotationGestureRecognizer = (UIRotationGestureRecognizer*)gestureRecognizer;
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged)
{
[rotationGestureRecognizer view].transform = CGAffineTransformRotate([[rotationGestureRecognizer view] transform], [rotationGestureRecognizer rotation]);
[rotationGestureRecognizer setRotation:0];
}
}
// Zoom
else if([gestureRecognizer isMemberOfClass:[UIPinchGestureRecognizer class]])
{
UIPinchGestureRecognizer* pinchGestureRecognizer = (UIPinchGestureRecognizer*)gestureRecognizer;
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged)
{
[pinchGestureRecognizer view].transform = CGAffineTransformScale([[pinchGestureRecognizer view] transform], [pinchGestureRecognizer scale], [pinchGestureRecognizer scale]);
[pinchGestureRecognizer setScale:1];
}
}
// Menu - Delete
else if([gestureRecognizer isMemberOfClass:[UILongPressGestureRecognizer class]])
{
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan)
{
UIMenuController *menuController = [UIMenuController sharedMenuController];
UIMenuItem *resetMenuItem = [[UIMenuItem alloc] initWithTitle:@"Reset" action:@selector(deleteMenu:)];
CGPoint location = [gestureRecognizer locationInView:[gestureRecognizer view]];


[self becomeFirstResponder];
[menuController setMenuItems:[NSArray arrayWithObject:resetMenuItem]];
[menuController setTargetRect:CGRectMake(location.x, location.y, 0, 0) inView:[gestureRecognizer view]];
[menuController setMenuVisible:YES animated:YES];


[resetMenuItem release];
}
}
// Move
else if([gestureRecognizer isMemberOfClass:[UIPanGestureRecognizer class]])
{
UIView* piece = [gestureRecognizer view];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged)
{
UIPanGestureRecognizer* panGestureRecognizer = (UIPanGestureRecognizer*)gestureRecognizer;
CGPoint translation = [panGestureRecognizer translationInView:[piece superview]];


[piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y + translation.y)];
[panGestureRecognizer setTranslation:CGPointZero inView:[piece superview]];
}
}
}

2011年8月15日 星期一

UIImagePickerController Camera獲取的圖片在CGContext下的正確繪製方法

UIImagePickerController Camera獲取的圖片,
本身是由它的Orientation決定它的方法,
在UIImage draw會無問題,
不過在CGImageRef下就要做一些手腳了

#define CC_DEGREES_TO_RADIANS(__ANGLE__) ((__ANGLE__) / 180.0f * (float)M_PI)
CGContextRotateCTM(context, CC_DEGREES_TO_RADIANS(-90));
CGContextTranslateCTM(context, -size.height, 0);
CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, size.height, size.width), image.CGImage);

由原圖獲取iPhone Photo Library Thumbnail Image的方法

這兩天要給公司完成一個有關相片的話Demo,
所以把以前研究圖片的代碼發上來,
可能有用,加上可以做個筆記,一舉兩得

+ (UIImage*)resizeImage:(UIImage*)image width:(int)width height:(int)height
{
// Create a thumbnail version of the image for the event object.
CGSize size = image.size;
CGSize croppedSize;
CGFloat offsetX = 0.0;
CGFloat offsetY = 0.0;

// check the size of the image, we want to make it
// a square with sides the size of the smallest dimension
if (size.width > size.height)
{
offsetX = (size.height - size.width) / 2;
croppedSize = CGSizeMake(size.height, size.height);
}
else
{
offsetY = (size.width - size.height) / 2;
croppedSize = CGSizeMake(size.width, size.width);
}

// Crop the image before resize
CGRect clippedRect = CGRectMake(offsetX * -1, offsetY * -1, croppedSize.width, croppedSize.height);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], clippedRect);
// Done cropping

// Resize the image
UIGraphicsBeginImageContext(CGSizeMake(width, height));
CGContextRef bitmap = UIGraphicsGetCurrentContext();
// 以下代碼很特別,
// 如果圖片是由於Photo library/UIImagePickerController獲得來的,
// 圖片本身的方向會90度倒轉了,
// 只要設定一下CGContextSetTextMatrix為1.0, 0.0, 0.0, -1.0, 0.0, 0.0就會回復正常
CGAffineTransform transform = CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, 0.0);
CGContextSetTextMatrix(bitmap, transform);
[[UIImage imageWithCGImage:imageRef] drawInRect:CGRectMake(0, 0, width, height)];
UIImage* thumbnail = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Done Resizing

return thumbnail;
}

2011年7月5日 星期二

當IOS App發生Exception時生成自定義Log Report

昨天看到有人POST一遍程序異常退出時發送郵件報告的代碼的文章,
今天試了一次,發現很實用,
可以幫助我們Developer在沒有Debug Console的情況下建立Log Report,
可惜是BAD_EXC_ACCESS的Exception就不能捕捉到Log Report了,
不過還是有用的,所以發上來。

// Define exception handler callback function
void UncaughtExceptionHandler(NSException* exception)
{
    // Get the exception detail infomation
    NSArray* arr = [exception callStackSymbols];
    NSString* reason = [exception reason];
    NSString* name = [exception name];
    NSString* exceptionStr = [NSString stringWithFormat:@"Exception Name : %@\nReason : %@\n Call Stack Symbols :\n%@", name,reason,[arr componentsJoinedByString:@"\n"]];

    // Save to log file and file encoding was UTF-8
    [exceptionStr writeToFile:[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, TRUE) objectAtIndex:0] stringByAppendingPathComponent:@"exception.log"] atomically:TRUE encoding:NSUTF8StringEncoding error:nil];

    // Print the exception message in debug console
    NSLog(exceptionStr);
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Setup the exception callback handler
    NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);

    return TRUE;
}

2011年6月27日 星期一

利用AirPrint列印UIWebView當前顯示的範圍

// Get the UIWebView content and convert to UIImage
UIViewPrintFormatter* printFormatter = [_webView viewPrintFormatter];
UIGraphicsBeginImageContext(_webView.frame.size);
[printFormatter.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// For Testing check the view layer size
//[UIImagePNGRepresentation(newImage) writeToFile:@"/Users/kito/Desktop/testfile.png" atomically:TRUE];

// Assign the AirPrint Job
UIPrintInteractionController* printController = [UIPrintInteractionController sharedPrintController];
printController.printingItem = newImage;

// Present the AirPrint Dialog from Bar Button
[printController presentFromBarButtonItem:barBtn animated:TRUE completionHandler:nil];

2011年6月26日 星期日

AirPrint Sample

AirPrint一開始還以為會很煩同複雜,
看過Document才發現,不是哪回事,
簡單幾行就要以了,IOS真係很為開發者著想。

UIPrintInteractionController* printController = [UIPrintInteractionController sharedPrintController];

UIPrintInfo *printInfo = [UIPrintInfo printInfo];
printInfo.outputType = UIPrintInfoOutputGeneral;
printInfo.jobName = @"Print Job - 1";
printController.printInfo = printInfo;
printController.printFormatter = [_webView viewPrintFormatter];

[printController presentAnimated:TRUE completionHandler:cUIPrintInteractionCompletionHandler];

2011年6月21日 星期二

iOS App進入Background Process也可不中斷(二)

之前一次已經說過iOS App進入Background Process也可不中斷連線下載的方法,
不過上次的方法有一個壞處,
就是用戶再開啟App,App是不會更新畫面,
只會出Default Image要直到Callback完成,
所以今天看了一下Help Document,
終於找到最好的方法了

UIBackgroundTaskIdentifier bgTask;
if(bgTask == UIBackgroundTaskInvalid)
{
    UIApplication* app = [UIApplication sharedApplication];

    // 開啟了BackgroundTask就要以令以下的queue在Background/Foreground Task都可以運行
    bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
        NSLog(@"System Expiration End Background Task");
        [app endBackgroundTask:bgTask];
        bgTask = UIBackgroundTaskInvalid;
    }];

    // Start the long-running task and return immediately.
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    // Do the work associated with the task.
    for (int i = 0; i < 1000; i++)
    {
        NSLog(@"Sleep %d", i);
        // 返回Main Process更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            UIButton* btn = (UIButton*)[viewController.view viewWithTag:100];
            [btn setTitle:[NSString stringWithFormat:@"Sleep %d", i] forState:UIControlStateNormal];
        }); 
        [NSThread sleepForTimeInterval:5];
    }

    NSLog(@"Completed State End Background Task");
    [app endBackgroundTask:bgTask];
    bgTask = UIBackgroundTaskInvalid;
    });
}

UISearchBar小技巧(二)-修改UISearchBar內其他完件的大小

今天有項目要把UISearchBar放大,
不過只係設定UISearchBar的Frame Size就只會把UISearchBar背景放大,
而中間的Text Field沒有放大,
於是又用到找Subview的方法,
找出Text Field來放大,
不過要放大UISearchBar內其他完件的大小,
不可以在平時的Method Call完成,
要用到Override UIView下layoutSubviews method才可以完成

@implementation UISearchBar (Override)

- (void)layoutSubviews
{
   UITextField *searchField;
   for(int i = 0; i < [self.subviews count]; i++)
   {
      if([[self.subviews objectAtIndex:i] isKindOfClass:[UITextField class]])
      {
         searchField = [self.subviews objectAtIndex:i];
      }
   }
}

@end

2011年6月12日 星期日

UISearchBar小技巧

今早有朋友問我如何令UISearchBar的Keyboard彈出,
不過早上太多工作沒時間回他們,
所以現在Lunch時間寫出來,
給他們看看,方便我不用在MSN上說多一次

UISearchBar* searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];

// 獲取所有UISearchBar Subviews的內容
//NSLog(@"%@", [searchBar.subviews description]);

// 令UISearchBar的Keyboard彈出
[((UIControl*)[searchBar.subviews objectAtIndex:1]) becomeFirstResponder];

// 令UISearchBar沒有了背景
[((UIView*)[searchBar.subviews objectAtIndex:0]) removeFromSuperview];

[self.view addSubview:searchBar];
[searchBar release];

iOS劃分Debug/Release代碼的方法

有時寫代碼的時候有一些Log信息只想在Debug Mode下才顯示,
哪麼以下代碼就很合適了

#ifdef __OPTIMIZE__
// Release Mode
#else
// Debug Mode
#endif

2011年6月10日 星期五

讓MKMapView懂得自動移去兩個地圖子針之間

今天Project要加入一個新功能,
就是令MKMapView懂得自動移去兩個地圖子針之間的位置,
令用戶可以知道目的地同自己差多遠,
最後看了一下MKMapView的Document就找到了,
倒不算太難,
留下來作筆記XD

// 獲取用戶的位置範圍
MKMapPoint userMapPoint = MKMapPointForCoordinate(userLocation.location.coordinate);
MKMapRect userRect = MKMapRectMake(userMapPoint.x, userMapPoint.y, 50, 50);

// 獲取目的地的位置範圍
MKMapPoint targetMapPoint = MKMapPointForCoordinate(_coordinate);
MKMapRect targetRect = MKMapRectMake(targetMapPoint.x, targetMapPoint.y, 50, 50);

// 把範圍Union
MKMapRect unionRect = MKMapRectUnion(userRect, targetRect);
// 叫MKMapView移去指定範圍
[_mapView setVisibleMapRect:unionRect animated:TRUE];

2011年6月8日 星期三

令iOS App進入Background Process也可以進行連線下載

其實要令到iOS App進入Background Process也可以進行連線下載不中斷,
其實有一個很簡單的方法,
就是用NSTimer啦!!
利用NSTimer,在NSTimer的Callback Function,
即是App進入Background Process,
iOS還是會讓NSTimer的Callback Function完成,
才會在Background Process停下來。

PS:不過這方法有一個問題,就是NSTimer的Callback沒完成,用戶再開啟App,App是不會更新畫面,只會出Default Image要直到Callback完成,才會更新

[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(timerLoadUpdate) userInfo:nil repeats:FALSE];

2011年5月26日 星期四

Objective C 小技巧 ﹣ 由Object獲取Class的所有Method名

今天做實驗,
想試出有什麼方法可以更改iOS SDK中一些私有API,
而又可以理論上通過App Store的審核。
不過作為私有API,我們根本就無法看得到Class的Header File,
哪麼最簡單更改方法就只好由Method入手,
因為Objective C有Categories的Override功能,
十分好用,可作某Method下的覆寫。

代碼如下
// 必須的Header file
#import <objc/runtime.h>

// 找Method名的代碼
unsigned int methodCnt = 0;
Method* methodList = class_copyMethodList([obj class], &methodCnt);
for (int i = 0; i < methodCnt; i++)
{
   Method method = methodList[i];
   // Print Method Name
   NSLog(@"%@", NSStringFromSelector(method_getName(method)));
}

2011年5月24日 星期二

iOS UIWindow 小技巧﹣令UIViewController收不到Interface Orientation Callback的原因

今天開始接手同事的iPhone項目,
我主要的工作是把項目轉為同時支援iPad的版本

剛開始就發現項目不論在iPhone或是iPad都不支持Interface Orientation,
經過同事和我的努力,
即使在plist和UIViewController的Interface Orientation Callback,
設定了都不可以收到Interface Orientation

最後同事發現到原來是Interface Builder,
把viewController的view加到UIWindow中就會引發這個問題,
他之後改為programme code去把viewController的view加到UIWindow中就沒事了
// 把viewController的view加到UIWindow中
[self.window addSubview:viewController.view];

2011年5月6日 星期五

iOS UITableView 小投巧(二)﹣修改Section Header方便方法

一般大家修改Section Header都會用
// 修改Header高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
// 修改Header介面的View
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

但是今天我發現可以利用UITableView本身的property tableHeaderView
不過它只會出現在UITableView的最上方,
因為它根不就不是一個Section Header,
只是在UITableView用到一Section Header,
就可以用它來簡化代碼
UILabel* titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 245, 50)];
[titleLabel setText:@"Title"];
[titleLabel setFont:[UIFont boldSystemFontOfSize:22]];
//[titleLabel setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
[titleLabel setTextAlignment:UITextAlignmentCenter];
[titleLabel setBackgroundColor:[UIColor clearColor]];
[titleLabel setTextColor:[UIColor whiteColor]];
self.tableView.tableHeaderView = titleLabel;
[titleLabel release];

iOS UITableView 小投巧(一)﹣優化效能

最近一位同事要利用UITableView載入二萬個Record
不過UITableView用了約兩分鐘才完成初始化
真是慢得無朋友

最後我們發現他原來用了UITableView Delegate Callback的heightForHeaderInSection Function
去修改Record的高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 100;
}

我們都覺得是Function Call太多的關係
是令UITableView慢原因
最好的方法都是用UITableView的property rowHeight去修改
_tableView.rowHeight = 100;