這次iPad版本延期官辽,主要是因?yàn)榻?jīng)驗(yàn)不足酬荞,對(duì)項(xiàng)目過(guò)于樂(lè)觀搓劫,導(dǎo)致項(xiàng)目時(shí)間估算不夠準(zhǔn)確,再加上混巧,前期一直在做3.7版本枪向,最初接近兩周時(shí)間都是小虎一個(gè)人做iPad版。
在上家公司做過(guò)iPad版本咧党,但因?yàn)槭怯螒駻PP秘蛔,除了少數(shù)特殊界面,基本上iPad版本就是放大版的iPhone版凿傅。
1.app視圖結(jié)構(gòu)簡(jiǎn)單缠犀,也沒(méi)有導(dǎo)航欄、tab頁(yè)聪舒,頁(yè)面跳轉(zhuǎn)都是采用present方式辨液,且iPad版不改變?cè)械囊晥D結(jié)構(gòu)
2.不用實(shí)現(xiàn)分屏功能
3.app只支持橫屏模式,不用考慮橫豎屏切換問(wèn)題
4.對(duì)app熟悉箱残,90%的界面都是自己完成的
5.大部分頁(yè)面采用xib+autoSize布局滔迈,只有少數(shù)頁(yè)面采用代碼布局
6.設(shè)計(jì)師預(yù)先將大部分界面重新設(shè)計(jì)排版止吁,需求明確
我們遇到以下幾個(gè)問(wèn)題:
1.改變iPhone版原有的視圖結(jié)構(gòu)
iPhone原有的視圖結(jié)構(gòu)主要基于UITabBarController 和UINavigationController,視圖層級(jí)結(jié)構(gòu)復(fù)雜燎悍。由于側(cè)邊欄敬惦、分屏等需求,iPad改變?cè)械囊晥D結(jié)構(gòu)谈山,導(dǎo)致無(wú)法接收到推送消息俄删;不彈出actionSheet;點(diǎn)擊按鈕后某些視圖不跳轉(zhuǎn)……等問(wèn)題奏路。
2.分屏
在橫屏模式下畴椰,界面需要分屏顯示,左邊視圖為主視圖鸽粉,右邊視圖為詳細(xì)視圖斜脂,此時(shí)兩者為并列關(guān)系。而在豎屏模式下触机,兩者都在導(dǎo)航上為上下關(guān)系帚戳,通過(guò)pop或present方式彈出下一個(gè)視圖。
3.橫豎屏適配
最初采用- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:方法來(lái)實(shí)現(xiàn)屏幕適配功能儡首,但視圖經(jīng)過(guò)多個(gè)層級(jí)結(jié)構(gòu)后片任,在某些情況下不再調(diào)用該方法。以編輯個(gè)人信息中的姓名為例椒舵,視圖從XRMeTabSplitVC->DoctorInfo2ViewController->ItemInputViewController蚂踊,到DoctorInfo2ViewController層級(jí)中還會(huì)調(diào)用該方法,而到ItemInputViewController層級(jí)中笔宿,并不是每次屏幕旋轉(zhuǎn)都會(huì)調(diào)用該方法犁钟,具體原因不明。只能在每個(gè)界面監(jiān)聽(tīng)設(shè)備旋轉(zhuǎn)事件泼橘,手動(dòng)調(diào)整界面的frame涝动,來(lái)達(dá)到適配需求。
4.布局方式不統(tǒng)一
在項(xiàng)目中炬灭,有通過(guò)代碼布局醋粟、autoSize布局、autoLayout布局三種方式布局重归。因?yàn)椴季址绞讲唤y(tǒng)一米愿,不能批量對(duì)頁(yè)面進(jìn)行調(diào)整,只能人工對(duì)各個(gè)界面逐一調(diào)整鼻吮,比較費(fèi)時(shí)育苟。而代碼布局方式更增加了界面適配的難度,通常需要先讀懂代碼椎木,經(jīng)過(guò)多次調(diào)整測(cè)試违柏,才能達(dá)到最好的效果博烂。
5.對(duì)項(xiàng)目不夠熟悉
雖然熟悉項(xiàng)目的框架結(jié)構(gòu),但項(xiàng)目龐大漱竖,只有少數(shù)的幾個(gè)界面是自己完成的禽篱,大部分界面接觸的較少。
6.需求不夠明確
sketch中只有幾個(gè)界面的布局馍惹,大部分界面并沒(méi)有設(shè)計(jì)稿躺率。為了達(dá)到更好的效果,通常要通過(guò)多次更改代碼万矾,在模擬器對(duì)比之后肥照,才能確定采用哪種方式。
怎樣才能估算項(xiàng)目時(shí)間更準(zhǔn)確呢勤众?
最根本的還是需要對(duì)項(xiàng)目非常熟悉,有足夠的經(jīng)驗(yàn)鲤脏,但這個(gè)短期內(nèi)達(dá)不到要求们颜。所以,一方面要盡快熟悉整個(gè)項(xiàng)目猎醇,另一方面通過(guò)以下三種方式窥突,盡量估算準(zhǔn)確。
1.每天工作時(shí)間按6小時(shí)計(jì)算硫嘶,而不是8小時(shí)
過(guò)于理想化阻问,其實(shí)每天的真正有效的編碼時(shí)間小于8小時(shí)。編碼時(shí)間經(jīng)常被各種會(huì)議沦疾、活動(dòng)打斷称近。
2.盡量將任務(wù)拆解到1天以內(nèi),即6小時(shí)以內(nèi)
最初做計(jì)劃時(shí)哮塞,只是按照模塊和功能刨秆,粗略的規(guī)劃大概時(shí)間,并未細(xì)化到每天忆畅,導(dǎo)致計(jì)劃時(shí)間不夠準(zhǔn)確衡未。
3.時(shí)間按樂(lè)觀來(lái)估算,最終計(jì)劃時(shí)間按樂(lè)觀時(shí)間*1.5或*2來(lái)估算
在iOS開(kāi)發(fā)中家凯,經(jīng)常因?yàn)槟承澳钡膯?wèn)題(可能是iOS本身的問(wèn)題缓醋,或者對(duì)某些API不夠熟悉導(dǎo)致的問(wèn)題),導(dǎo)致延期绊诲,通常需要預(yù)留部分時(shí)間來(lái)處理這些預(yù)料之外的問(wèn)題送粱。
如果開(kāi)發(fā)過(guò)程中發(fā)現(xiàn)估算項(xiàng)目時(shí)間存在嚴(yán)重的誤差,應(yīng)該怎么辦呢驯镊?
盡早溝通葫督,為了保證產(chǎn)品質(zhì)量竭鞍,要么砍需求,要么延長(zhǎng)時(shí)間橄镜。
方法簡(jiǎn)單偎快,但實(shí)際操作卻有問(wèn)題:時(shí)間、產(chǎn)品洽胶、老板不肯妥協(xié)晒夹,而程序員通常氣場(chǎng)不夠強(qiáng)大,只能妥協(xié)姊氓,只能靠苦逼的加班來(lái)解決丐怯。但加班的方式太被動(dòng)了,還是應(yīng)該嘗試主動(dòng)說(shuō)明項(xiàng)目延期的原因翔横,也許效果不是很好读跷,但是至少爭(zhēng)取過(guò)。
iPad開(kāi)發(fā)中遇到的問(wèn)題
問(wèn)題1:點(diǎn)擊某個(gè)按鈕禾唁,以present方式彈出界面后效览,再關(guān)閉該界面,再點(diǎn)擊該按鈕荡短,無(wú)響應(yīng)丐枉,不彈出界面,提示:Presenting view controllers on detached view controllers
在iOS開(kāi)發(fā)中掘托,界面跳轉(zhuǎn)通常有三種方式:
1.pushViewController和popViewController瘦锹,但這種方式基于UINavigationController
2.presentViewController和dismissViewController
3.addSubview和removeFromSuperview
解決方案:設(shè)置self.modalPresentationStyle = UIModalPresentationOverFullScreen;
modalPresentationStyle的默認(rèn)值是UIModalPresentationFullScreen,兩者的區(qū)別是:使用UIModalPresentationFullScreen方式闪盔,present視圖完成后弯院,會(huì)刪除該視圖;而UIModalPresentationOverFullScreen并不會(huì)刪除該視圖
UIModalPresentationFullScreen泪掀,全屏模式
UIModalPresentationPageSheet 抽兆,在豎屏中,全屏模式族淮,在橫屏中辫红,窗口
UIModalPresentationFormSheet ,窗口模式祝辣,處于屏幕中間
UIModalPresentationCurrentContext 贴妻,當(dāng)definesPresentationContext屬性為YES時(shí),不常用
UIModalPresentationCustom 蝙斜,自定義模式
UIModalPresentationOverFullScreen 名惩,iOS8新出現(xiàn)
UIModalPresentationOverCurrentContext ,iOS8新出現(xiàn)
UIModalPresentationPopover 孕荠,iOS8新出現(xiàn)娩鹉,在豎屏中攻谁,全屏模式,在橫屏中弯予,pop視圖戚宦,點(diǎn)擊視圖之外的位置,會(huì)關(guān)閉該視圖
UIModalPresentationNone锈嫩,一般不用
問(wèn)題2:iPad中點(diǎn)擊按鈕受楼,不彈出actionSheet
解決方案:將[sheet showFromTabBar:[[self tabBarController] tabBar]];換成[sheet showInView:self.view];
將origin:[[self tabBarController] 換成origin:cell
UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:self
cancelButtonTitle:@"取消"
destructiveButtonTitle:nil
otherButtonTitles:@"拍照",@"從手機(jī)相冊(cè)選擇", nil];
[sheet showInView:self.view];
[ActionSheetDatePicker showPickerWithTitle:@"請(qǐng)選擇出生年月"
datePickerMode:UIDatePickerModeDate
selectedDate:self.birthDate
target:self
action:@selector(dateWasSelected:element:)
origin:cell];//origin:[[self tabBarController] tabBar]];
因?yàn)樵趇Phone中通過(guò)tabBar來(lái)切換tab頁(yè),而在iPad中改變了原有結(jié)構(gòu)呼寸,以側(cè)邊欄的方式實(shí)現(xiàn)tab頁(yè)切換艳汽,導(dǎo)致[self tabBarController]為nil,所以不彈出actionSheet
問(wèn)題3:iPad中點(diǎn)擊actionSheet無(wú)響應(yīng)
查看錯(cuò)誤日志对雪,提示W(wǎng)arning: Attempt to present on which is already presenting
解決方案:將– (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex替換為– (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex
正常情況下河狐,調(diào)用后clickedButtonAtIndex
會(huì)調(diào)用方法didDismissWithButtonIndex,但是不知道為什么在iPad下并沒(méi)有dismiss掉actionSheet瑟捣,所以出現(xiàn)警告:Attempt to present on which is already presenting
// Called when a button is clicked. The view will be automatically dismissed after this call returns
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex NS_DEPRECATED_IOS(2_0, 8_3) __TVOS_PROHIBITED;
查看蘋(píng)果官方文檔甚牲,發(fā)現(xiàn)在iOS8以后,UIActionSheet被棄用蝶柿,而使用UIAlertController:Important: UIActionSheetDelegate is deprecated in iOS 8. (Note that UIActionSheet is also deprecated.) To create and manage action sheets in iOS 8 and later, use UIAlertController.
問(wèn)題4:iPad中旋轉(zhuǎn)設(shè)備時(shí),第三方類庫(kù)SVProgressHUD旋轉(zhuǎn)角度不對(duì)
解決方案:刪除旋轉(zhuǎn)相關(guān)代碼非驮。self.hudView.transform = CGAffineTransformMakeRotation(angle);
switch (orientation) {
case UIInterfaceOrientationPortraitUpsideDown:
//rotateAngle = M_PI;
newCenter = CGPointMake(posX, orientationFrame.size.height-posY);
break;
case UIInterfaceOrientationLandscapeLeft:
//rotateAngle = -M_PI/2.0f;
newCenter = CGPointMake(posY, posX);
break;
case UIInterfaceOrientationLandscapeRight:
//rotateAngle = M_PI/2.0f;
newCenter = CGPointMake(orientationFrame.size.height-posY, posX);
break;
default: // as UIInterfaceOrientationPortrait
rotateAngle = 0.0;
newCenter = CGPointMake(posX, posY);
break;
}
因?yàn)镾VProgressHUD本身就跟隨設(shè)備旋轉(zhuǎn)交汤,在加上旋轉(zhuǎn)代碼,導(dǎo)致SVProgressHUD旋轉(zhuǎn)角度不對(duì)
問(wèn)題5:在隱藏導(dǎo)航欄時(shí)劫笙,有個(gè)消失過(guò)程的動(dòng)畫(huà)
背景:在iPad中職業(yè)站沒(méi)有導(dǎo)航欄芙扎,但職業(yè)站中所有界面的跳轉(zhuǎn)都是基于導(dǎo)航欄,沒(méi)有導(dǎo)航欄填大,點(diǎn)擊職業(yè)站任何按鈕均不跳轉(zhuǎn)界面戒洼。為了實(shí)現(xiàn)該需求,在職業(yè)站界面將消失時(shí)顯示導(dǎo)航欄允华,在職業(yè)站界面將出現(xiàn)時(shí)隱藏導(dǎo)航欄圈浇。
解決方案:將self.navigationController.navigationBarHidden = true;改為self.navigationController.navigationBar.hidden = YES;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBarHidden = true;
[self queryWorkSiteData];
[self resetSubViewFrame];
}
這個(gè)問(wèn)題查了許久,一直沒(méi)找到解決方案靴寂。因?yàn)樵谖铱磥?lái)self.navigationController.navigationBarHidden = true;和self.navigationController.navigationBar.hidden = YES;是等效的磷蜀,但事實(shí)上,navigationBarHidden比hidden多了動(dòng)畫(huà)功能百炬。
@property(nonatomic,getter=isNavigationBarHidden) BOOL navigationBarHidden;
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated; // Hide or show the navigation bar. If animated, it will transition vertically using UINavigationControllerHideShowBarDuration.