Had a request to make the work flow a little easier heres those icons in descending size order
我å‚åŠ çš„é˜Ÿä¼ç”?å专业äh士组æˆï¼Œå‡ÞZ¹Žæˆ‘æ„æ–™çš„æ˜¯æˆ‘们䏿œ?ä¸ªä¹‹å‰æ ¹æœ¬æ²¡æœ‰ä“Qä½•é‡‡ç”¨æ•æ·å®Œæˆå·¥ä½œçš„¾léªŒã€‚è¿™æ øP¼Œæˆ‘们在其ä¸?åæœ‰¾l验的åŒä¼´æŒ‡å¯ég¸‹å¼€ 始工作了。一开始,让ähé«˜å…´çš„äº‹æƒ…åÆˆä¸å¤šåQŒå¤§éƒ¨åˆ†çš„事情都让äh感到沮ä“á。我们认识到åQŒæ•æ·åƈä¸åªæ˜¯å†²åˆºï¼ˆSprintåQ‰å’Œæ²¡æœ‰æ–‡æ¡£ - 有丞®‘ç»†å¾®ä¹‹å¤„ä½ å¾—èŠ±åŠŸå¤«å¦æ‰è¡Œã€?/p>
˜q™é‡Œè¯´æ˜Žä¸€ä¸‹æ›¾¾lå‘生过的事情:
æ•æ·è®²çš„全是当下的考é‡ã€å½“å‰çš„冲刺ã€å½“å‰çš„用户故事½{‰ã€‚当一路走æ¥çªç„¶æœ‰å…¶ä»–æŸä¸ªæ•…事出现在é¢å‰çš„æ—¶å€™ï¼Œæˆ‘们òq¶ä¸ä¼šäº‹å…ˆè€ƒè™‘事情ž®†å¦‚何å‘展。过åŽ? 在瀑布模型里,我们常常是首先考虑整个¾pÈ»ŸåQŒHLDåQˆé«˜å±‚设计)和LLDåQˆä½Žå±‚设计)是第一æ¥ï¼Œåœ¨ç‘ô¾l剘q›ä¹‹å‰ï¼Œå®ƒä»¬å¿…é¡»å†È»“ã€?/p>
相ååQŒæ•æïLš„内容完全是当å‰çжæ€å’Œä¸æ–改å˜åQŒè¦æ˜¯æˆ‘们的需求未æ¥ä¼šå˜åŒ–åQŒæˆ‘们的设计也将éšä¹‹æ¼”å˜åQ›ä½†æ˜¯æˆ‘们对于超‘Šå½“å‰å†²åˆºçš„事情òq¶ä¸æ˜¯ç‰¹åˆ«å…³æ³¨ã€‚è¦æŽ¥å—˜q™ä¸€äº‹å®žå¾—花ç‚ÒŽ—¶é—´ã€?/p>
æ•æ·æ˜¯ä¸€ä¸ªä¸æ–å˜åŒ–的环境ã€?#8220;å“应å˜åŒ–”是其ä¸Õd¯¼åŽŸåˆ™ä¹‹ä¸€ã€‚äØ“äº†å“应,开å‘者必™å»è·Ÿå¸‚é¢ä¸Šçš„æŠ€æœ¯ä¿æŒåŒæ¥ï¼Œå¦åˆ™å®ƒå°†è®©ä½ 异常痛苦ã€?/p>
我们曑֜¨™å¹ç›®ä¸ä‹É用了一¿Uæœç´¢å¼•擎库åQŒä½†æˆ‘们ä¸åªæœ‰ä¸€äººå¯¹å…¶æœ‰äº†è§£ã€‚ç”±äºŽè¿™ä¸ªç¼ºé™øP¼Œæˆ‘们在估时ã€ç»“å¯V€ç«™ç«‹å¼ä¼šè®®åQŒä»¥åŠå…¶ä»–日常实践里都é‡åˆîCº†é—®é¢˜ã€?/p>
我们é¢å¯¹çš„å¦ä¸€ä¸ªé—®é¢˜æ˜¯æ°å½“地进行TDD。从技术观点看åQŒTDDå·²ç»æœ‰äº†ä¸å°‘é€‰æ‹©ã€‚ä½ å¯ä»¥ä¾èµ–ä¸åŒçš„æ¨¡æ‹Ÿå’Œ‹¹‹è¯•框架åQŒè¿™äº›å¾€å¾€éƒ½æ˜¯ä½ 在瀑布模型里ä¸ä½¿ç”¨çš„东è¥Ñ€‚è¦æ˜¯ä½ 想采用TDDåQŒé‚£ä¹ˆå°±å¿…须具备对它们的丰富知识åQŒå¦åˆ™æ•´ä¸ªè¿‡½E‹å°±ä¸ä¼šå¤ªé¡ºåˆ©ã€?/p>
在瀑布模型里,æ€ÀL˜¯ä»£ç 先完æˆï¼Œç„¶åŽå†è¿›è¡Œäº›‹¹‹è¯•åQˆé€šå¸¸éƒ½æœ‰åQŒä½†òq‰™žæ€ÀL˜¯æœ‰ï¼‰åQ›ä½†TDD则完全是¾U¢ã€ç»¿å’Œé‡æž„。这¿Uæ–¹æ³•è¦æ±‚æˆ‘ä»¬ç”¨æŠ½è±¡æœ¯è¯ ˜q›è¡Œæ€è€ƒï¼Œç„¶åŽç”±å®ƒä»¬æ¼”å˜å‡ºå…·ä½“的事物。由于一开始难以适应åQŒæˆ‘们往往求助于先写出逻辑åQŒç„¶åŽç¡®ä¿å˜åœ¨è¦†ç›–它们的‹¹‹è¯•。我把这¿UîCؓ开å‘驱动测试(DDTåQ? æ–ÒŽ³•ã€?/p>
DDTòq¶æ²¡æœ‰å¢žåŠ å¤ªå¤šçš„æ„义。å‡å¦‚ä½ çŸ¥é“ä½ çš„ä»£ç ž®†ä¼šå¾ˆå¥½åœ°å·¥ä½œï¼Œé‚£äؓ何还è¦å†™ä¸ªæµ‹è¯•å‘¢åQŸåªæ˜¯è¦è¯æ˜Ž˜q™æ®µä»£ç ¼‹®å®žæœ‰æ•ˆåQŸåº”è¯¥ä¸æ¢è¿™ä¸€ç‚V€‚çš„¼‹®æ˜¯˜q™æ ·ã€‚例如,写测试å¯ä»¥è®©ä½ 的代ç åœ¨è€¦åˆæ€§æ–¹é¢è¡¨çŽ°æ›´å¥½ï¼Œè€Œä¸”˜q˜èƒ½å¤ŸäØ“ž®†æ¥çš„代ç å˜æ›´æä¾›ä¸€ä¸ªå·¨å¤§çš„安全¾|‘ã€?/p>
TDDåœ¨è¿™é‡Œå¯¹æˆ‘ä»¬å¤§æœ‰å¸®åŠ©ã€‚å®ƒè¦æ±‚我们按抽象术è¯è¿›è¡Œæ€è€ƒã€‚æ¤å¤–,åªè¦æˆ‘们开始修æ”ÒŽµ‹è¯•,我们ž®±å¿…™å»è€ƒè™‘˜q›è¡Œé‡æž„åQŒè®©æˆ‘们的代ç å˜å¾—æ›´ 好。DDT也能帮助我们立å³è¯†åˆ«ä»ÖM½•è´¨é‡é—®é¢˜åQŒè¿™æ ·å®ƒä»¬å°±èƒ½åŠæ—¶åœ°å¾—到修å¤ã€‚è¿™æ øP¼Œæˆ‘们最¾lˆè®¤è¯†åˆ°å¿…须抛弃“首先¾~–代ç ,然åŽå†™æµ‹è¯?#8221;çš„å¥—è·¯ï¼ŒåŽæ¥æˆ‘们é‡? 新开始采用了TDDæ–ÒŽ³•ã€?/p>
但是åQŒæ•æ·ä¸çš„团队钟情于˜q™ç§åˆä½œåšäº‹çš„æ–¹å¼ï¼Œä¸å–œ‹Æ¢ç›¸åŒé¡¹ç›®ç»„或房间里的äh啿žªåŒšw©¬åœ°å¹²‹z…R€‚当团队一起工作时åQŒä»–们会ç«å°½æ‰€èƒ½åœ°æŠŠäº‹æƒ…å‘剿ލ˜q›ï¼Œ¾l“æžœä½ æœ‰æžå¤§çš„å¯èƒ½æ€§æ˜¯ä»¥ä¸€è‡´çš„æ–¹å¼å®Œæˆäº‹æƒ…åQŒè€Œä¸æ˜¯æœ€¾lˆåœ¨å¤šä¸ªæˆ˜çº¿å¤±åˆ©ã€?/p>
éµåó@TDD是最佳的方å¼åQŒä½†è¦æ˜¯ä½ è¿˜æ²¡æœ‰é€‚åº”å®ƒï¼Œä½ ä»ç„¶å¯ä»¥ç»“对:利用一å—玻璃(白)æ¿ï¼Œé¦–å…ˆç”Õd‡ºæŸä¸ªè®¾è®¡‹¹ç¨‹å›¾ï¼Œç„¶åŽå…¶ä¸ä¸€ä¸ªåˆä½œè€…å¯ä»¥ç¼–写代ç ,而å¦ä¸€ä¸ªåˆ™å¯ä»¥¾~–写‹¹‹è¯•用例ã€?/p>
当个别故事全部完工而æŸäº›æ²¡æœ‰ï¼Œ˜q™æ ·çš„冲刺算ä¸ç®—æˆåŠŸå‘¢ï¼Ÿé‚£å‡å¦‚冲刺的全部故事基本完æˆä½†è¿˜æœ‰äº›çƒ¦ähçš„å°é—®é¢˜å‘¢ï¼Ÿ
团队应该å…Ïx³¨è®©æ•´ä¸ªæ•…事完工,所有问题得到解冻I¼Œòq¶ä¸”满èƒöå®Œå·¥æ ‡å‡†åQ›è€Œä¸æ˜¯æ…Œæ…Œå¼ å¼ åœ°ç›¯ç€Backlog马虎了事。一‹Æ¡å®žæ–½è¹©è„šçš„冲刺没有 ä»ÖM½•æ„义åQŒé™¤äº†åœ¨¾l§ç®‹å‰è¿›ä¹‹å‰å¿…é¡»ž®†å·²å®Œæˆçš„äº‹æƒ…è¿”å·¥ã€‚æ—¶é—´å¸¸å¸¸æ˜¯ä¸€ä¸ªçº¦æŸæ¡ä»Óž¼Œå› æ¤æ ÒŽ®æ•…事的相寚w‡è¦ç¨‹åº¦ï¼Œå›¢é˜Ÿåº”该扑ևºä¸€¿U对他们æè®®çš„è§£å†Ïx–¹æ¡ˆæ›´å…? è´¨é‡æ„义的实施方法。ä½äºŽBacklog™å¶ç«¯çš„æ•…事必™åÕd°½é‡ä»¥æœ€å¥½çš„æ–¹å¼å¼€å‘解冻I¼Œéšç€æˆ‘们逿¸¿UÕdŠ¨åˆ°Backlog的底部,对解å†Ïx–¹æ¡ˆçš„è´¨é‡å¯èƒ½ä¼šæœ‰äº? 妥ååQŒä½†òq¶ä¸æ˜¯å¯¹å®Œå·¥æ ‡å‡†è€Œè¨€ã€‚对于故事的实现åQŒæ›´å¥½çš„æ–¹å¼æ˜¯å®¾~ºå‹¿æ»¥ã€?/p>
庄严的站立å¼ä¼šè®®ä¹Ÿå¿…™åÀLŽ§åˆ¶åœ¨ä¸€å®šæ—¶é—´ä¹‹å†…ã€‚æˆ‘ä»¬å¸¸å¸æ€¼šæŠ‘制ä¸ä½å¼€å§‹å°±æŸä¸ªé—®é¢˜˜q›è¡Œè®¨è®ºçš„诱惑,但那是必™å»è¦é¿å…çš„ã€?/p>
ä½†è¿™äº›æ•°å—æ³¨å®šå°±æ˜¯å¯èƒ½ä¼šå‡ºé”™çš„大致数å—,˜q™åÆˆä¸æ„味ç€ä½ 纯¾_ÒŽ˜¯é è¿æ°”æ¥å¼€å§‹çŽ©è½®ç›˜èµŒã€‚è¿™äº›æ•°å—在未æ¥çš„å‡ ä¸ªå†²åˆÞZ¹‹åŽå°†¾l™äºˆä½ 柿U指½Cºï¼Œå‘Šè¯‰ä½ 能够完æˆçš„工作é‡ã€?/p>
½Ž€å•干脆的故事最å®ÒŽ˜“被估½Ž—,åÏx¸…楚地知é“需求是什么,å†åŠ ä¸Šç‚¹åº”è¯¥å¦‚ä½•å®žçŽ°çš„ç»†èŠ‚ã€‚äñ”å“è´Ÿè´£ähæ— æ³•æä¾›˜q™ä¹ˆòq²è„†çš„æ•…事。它们åªèƒ½é€šè¿‡è·Ÿå›¢é˜Ÿä¸€èµ¯‚®¨è®ºå¾—出æ¥ã€‚å› æ¤ï¼Œå›¢é˜Ÿå¿…é¡»èŠÞq‚¹æ—‰™—´æ¥äØ“ä¸‹ä¸€ä¸ªå†²åˆø™¿›è¡Œè°ƒæ•´ã€?/p>
在扎˜q›æ•æ·ä¹‹å‰ï¼Œä½ å¯ä»¥åšç‚¹å‡†å¤‡å·¥ä½œï¼š
å¦ä¼šè¯´A到ZåQŒç„¶åŽå†è®©ä½ 忘记åQŒé‡æ–°å¦Z到A往往ä¸ä¼šå¤ªå®¹æ˜“。这会带æ¥äº›ç—›è‹¦åQŒä½†å®Œæˆè½¬å˜ä¹‹åŽåQŒä½ ž®×ƒ¼šçŸ¥é“˜q™æ ·åšæ˜¯å€¼å¾—çš„ã€?/p>
è¯å°±è¯´è¿™ä¹ˆå¤šåQŒåŒå¿—们åQŒä¸Šè·¯å§åQï¼:)
查看英文原文åQ?a >Confessions of A New Agile Developer
åŽŸå¸–åœ°å€ http://www.cocoachina.com/bbs/read.php?tid-7923.html
˜q™ä¸¤å¤©å…¬å¸è®©æ•´ç†æ–‡æ¡£åQŒä¸€ç›´æƒ³å†™ä¸€½‹‡æ›´è¯¦ç»†çš„è”æœø™°ƒè¯•的文档åQŒè¿™‹Æ¡åˆšå¥½æ˜¯æœÞZ¼š
æˆ‘å†™çš„åªæ˜¯è”æœø™°ƒè¯•çš„æ¥éª¤åQŒåÆˆæ²¡æœ‰æ·±å…¥çš„åŽ»å†™äØ“ä»€ä¹ˆè¿™ä¹ˆåšåQˆå…³é”®æ˜¯æˆ‘ä¸çŸ¥é“åQ?br />
希望˜q™ç¯‡æ–‡ç« 对èœé¸Ÿä»¬æœ‰å¸®åŠ©ï¼Œ
高手们è§äº†äº†ž®×ƒ¸è¦ç¬‘了~~
æ˜Žå¤©å†æŽ¥ç€å†™å¦‚何å‘布程åº?/p>
å¿…é¡»æ¡äšgåQ?br />
99$
(没这个就ä¸ç”¨å†å¾€ä¸‹çœ‹äº?
我的¾pÈ»Ÿæ˜?0.5.7 ä¸åŒç‰ˆæœ¬çš„ç³»¾lŸï¼Œå¯èƒ½æœ‰ç‚¹ž®å°çš„区åˆ?br />
首先ç™Õd½•åˆ?br />
http://developer.apple.com/iphone/index.action
如果已ç»è´ä¹°äº?9$, ç™Õd½•˜q›åŽ»åŽä¼šçœ‹åˆ°å¦‚下地方(å›?)
点进åŽÖM¼šçœ‹åˆ°å¦‚下界é¢(å›?)
˜q™é‡Œæˆ‘è¦æä¸€ä¸‹ï¼Œå…¬å¸å’Œä¸ªäººçš„$99 Team里é¢çš„内å®ÒŽ˜¯æœ‰æ‰€ä¸åŒçš?臛_°‘我å‘现有ä¸åŒ)
å…¬å¸çš„Team下é¢å¯ä»¥åŠ å…¥Member 而个人的ä¸èƒ½åŠ å…¥Memberåªèƒ½æ˜¯ä¸€ä¸ªähåQŒä¸˜q‡è¿™é‡ŒåÆˆæ— å¤§¼„,ä¸å¦¨¼„我们下é¢çš„内容ã€?br />
点击˜q›å…¥ Certificates 看到如下内容(å›?)
点击图举U¢è‰²éƒ¨åˆ† 会下载下æ¥ä¸€ä¸ªåå—䨓 AppleWWDRCA.cer 的东è¥?br />
˜q™ä¸ªæ˜¯è°ƒè¯•å‘布IPHONE
½E‹åºå¿…须的一个东西,˜q™ä¸ªä¸œè¥¿æˆ‘们åªè¦ä¸‹è²ä¸€‹Æ¡å°±å¤Ÿäº†åQŒå³ä½¿ä½ 有N个IDPåQŒé‚£ä¹ˆæ— 论是调试½E‹åºåQŒè¿˜æ˜¯å‘布程åºï¼Œé‚£ä¹Ÿåªéœ€è¦ä¸€ä¸ªï¼ŒåŽé¢æˆ‘们è¦è¯´åˆ?
Distribution™åµé¢æ—Óž¼Œä¹Ÿæœ‰˜q™ä¸ªé“¾æŽ¥åQŒå¦‚果已¾l下载过åQŒé‚£ä¹ˆå°±ä¸ç”¨å†ä¸‹˜q™ä¸ªäº†ã€?最åˆå› 为对˜q™ä¸ª‹¹ç¨‹ä¸æ‡‚åQŒæˆ‘下è²äº†N‹Æ¡è¿™ä¸?åQ?br />
下完之åŽåQŒç‚¹å‡»è¿™ä¸ªï¼Œ˜q™ä¸ªä¸œè¥¿ž®×ƒ¼šè£…到自己的机器上ã€?br />
æ‰“å¼€ä½ çš„ Keychain Access åQŒå¦‚æžœdocké‡Œé¢æ²¡æœ‰åˆ?Cç›?ApplicationsUtilities下é¢åŽÀL‰¾Keychain AccessåQŒä¸æ–‡åå—好åƒå«” 钥匙ä¸?#8230;” 打开˜q™ä¸ªåQŒä½ 会看到如ä¸?å›?)
˜q™ä»£è¡¨ä½ å·²ç»è£…è¿›åŽÖMº†ã€?/p>
下é¢åQŒæˆ‘们打开Keychain的这个èœå?å›?)
按ç€ä¸‹é¢çš„图æ?br />
(å›?)
(å›?)
(å›?)
ç„¶åŽcontinute
ž®×ƒ¼šç”Ÿæˆä¸€ä¸ªCertificateSigningRequest.certSigningRequest 的文ä»Óž¼Œåœ¨ä½ 的桌é¢ä¸Šå“?/p>
下一æ?å›?)åQ?br />
点击Request Certificate(�0)
点击Choose FileåQˆå›¾11åQ?br />
选ä¸CertificateSigningRequest.certSigningRequest
点击ChooseåQˆå›¾12åQ?br />
点击Submit
(å›?3)
点击Approve
åQˆå›¾14åQ?br />
˜q™æ—¶ä½ çš„è¯ä¹¦æ˜¯ç‰ŒPending的状æ€?br />
åˆäh–°ä¸€ä¸ªç½‘™åµï¼ŒOKž®±å¤„于如下状æ€?br />
åQˆå›¾15åQ?br />
˜q™æ—¶ç‚¹å‡» Download按钮åQŒä¼šå¾—到一个developer_identity.cer åŒå‡»˜q™ä¸ªæ–‡äšg
åQˆå›¾16åQ?br />
点击OK
˜q™æ—¶å†æ‰“å¼€ä½ çš„keychain 看看 会是如下¾l“æžœ
åQˆå›¾17åQ?br />
切记˜q™æ—¶èƒ½çœ‹åˆîC¸‹é¢é‚£ä¸?钥匙 状的东西åQŒå¦‚果没有这个,是ä¸å¯èƒ½è°ƒè¯•çš„ï¼Œä»¥å‰æœ‰æ—¶åQŒæˆ‘¾l常看ä¸åˆ°é‚£ä¸?钥匙 状的东西
OKåQŒæˆ‘们ç‘ô¾l?/p>
打开Devices 那一™å?br />
(å›?8)
点击 Add Devices
åQˆå›¾19åQ?br />
¾l™è‡ªå·Þqš„讑֤‡å–个åå—例如MyIPod
DeviceID ž®±æ˜¯è‡ªå·±IPod或IPHONE的机íw«å·åQ?0ä½ï¼‰åQŒæœºíw«å·çš„èŽ·å–æ–¹æ³•有很多åQŒå¯èƒ½é€šè¿‡ xcode çš„window->Organizer æ¥å–å¾?br />
(å›?0)
ç„¶åŽå¡«å…¥è¡¨ä¸òq¶ç‚¹å‡»summit
å›?1
䏋颿ˆ‘ä»¬æ¥æžAPPID
å›?2
点击NewAppID
å›?3
½W¬ä¸€å¤„,éšä¾¿å†™ä¸ªåå—ž®Þp¡Œäº†ï¼Œ 我以AppIDä¸ÞZ¾‹
½W¬äºŒå¤„ï¼Œä¸€å®šè¦æ³¨æ„äº†ï¼Œä¸€å®šè¦æŒ‰ç€˜q™æ ·çš„æ ¼å¼å†™
我这里写的是
com.abc.*
ä½ ä¹Ÿå¯ä»¥å†™æˆ com.xyz.* com.google.*
ž®½é‡ä¸é—´é‚£ä¸ªå˜é‡æœ‰ç‚¹æ„义åQŒä¸€èˆ¬æƒ…况下是公å¸çš„åå—ã€?br />
切记一定è¦å†™æˆ˜q™ç§æ ¼å¼åQŒä¸ç„¶ä»¥åŽä¼šæœ‰éº»çƒ?br />
ç„¶åŽsubmit
å›?4
¾l§ç®‹Provisioning™åµé¢
å›?5
点击New Profile
æŒ‰ç€æˆ‘的填一ä¸?br />
å›?6
填完å?Submit
å›?7
Download ä½ ä¼šå¾—åˆ°ä¸€ä¸?DevProvision.mobileprovision 的文ä»Óž¼Œ˜q™ä¸ªæ–‡äšg是è¦è£…到手机里é¢çš?/p>
ž®†è¿™ä¸ªæ‹–动到organizer 里é¢
å›?8
拖动åŽå¦‚å›?9
˜q™æ—¶æ‰“å¼€ä½ çš„IPOD ä½ ä¼šå‘现åœ?nbsp; 讄¡½®->通用->æè¿°æ–‡äšg é‡Œé¢æœ‰ä¸ªä¸œè¥¿åQŒé‚£ž®±æ˜¯ä½ çš„è¯ä¹¦äº?br /> ç„¶åŽå†åŒå‡»DevProvision.mobileprovision˜q™ä¸ªæ–‡äšgåQŒç„¶åŽä½ çš„xcode ž®Þp¢«‹È€‹z»ï¼Œ˜q™æ—¶æŸ¥çœ‹Cç›?br /> Users用户åLibraryMobileDeviceProvisioning Profilesä¸‹é¢ æ˜¯ä¸æ˜¯æœ‰äº†ä¸€ä¸?br /> XXXXXX-XXXXXX-XXXXX-XXXXXX.mobileprovision的文ä»Óž¼Ÿ
下é¢éšä¾¿æ‰“开一个IPHONE的工½E?br />
打开那个工程的plistæ–‡äšgåQŒæŠŠBundle identifieræ”ÒŽˆå¦‚下
å›?0
çŽ°åœ¨å¤§æ¦‚ä½ çŸ¥é?abc 是什么东西了å?br />
打开˜q™ä¸ªå·¥ç¨‹çš„info
å›?1
ç„¶åŽæŒ‰ç€ä¸‹å›¾æ”¹ä¸€ä¸‹è®¾¾|®ï¼ˆåˆ‡è®°é€‰ä¸ä½ 该选ä¸çš„那一™å¹ï¼‰
å›?2
å¦‚æžœä½ æƒ³é€‰ä¸çš„那一™åÒŽ˜¯ç°è‰²çš„ï¼Œé‚£ä¹ˆä½ å‰é¢çš„æŸäº›‹¹ç¨‹ä¸€å®šå‡ºäº†é—®é¢?/p>
然厘q˜æœ‰ä¸€å¤„需è¦ç¡®è®¤ï¼Œå¦‚下åQˆå›¾33åQ?br />
è¯ïL¡®è®¤ï¼Œ¾U¢è‰²æ˜¯éƒ¨åˆ†æ˜¯ä½ 应该选ä¸çš?br />
å›?4
OK
现在已ç»å¤§åŠŸå‘Šæˆäº?99.9999%äº?br />
选ä¸Device… Debug ç„¶åŽ Build and Go
å›?5
½E‹åºè‚¯å®šå·²ç»è·‘åœ¨ä½ çš„æœºå™¨ä¸Šäº†åQŒå¦‚æžœä¸è¡Œï¼Œé‚£ä¹ˆé‡è“vä¸€ä¸‹ä½ çš„IPODåQŒå¦‚æžœå†ä¸è¡Œé‚£å°±é‡è“vä¸€ä¸‹ä½ çš„ç”µè„‘ï¼Œå¦‚æžœå†å†ä¸è¡Œé‚£å°±è‡ªå·±æƒ›_Šžæ³•å§
¾l?br />
接ç€å¦‚何å‘布½E‹åº
打开Certificicates çš„Distribution™åµé¢
åQˆå›¾36åQ?br />
点击 Request Certificate按钮(有上é¢è”æœø™°ƒè¯•çš„æ¥éª¤˜q™ä¸ª˜q‡ç¨‹ä¸€å®šå¾ˆç†Ÿäº†å?
åQˆå›¾37åQ?br />
选ä¸CertificateSigningRequest.certSigningRequest æ–‡äšg(˜q™ä¸ªæ–‡äšgåœ¨è”æœø™°ƒè¯•çš„˜q‡ç¨‹ä¸å·²¾l生æˆè¿‡äº?åQ?br />
å›?8
选ä¸ç„¶åŽç‚¹å‡»Submit
å›?9
点击Appove
å›?0
˜q™æ—¶éœ€è¦åˆ·æ–îC¸€ä¸‹é¡µé?br />
å›?1
点击DownLoad
会得åˆîC¸€ä¸ªå为distribution_identity.cer 的文ä»?br />
åŒå‡»˜q™ä¸ªæ–‡äšgåQŒä¼šæœ‰å¦‚下æ½C?br />
å›?2
点击OK
ç„¶åŽæ‰“å¼€keychain ž®×ƒ¼šçœ‹åˆ°å¦‚下的图
å›?3
˜q™æ—¶ä½ 的电脑ž®±å·²¾l具备å‘布程åºçš„è¯ä¹¦äº?/p>
䏋颿‰“å¼€Provisioning çš„Distribution™åµé¢
å›?4
点击New Profile
ç„¶åŽæŒ‰ç€ä¸‹å›¾å¡«ä¸€ä¸‹ï¼ŒProfile Name éšä¾¿å†™ä¸ªåå—ž®Þp¡Œäº?br />
å›?5
ç„¶åŽSubmit
å›?6
åˆäh–°ä¸€ä¸?br />
å›?7
OK现在Download åQŒä½ 会得åˆîC¸€ä¸?Distribution.mobileprovision 的文ä»?br /> åŒå‡»˜q™ä¸ªæ–‡äšgåQŒä½ çš„xcode 会被‹È€‹z?br /> 查看Cç›?br /> Users用户åLibraryMobileDeviceProvisioning Profilesä¸‹é¢ åˆå¤šäº†ä¸€ä¸?br /> XXXXXX-XXXXXX-XXXXX-XXXXXX.mobileprovision的文ä»?/p>
现在我们å†é…¾|®ä¸€ä¸‹xcode
å›?8
打开Configurations ™åµé¢
选ä¸Release ç„¶åŽDuplicate 一ä¸?br />
å›?9
Duplicate之åŽå¦‚下å›?br />
å›?0
我们把åå—æ”¹æˆDistribution
å›?1
æŽ¥ç€æˆ‘们切æ¢åˆ°Build ™åµé¢
å›?2
图ä¸1处选ä¸Distribution 2处选ä¸Distribution
ç„¶åŽ
å›?3
å›?4
¼‹®å®šå›?4ä¸ï¼Œæ˜¯ä½ 应该选的åQŒä¸ç„¶build时会出现½{‘Ö错误
¼‹®å®šä¹‹åŽåQŒç‘ô¾l?br />
å›?5
选ä¸Device å’?Distribution
OK
å›?6
Build OK 大功告æˆäº?9.9%äº?/p>
˜q™æ—¶å¦‚果有ç¾åé”™è¯¯ï¼Œé‚£ä¸€å®šæ˜¯ä½ ä¸Šé¢æŸä¸ªåœ°æ–¹å‡ºäº†é—®é¢?/p>
æˆåŠŸå?br />
å›?7
å…¶ä¸çš„iHide ž®±æ˜¯æˆ‘们è¦ä¸Šä¼ 的东西åQŒåœ¨ä¸Šä¼ 之å‰åQŒæˆ‘ä»¬è¦æŠŠä»–åŽ‹ç¾ƒæˆ?zip åŒ?/p>
现在我们在æµè§ˆå™¨ä¸è¾“å…?br />
itunesconnect.apple.com
ç™Õd½•一ä¸?br />
å›?8
˜q›å…¥Manage Your Applications
å›?9
点击Add New Application
……………
OKäº?br /> åŽé¢çš„ä¸è¯´äº†~~~
工具下è²è§åŽŸå¸–åœ°å€ http://www.cocoachina.com/bbs/read.php?tid-7923.html
Below is the guide on how to add iAd banner into iPhone app. Guide is based on iAd Programming Guide and WWDC 2010 video.
The first thing you need to do is to add iAd.framework in your Xcode project which is contained in iOS 4 SDK. Also do not forget to add #import <iAd/iAD.h>. Developers can choose between two banners: 320×50 px for portrait and 480×32 px for landscape. The base of banner is ADBannerView, which is subclass of UIView. So the only thing you have to do is to add this view in your control elements hierarchy (you can do this programmatically or using Interface Builder).
Apple recommends to put banner at the bottom or at the top of the window and do not place it on any moving elements like ScrollView or TableView as far as that will decrease shows of banner (and your revenue respectively) and will make it more difficult for user to tap on the ads.
Let’s create a new project in Xcode using View-based Application template and add a banner into it. Name of the app – iAdEx. We are going to edit iAdExViewController.h
1
2
3
4
5
6
7
8
9
10
|
#import <UIKit/UIKit.h>; #import <iAd/iAd.h>; @interface iAdExViewController : UIViewController <ADBannerViewDelegate> { ADBannerView *adView; BOOL bannerIsVisible; } @property ( nonatomic ,assign) BOOL bannerIsVisible; @end |
and modify viewDidLoad method in iAdExViewController.m
1
2
3
4
5
6
7
8
9
10
|
- ( void )viewDidLoad { adView = [[ADBannerView alloc] initWithFrame:CGRectZero]; adView.frame = CGRectOffset(adView.frame, 0, -50); adView.requiredContentSizeIdentifiers = [ NSSet setWithObject:ADBannerContentSizeIdentifier320x50]; adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifier320x50; [ self .view addSubview:adView]; adView.delegate= self ; self .bannerIsVisible= NO ; [ super viewDidLoad]; } |
Let’s talk about requiredContentSizeIdentifiers and currentContentSizeIdentifier properties. In the first one you define all types of banners you are going to use. And the second property defines which type of banner you are going to use at the present moment.
Banners are downloaded from the network. What if we have no network connection right now? Or Apple has any issues with ads server? Our ADBannerView will be empty is these cases. It doesn’t look very nice and wastes space on the screen. Apple recommends to do it in this way: when there is no banner for any reason remove it from the screen; when banner is received – show it again.
We have the ADBannerViewDelegate in our class and it can receive messages from banner – bannerViewDidLoadAd (when banner is loaded successfully) and didFailToReceiveAdWithError (when any problems occured). Let’s implement these messages:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
- ( void )bannerViewDidLoadAd:(ADBannerView *)banner { if (! self .bannerIsVisible) { [UIView beginAnimations: @"animateAdBannerOn" context: NULL ]; // banner is invisible now and moved out of the screen on 50 px banner.frame = CGRectOffset(banner.frame, 0, 50); [UIView commitAnimations]; self .bannerIsVisible = YES ; } } - ( void )bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:( NSError *)error { if ( self .bannerIsVisible) { [UIView beginAnimations: @"animateAdBannerOff" context: NULL ]; // banner is visible and we move it out of the screen, due to connection issue banner.frame = CGRectOffset(banner.frame, 0, -50); [UIView commitAnimations]; self .bannerIsVisible = NO ; } } |
It’s time to launch the app and see what we have now:
Tap the banner
And that’s it – we’ve done it in accordance with Apple’s recommendations. When we launch the app we see the banner. If we tap it, then full view of advertisement is shown. But we still have one issue…
In real app we should stop any application’s activity such as video, audio playback or pause game. In order to solve this task we will create two methods bannerViewActionShouldBegin (when full screen ad is shown) and bannerViewActionDidFinish (when we close ads).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
- ( BOOL )bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:( BOOL )willLeave { NSLog ( @"Banner view is beginning an ad action" ); BOOL shouldExecuteAction = YES ; if (!willLeave && shouldExecuteAction) { // stop all interactive processes in the app // [video pause]; // [audio pause]; } return shouldExecuteAction; } - ( void )bannerViewActionDidFinish:(ADBannerView *)banner { // resume everything you've stopped // [video resume]; // [audio resume]; } |
What else should we do? We need to make banner change it’s orientation in accordance with iPhone position. First of all we need to change the line where we define types of banners which we will use:
1
2
3
4
5
|
- ( void )viewDidLoad { ... adView.requiredContentSizeIdentifiers = [ NSSet setWithObjects:ADBannerContentSizeIdentifier320x50,ADBannerContentSizeIdentifier480x32, nil ]; ... } |
and here are methods for changing orientation:
1
2
3
4
5
6
7
8
9
10
11
12
|
- ( BOOL )shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait|UIInterfaceOrientationPortrait); } - ( void )willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:( NSTimeInterval )duration { if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifier480x32; else adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifier320x50; } |
And here is how it look in a landscape
When device changes orientation we change currentContentSizeIdentifier property of ADBannerView. Now we can see banners in landscape mode too. There is still 50 px offset, but it’s easy to fix.
Apple developers also recommend to write object.delegate=nil; line before deallocating ADBannerView object. In our example it will look like:
1
2
3
4
5
|
- ( void )dealloc { adView.delegate= nil ; [adView release]; [ super dealloc]; } |
Now we have finished with programming part. Good luck with Apple iAd!
Preparing to replace language-pack-zh 1:9.10 (using .../language-pack-zh_1%3a9.10+20090805_all.deb) ...
Unpacking replacement language-pack-zh ...
Preparing to replace language-pack-zh-base 1:9.10 (using .../language-pack-zh-base_1%3a9.10+20090805_all.deb) ...
Unpacking replacement language-pack-zh-base ...
dpkg: error processing /var/cache/apt/archives/language-pack-zh-base_1%3a9.10+20090805_all.deb (--unpack):
trying to overwrite `/usr/share/locale-langpack/zh_TW/LC_MESSAGES/sed.mo', which is also in package language-pack-zh-hant-base
dpkg-deb: subprocess paste killed by signal (Broken pipe)
Preparing to replace language-pack-gnome-zh 1:9.10 (using .../language-pack-gnome-zh_1%3a9.10+20090805_all.deb) ...
Unpacking replacement language-pack-gnome-zh ...
Preparing to replace language-pack-gnome-zh-base 1:9.10 (using .../language-pack-gnome-zh-base_1%3a9.10+20090805_all.deb) ...
Unpacking replacement language-pack-gnome-zh-base ...
dpkg: error processing /var/cache/apt/archives/language-pack-gnome-zh-base_1%3a9.10+20090805_all.deb (--unpack):
trying to overwrite `/usr/share/locale-langpack/zh_TW/LC_MESSAGES/gnome-settings-daemon.mo', which is also in package language-pack-gnome-zh-hant-base
䏿–‡çš„æ˜¾½Cºæ˜¯˜q™æ ·åQ?/p>
(æ£åœ¨è¯Õd–æ•°æ®åº?... ¾pÈ»Ÿå½“剿€Õd…±å®‰è£…æœ?123909 个文件和目录ã€?
æ£åœ¨è§£åŽ‹¾~?language-pack-gnome-zh-hans-base (ä»?.../language-pack-gnome-zh-hans-base_1%3a9.10+20090819_all.deb) ...
dpkgåQšå¤„ç?/var/cache/apt/archives/language-pack-gnome-zh-hans-base_1%3a9.10+20090819_all.deb (--unpack)时出错:
æ£è¯•图覆ç›?#8220;/usr/share/locale-langpack/zh_CN/LC_MESSAGES/gnome-settings-daemon.mo”åQŒå®ƒå±žäºŽè½¯äšgåŒ?language-pack-gnome-zh-base
dpkg-deb: åè¿›½E?¾_˜è„“ 被信å?Broken pipe)¾lˆæ¢äº?br />
æ£åœ¨è§£åŽ‹¾~?language-pack-gnome-zh-hant-base (ä»?.../language-pack-gnome-zh-hant-base_1%3a9.10+20090819_all.deb) ...
dpkgåQšå¤„ç?/var/cache/apt/archives/language-pack-gnome-zh-hant-base_1%3a9.10+20090819_all.deb (--unpack)时出错:
æ£è¯•图覆ç›?#8220;/usr/share/locale-langpack/zh_TW/LC_MESSAGES/gnome-settings-daemon.mo”åQŒå®ƒå±žäºŽè½¯äšgåŒ?language-pack-gnome-zh-base
dpkg-deb: åè¿›½E?¾_˜è„“ 被信å?Broken pipe)¾lˆæ¢äº?br />
åœ¨å¤„ç†æ—¶æœ‰é”™è¯¯å‘生:
/var/cache/apt/archives/language-pack-gnome-zh-hans-base_1%3a9.10+20090819_all.deb
/var/cache/apt/archives/language-pack-gnome-zh-hant-base_1%3a9.10+20090819_all.deb
E: Sub-process /usr/bin/dpkg returned an error code (1)
¾cÖM¼¼˜q™æ ·çš„错误:
dpkg: error processing /var/cache/apt/archives/AAA (–unpack):
trying to overwrite `/usr/lib/BBB‘, which is also in package CCC
dpkg-deb: subprocess paste killed by signal (Broken pipe)
Errors were encountered while processing:
AAA
E: Sub-process /usr/bin/dpkg returned an error code (1)
解决æ–ÒŽ³•åQ?/p>
sudo dpkg -i --force-overwrite AAA
(give full path of AAA), and then run
sudo apt-get -f install
again.
Enough said, the solution is simple. You have to subclass your navigationController’s popViewControllerAnimated:(BOOL)animated. So create a custom navigationController:
#import
@interface customNavigationController : UINavigationController {}
@end
And a custom “popViewControllerAnimated:(BOOL)animated”, this popViewControllerAnimated-function uses the “UIViewAnimationTransitionCurlDown” when popping from a SettingsTableView.
#import "customNavigationController.h"
#import "SettingsTableController.h"
@implementation customNavigationController
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
if([[self.viewControllers lastObject] class] == [SettingsTableController class]){
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration: 1.00];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown
forView:self.view cache:NO];
UIViewController *viewController = [super popViewControllerAnimated:NO];
[UIView commitAnimations];
return viewController;
} else {
return [super popViewControllerAnimated:animated];
}
}
@end
Use your custom navigationController in your appDelegate:
customNavigationController *navigationController =
[[customNavigationController alloc]
initWithRootViewController:rootView];
在实际的开å‘ä¸åQŒå¯¼èˆªå™¨æ˜¯æœ€é‡è¦çš„容器之一åQŒæˆ‘们ç»å¸¸è¦åœ¨å¯¼èˆªæ 䏿·»åŠ å„¿Uæ ·å¼çš„æŒ‰é’®åQŒæ·»åŠ ä¸€ä¸ªæŒ‰é’®å¾ˆ½Ž€å•,代ç 如下图:
UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithTitle:@"Setting" style:UITabBarSystemItemContacts
target:self action:@selector(clickSettings:)];
self.navigationItem.rightBarButtonItem = anotherButton;
[anotherButton release];
其䏿Œ‰é’®çš„æ ·å¼å¯ä»¥æœ‰å¤šç§åQŒå…·ä½“çš„å¯ä»¥å‚考:https://developer.apple.com/library/ios/prerelease/#documentation/UIKit/Reference/UIBarButtonItem_Class/
在有些项目ä¸è¦åœ¨å³é¢æ·ÕdŠ ä¸¤ä¸ªæŒ‰é’®åQŒå®žçŽ°çš„æ ·å¼å¦‚下图:
实现的代ç 如下图åQ?/p>
UIToolbar* tools = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 150, 45)];
[tools setTintColor:[self.navigationController.navigationBar tintColor]];
[tools setAlpha:[self.navigationController.navigationBar alpha]];
NSMutableArray* buttons = [[NSMutableArray alloc] initWithCapacity:2];UIBarButtonItem *anotherButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
target:self action:@selector(clickSettings:)];UIBarButtonItem *anotherButton1 = [[UIBarButtonItem alloc] initWithTitle:@"Edit" style:UITabBarSystemItemContacts
target:self action:@selector(clickEdit:)];
[buttons addObject:anotherButton];
[anotherButton release];
[buttons addObject:anotherButton1];
[anotherButton1 release];
[tools setItems:buttons animated:NO];
[buttons release];
UIBarButtonItem *myBtn = [[UIBarButtonItem alloc] initWithCustomView:tools];
self.navigationItem.rightBarButtonItem = myBtn;[myBtn release];
[tools release];
ä¸€ã€æ–°å»ºConstants.hæ–‡äšgåQˆæ–‡ä»¶åæ ÒŽ®éœ€è¦è‡ªå·±å–åQ‰ï¼Œç”¨äºŽå˜æ”¾å…¨å±€å˜é‡åQ?/p>
二ã€åœ¨Constants.hä¸å†™å…¥ä½ 需è¦çš„全局å˜é‡å,例如åQ?/p>
注æ„åQšåœ¨å®šä¹‰å…¨å±€å˜é‡çš„æ—¶å€™ä¸èƒ½åˆå§‹åŒ–åQŒå¦åˆ™ä¼šæŠ¥é”™åQ?/p>
三ã€åœ¨éœ€è¦ç”¨åˆ°å…¨å±€å˜é‡çš„æ–‡ä»¶ä¸å¼•å…¥æ¤æ–‡ä»Óž¼š
å››ã€ç»™å…¨å±€å˜é‡åˆå§‹åŒ–或者赋å€û|¼š
五ã€ä‹É用全局å˜é‡åQšå’Œä½¿ç”¨æ™®é€šå˜é‡ä¸€æ ·ä‹É用ã€?/p>
转自åQšhttp://www.wuleilei.com/Blog/193
å‰é¢å†™äº†Macå¼€å‘基¼‹€åQšæ‰“å¼€ä¸€ä¸ªç›®å½•çš„æ–ÒŽ³•åQŒä¸‹é¢ç‘ô¾l,看看如何通过½E‹åºå¾—到一个目录的内容ã€?/p>
1, 使用NSFileManageræ¥å¾—到这个目录的内容
NSArray *contentArray = [[NSFileManager defaultManager]
contentsOfDirectoryAtURL:[[oPanel URLs] objectAtIndex:0]
//oPanel是上个帖åä¸çš„NSOpenPanel对象
includingPropertiesForKeys:[NSArray array]
options:0
error:nil];
//我们得到一个Array的NSURL
2, ½Ž€å•显½Cø™¿™ä¸ªArrayä¸çš„内容
for(id innerUrl in contentArray)
{
NSLog([innerUrl absoluteString]);
}
3, ¾l“æžœ
run
[Switching to process 3626]
Running…
2010-06-17 23:32:43.409 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0001.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0002.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0003.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0004.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0005.xml
2010-06-17 23:32:43.411 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0006.xml
2010-06-17 23:32:43.412 XML2HTML[3626:a0f] file://localhost/Users/kingmtn/Downloads/T01/T01n0007.xml
awakeFromNib
to every object in the nib. This is where you can access outlets to set up default values or do configuration in code. Example:
- (void)awakeFromNib
{
[buttonOutlet setPeriodicDelay:2 interval:0.5];
[textFieldOutlet setStringValue:@"Hello world!"];
}
注æ„事项åQ?/font>
  1.使用TinyUmbrella之å‰åQŒè¯·å¸è²ç”µè„‘上的Wi-Fi SyncåQˆå¦‚果以å‰å®‰è£…过åQ‰ã€?/p>
  2.使用本地SHSHæ¢å¤˜q‡ç¨‹ä¸å¯èƒ½ä¼šæœ‰é”™è¯¯æ½Cºå¼¹å‡ºï¼Œä½†ä¸ç”¨æ‹…心,å›ÞZšg的确å¯ä»¥å®Œæˆæ¢å¤åQŒå¦‚æžœå¡åœ¨DFUæˆ–é»‘å±æ¨¡å¼æ— 法退出,则å¯ä»¥ä‹É用集æˆçš„Kick Device Out of RecoveryåQˆå¦‚下图åQ?/strong>按钮æ¥é€€å‡ºæ¢å¤æ¨¡å¼ã€?/p>
  3.Mac用户请将App拷备出DMG镜åƒå†è¿è¡Œï¼Œå¦åˆ™ä¼šæ”¶åˆ°æ— ž®½çš„å¯†ç æç¤ºé€‰é¡¹ã€?/p>
  4.必须安装iTunes9.0或以上版本,推èiTunes10ã€?/p>
  软äšgçš„æ“作éžå¸¸ç®€å•:
一ã€å¤‡ä»½shsh
  先将iPhoneè”æœºåQŒç‚¹å‡?strong>Save My SHSH开始备份ã€?br />
SHSHæ–‡äšg的佾|®ä½äºŽï¼š
Windows XP C:"Documents and Settings"用户å?.shsh
Windows   7 C:"Users"用户å?.shsh"
Mac     /Users/用户å?.shsh/
注æ„åQ?br /> 1åQŒåœ¨Mac䏋颿–‡äšgå¤?shsh是éšè—çš„åQŒè¦æƒÏx˜¾½Cºï¼Œéœ€åœ¨ç»ˆç«¯ä¸è¾“å…¥åQšdefaults write com.apple.finder AppleShowAllFiles -bool true命ä×oåŽé‡å¯FinderåQ›æ¢å¤éšè—,则将trueæ”¹äØ“falseåQŒé‡å¯finderã€?/p>
  2åQŒä»ŽAppleå’ŒSaurikå–回的SHSH大å°ä¸åŒåQŒä½†æ˜¯åœ¨æ¢å¤æ—¶æ²¡æœ‰åª„å“ã€?/p>
二ã€è‡ªå»ºæœåС噍æ¢å¤å›ÞZšg
  在第一æ¥å®Œæˆä¹‹åŽï¼Œ
æ— éœ€æ›´æ”¹hostsåQ?br />
点击å³ä¸Šè§?strong>Start TSS ServiceråŽæ¢å¤SHSH对应å›ÞZšgå›_¯ã€?/p>
å‡ ç‚¹è¯´æ˜ŽåQ?/p>
  1.Save My SHSH按钮ž®±æ˜¯å¤‡ä†¾SHSH的开兟뀂连接iDevice到电脑åŽåQŒæ‰“å¼€TinyUmbrellaåŽç›´æŽ¥ç‚¹å‡ÀL¤æŒ‰é’®å›_¯å¤‡ä†¾SHSHã€?/p>
  2.Start TSS Server按钮是在æ¢å¤å›ÞZšgæ—¶ä‹É用的åQŒæŒ‰ä¸‹åŽå›_¯ä½¿ç”¨æœ¬åœ°çš„SHSHæ–‡äšgæ¥æ¢å¤iPhoneå›ÞZšgã€?/p>
  3.Display SHSHsæŒ‰é’®æ˜¯ç”¨æ¥æ˜¾½Cø™®¾å¤‡æ˜¯å¦æœ‰ç›¸åº”çš„SHSHå¯ä»¥ä¸‹è²æˆ–者ä‹É用,效果如下åQ?/p>
  4.Advanced Optionså‹ùN€‰åŽä¼šå‡ºçްECIDã€Device/Versionå’ŒLocation三个选项åQšç¬¬ä¸€ä¸ªECID处å¯ä»¥æ‰‹åŠ¨è¾“å…¥ECIDç ï¼›½W¬äºŒ Device/Version用æ¥å¼ºåˆ¶é€‰æ‹©åž‹å·å’Œç‰ˆæœ¬ï¼›½W¬ä¸‰ä¸ªLocation是选择SHSH的备份地å€åQŒè¿™ä¸ªè¦é‡ç‚¹ä»‹ç»ä¸€ä¸‹ï¼Œé€‰æ‹©Apple是从è‹ÒŽžœå¤? 份ã€é€‰æ‹©Cydia则是在Cydiaä¸å¤‡ä»½ã€é€‰æ‹©Local是备份到本地。没有在本地看到SHSHæ–‡äšg的用户ä¸ç”¨æ‹…心,默认的备份ä‹É用CydiaæœåС噍ã€?/p>
当TinyUmbrellaæ— æ³•è¯†åˆ«å·²ç»˜qžæŽ¥åˆ°ç”µè„‘çš„iPhoneã€iPad或者iPod touchæ—Óž¼Œž®×ƒ¸èƒ½ä¸€é”®å¤‡ä»½SHSH了,首先需è¦?a title="使用USBViewæ¥æŸ¥çœ‹iPhoneçš„ECID" target="_blank">使用USBViewæ¥æŸ¥çœ‹iPhoneçš„ECIDåQˆä¹Ÿå¯ä‹Éç”?a title="ECIDè¯Õd–å™? target="_blank">ECIDè¯Õd–å™?/a>或è€?a title="Auto SHSH Grabber" target="_blank">Auto SHSH GrabberåQŒé€‰æ‹©ä¸€¿Uä¹ æƒ¯çš„æ–ÒŽ³•å›_¯åQŒä¸éœ€è¦æ¯¿U方法都会)。这里我们ä‹É用最å¯é çš„USBViewã€?
首先åQŒæŒ‰ä½HOMEå’ŒPoweré”®å¿U’å·¦å»I¼ŒiPhoneå±å¹•黑掉åQŒä¹‹åŽè¯»äº”ç§’åQŒä¿æŒHome键按ä½åƈæ‘Ö¼€PoweråQŒå½“打开的iTunes出现上图ä¸çš„åæ˜ æ—¶æ¾å¼€æ‰€æœ‰æŒ‰é”®ã€?br />
æ¤æ—¶åQŒåº”该有˜q™æ ·çš„æ½Cºã€?br />
˜q行USBViewåQŒç‚¹å‡ÖM¸Šé¢çš„Optionsã€?br />
按图ä¸é€‰ä¸Config Descriptionã€?br />
ä¹‹åŽæŒ‰F5åˆäh–°åQŒå¯ä»¥çœ‹åˆîC¸€ä¸ªå¤„于DFU模å¼çš„iPhoneã€?br />
˜q™æ—¶åQŒåœ¨å³ä¾§å¯ä»¥æ‰‘Öˆ°å¯¹åº”çš„ECIDå’ŒiBoot版本ã€?br />
打开TinyUmbrellaòq¶å°†ECIDå¡«å…¥Advanced Optionsä¸çš„ECID框内ã€?br />
éšåŽåQŒæ²¡æœ‰è¶Šç‹Þp¿‡çš„iPhoneéœ€è¦æŠŠRequest From一™åšw€‰æ‹©ä¸ºAppleåQŒç„¶åŽç‚¹å‡»Save My SHSH按键开始ä¿å˜ç›¸åº”版本的SHSH。(‘Šç‹±òq¶å¤‡ä»½è¿‡4.1SHSH的用户å¯ä»¥ç›´æŽ¥é€‰æ‹©Cydiaæ¥ä¿å˜SHSHåQ‰ã€?br />
æ¢å¤å›ÞZšg时打开TSS Server选项åQŒè¿™æ—¶ä¼šè‡ªåŠ¨æ€æŽ‰iTunesåQŒé‡æ–°æ‰“å¼€åŽå³å¯æ£å¸¸æ¢å¤ã€?/p>
To reduce the overhead of creating this functionality again and again we may encapsulate the common functions in a class. So did I for my applications.
I have created a class named PaginationAgent. After loading records it calls related methods of its delegate which should implement protocol PaginationAgentDeleagte.
Additionally two php files are needed. One to return the total number of records and another one is to fetch the page data. They must be named respectively as
Lets see an example.
First, I have created a simple view based project “Pagination” and designed the view as u see:
1. Added a button to show previous page data
2. One button to reload the data.
3. Another to show data on next page.
4. Added a UITable which will show the result.
In PaginationViewController.h, imported PaginationAgent.h file and declared:
Now lets see how to initialize it. In the viewDidLoad method I initialized this like this:
remoteApiBasePath must be set to the location where the server side php files are placed.
No trailing slash.
PaginationAgentDelegate protocol declares the following functions to be overridden by the Delegate:
The php files must return data in json format and must use the format as used:
See the attached project for details. The php files are inside the folder “PHPFILES”.
With the iOs SDK 4 now public and the advent of iAds just a few days away, I thought we’d celebrate with a tutorial on how to integrate iAd into your iPhone app!
In this tutorial, not only will we show you how to get started with iAd, but we’ll also show you how to deal with some complex issues you may run into along the way such as:
We’re actually going to start with where we left off in the How To Port an iPhone Application to the iPad and use the universal app we developed in that tutorial in the starting point.
So grab a copy if you haven’t already, and let’s get to adding some iAds!
The first step to use iAd is to make sure our project has the right Base SDK and iPhone OS Deployment Target selected.
For those of you confused about the difference between the Base SDK and Deployment Target (like I was for quite some time!), here’s what they mean:
The tricky bit is what happens when you want to use a class, function, or framework available in one version of the OS if it’s available, but still work on the old version of the OS if it isn’t. We already did some of this in How To Port an iPhone Application to the iPad, and we’ll do even more in this tutorial!
For this tutorial, we want to set things up so that our code can use stuff available in iOS 4.0 (such as iAd), but still run on as many devices as reasonable (3.0+).
So first let’s set iOs 4.0 as the base SDK. To do this, expand the Targets directory, right click on PortMe, and choose “Get Info”. Click the Build tab, make sure “All Configurations” is selected, navigate to Architectures\Base SDK, and change the value to iPhone Device 4.0.
Then, let’s set iPhone OS 3.0 as the iPhone OS Deployment Target. To do this, still in the Target Build tab, navigate to Deployment\iPhone OS Deployment Target, and change the value to iPhone OS 3.0.
You should now be able to compile and run your app (use the iPhone simulator), and try it out on an iPhone 4 simulator. Once you run your code, in the simulator choose Hardware\Device\iPhone OS 4 and re-run your app. The simulator window will look a little different, and say iPhone 4 in the toolbar, so you’ll know it’s working!
The next thing we need to do is add the iAd framework to the project. You can do this by right clicking on Frameworks, choosing “Add\Existing Frameworks…”, and choosing “iAd.framework”.
The problem is, if that is all we do our code will break on older devices that don’t have the iAd framework.
You can verify this by trying to run your code in the iPad Simulator 3.2 – boom! The app will crash on startup and you’ll see the following error log:
dyld: Library not loaded: /System/Library/Frameworks/iAd.framework/iAd Referenced from: /Users/rwenderlich/Library/Application Support/ iPhone Simulator/3.2/Applications/ 3ACB1BDA-26F6-43A6-84EA-9FB637B8CDCD/PortMe.app/PortMe Reason: image not found
To fix this, we need to weak link against the iAd framework. Expand the Targets directory, right click on PortMe, and choose “Get Info”. Click the Build tab, make sure “All Configurations” is selected, and navigate to Linking\Other Linker Flags. Double click on that entry, click the “+” button, and type “-weak_framework iAd”.
Click OK, and then try your app on the iPad simulator again and viola – it should work!
In this tutorial, we’re going to integrate iAd into both the PortMeGameListController and the PortMeGameDetailsController. However, the integration is a bit easier in the PortMeGameDetailsController because it is a subclass of UIViewController, so we’re going to start there first.
Open up PortMeGameDetailsController.xib. You’ll see that all of the controls are children of a single view:
What we’re going to need to do with iAd is scroll an ad view onto the screen when an ad is available, and shrink the rest of the content to fill the remaining space. As currently designed, this isn’t that easy because all of the controls are direct children of the root view. But there’s an easy way to fix it – we’ll simply move the controls into a subview instead!
The easiest way to do this is to drag another view from the library into the XIB, and change its size to be the same as the existing view’s size (320×416). Then drag the existing view as a subview of the new view. When you’re done, it should look like the following:
Then, control-drag from the File’s Owner to the new view (which is now the root view) to connect it to the view outlet. Save your XIB, and run the project and verify that everything still works OK with the details view (in particularly that orientation resizing works correctly). If all works well, we’re one step closer to integrating iAd!
Ok, now let’s get to the fun part – integrating iAd!
First, make the following changes to PortMeGameDetailsController:
// In the import section #import "iAd/ADBannerView.h" // Modify the PortMeGameDetailsController interface @interface PortMeGameDetailsController : UIViewController <GameSelectionDelegate, UISplitViewControllerDelegate, ADBannerViewDelegate> { // Inside the PortMeGameDetailsController interface UIView *_contentView; id _adBannerView; BOOL _adBannerViewIsVisible; // After the interface @property (nonatomic, retain) IBOutlet UIView *contentView; @property (nonatomic, retain) id adBannerView; @property (nonatomic) BOOL adBannerViewIsVisible; |
We first include the iAd headers and mark the view controller as implementing the ADBannerViewDelegate. This way, we can receive events as ads become available or not.
We then declare a property to keep track of the content view that contains all of the controls (basically the inner UIView). We also declare a variable to keep track of our iAd banner view, and whether or not it’s currently visible.
Note that we declare the iAd banner view as an id variable rather than as a ADBannerView. This is because we want to ensure backwards compatibility all the way to OS 3.0, and the ADBannerView class is only available on 4.0+, so we need to weak link against it.
Before we forget, let’s hook up our content view to the new outlet we just made. Make sure you save PortMeGameDetailsController.h, go back to PortMeGameDetailsController.xib, control-drag from the File’s Owner to the inner (second) UIView, and connect it to the contentView outlet.
Then switch over to PortMeGameDetailsController.m and make the following changes:
// In the synthesize section @synthesize contentView = _contentView; @synthesize adBannerView = _adBannerView; @synthesize adBannerViewIsVisible = _adBannerViewIsVisible; // In the dealloc section self.contentView = nil; self.adBannerView = nil; |
Next, we’re going to add the meat of the code. But there’s a lot of it – so let’s break it down into 6 steps.
1) Add helper functions to get height of iAd banner
- (int)getBannerHeight:(UIDeviceOrientation)orientation { if (UIInterfaceOrientationIsLandscape(orientation)) { return 32; } else { return 50; } } - (int)getBannerHeight { return [self getBannerHeight:[UIDevice currentDevice].orientation]; } |
There are several places in the rest of the code where we’re going to want to know how large the banner view should be given a particular orientation. Currently iAds have two possible sizes: 320×50 for landscape, or 480×32 for portrait. So we simply retrieve the proper height based on the passed in orientation.
2) Add helper function to create the iAd view
- (void)createAdBannerView { Class classAdBannerView = NSClassFromString(@"ADBannerView"); if (classAdBannerView != nil) { self.adBannerView = [[[classAdBannerView alloc] initWithFrame:CGRectZero] autorelease]; [_adBannerView setRequiredContentSizeIdentifiers:[NSSet setWithObjects: ADBannerContentSizeIdentifier320x50, ADBannerContentSizeIdentifier480x32, nil]]; if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) { [_adBannerView setCurrentContentSizeIdentifier: ADBannerContentSizeIdentifier480x32]; } else { [_adBannerView setCurrentContentSizeIdentifier: ADBannerContentSizeIdentifier320x50]; } [_adBannerView setFrame:CGRectOffset([_adBannerView frame], 0, -[self getBannerHeight])]; [_adBannerView setDelegate:self]; [self.view addSubview:_adBannerView]; } } |
This helper function creates an ADBannerView in a manner that is safe to use across multiple OS versions. It uses weak linking and NSClassFromString to check if the ADBannerView class is available – if it is not, the method will return nil and the function will bail.
However, if it is available it creates an instance of the class. It then uses the setRequiredContentSizeIdentifiers to specify what kind of ads this app needs. For our case, our app supports both portrait and landscape modes so it needs both ad options.
It then calls setCurrentContentSizeIdentifier to tell iAd which ad it should display. We simply choose the correct one by looking at the current orientation.
Next, we need to set the frame for the iAd. Note there’s some funky business here – we actually set the frame of the view to be offscreen! This is because we don’t know if an ad is available yet, and we don’t want to display the view until we know one is.
We set our view controller as the delegate so that we can receive notice about iAds being available or not. Then finally we ad the new iAd banner view as a subview of our view!
Note something subtle about the above – we always use message passing syntax rather than dot notation (i.e. [_adBannerView setRequiredContentSizeIdentifiers:...] instead of _adBannerView.requiredContentSizeIdentifiers = …). This is again to make sure everything runs fine on OS 3.0+.
3) Add function to size views correctly
- (void)fixupAdView:(UIInterfaceOrientation)toInterfaceOrientation { if (_adBannerView != nil) { if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) { [_adBannerView setCurrentContentSizeIdentifier: ADBannerContentSizeIdentifier480x32]; } else { [_adBannerView setCurrentContentSizeIdentifier: ADBannerContentSizeIdentifier320x50]; } [UIView beginAnimations:@"fixupViews" context:nil]; if (_adBannerViewIsVisible) { CGRect adBannerViewFrame = [_adBannerView frame]; adBannerViewFrame.origin.x = 0; adBannerViewFrame.origin.y = 0; [_adBannerView setFrame:adBannerViewFrame]; CGRect contentViewFrame = _contentView.frame; contentViewFrame.origin.y = [self getBannerHeight:toInterfaceOrientation]; contentViewFrame.size.height = self.view.frame.size.height - [self getBannerHeight:toInterfaceOrientation]; _contentView.frame = contentViewFrame; } else { CGRect adBannerViewFrame = [_adBannerView frame]; adBannerViewFrame.origin.x = 0; adBannerViewFrame.origin.y = -[self getBannerHeight:toInterfaceOrientation]; [_adBannerView setFrame:adBannerViewFrame]; CGRect contentViewFrame = _contentView.frame; contentViewFrame.origin.y = 0; contentViewFrame.size.height = self.view.frame.size.height; _contentView.frame = contentViewFrame; } [UIView commitAnimations]; } } |
This is a helper function we can call to make sure our views are in the right position. If ads are available, we want the ad banner view to be at the top of the screen and the content view shrunk a bit to fill the rest of the area. If ads are not available, we want the ad banner view offscreen and the content view as large as the entire view here.
And that’s exactly what the above function does. It looks long, but is fairly simple and self-explanatory. Note that we wrap the resizing code in an animation block to make things look awesome.
4) Call createAdView in viewDidLoad
- (void)viewDidLoad { [self createAdBannerView]; } |
We want to create our ad view as soon as our view is loaded, even if we aren’t ready to display it quite yet.
5) Call fixupAdView in viewWillAppear and willRotateToInterfaceOrientation
- (void) viewWillAppear:(BOOL)animated { [self refresh]; [self fixupAdView:[UIDevice currentDevice].orientation]; } - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { [self fixupAdView:toInterfaceOrientation]; } |
We need to fix up our ad view in viewWillAppear, because the device may have changed orientations in the time between when our view was visible last and now. And we obviously need to change it upon rotation as well!
6) Implement ADBannerViewDelegate
#pragma mark ADBannerViewDelegate - (void)bannerViewDidLoadAd:(ADBannerView *)banner { if (!_adBannerViewIsVisible) { _adBannerViewIsVisible = YES; [self fixupAdView:[UIDevice currentDevice].orientation]; } } - (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error { if (_adBannerViewIsVisible) { _adBannerViewIsVisible = NO; [self fixupAdView:[UIDevice currentDevice].orientation]; } } |
Now that we have our helper functions, implementing the ADBannerViewDelegate methods are quite simple. We simply toggle whether the ad banner view should be visible or not, and call fixupAdView.
Done!
And that’s it! Compile and run your project, and you should see ads appear correctly in both portrait and landscape mode.
And best yet – if you run the code on an iPad or iPhone 3.0 device it will work just fine as well, but without ads!
Well that worked great for our details controller, but we want it in our list controller too!
The problem is our list controller is a UITableViewController. Unfortunately, it seems like the best way to deal with this situation is to convert your UITableViewController to a normal UIViewController and then proceed similarly to the way we did above. So here are all of the gory steps:
1) Create a XIB for PortMeGameListController
Go to File\New File, choose User Interface and View XIB, make sure Product is iPhone, and click Next. Name the XIB PortMeGameListController.xib and click Finish.
Open up the XIB, click on the File’s Owner, and in the fourth tab of the Attributes Inspector change the class to PortMeGameListController.
Then drag a UIView into the current UIView (so there are 2, just like we did before), and add a UITableView to the inner view. When you’re done it should look like this:
2) Make some changes to PortMeGameListController
Inside PortMeGameListController.h:
// Change the interface declaration @interface PortMeGameListController : UIViewController <UITableViewDelegate, UITableViewDataSource> { // Add inside class UITableView *_tableView; UIView *_contentView; // Add after class @property (nonatomic, retain) IBOutlet UITableView *tableView; @property (nonatomic, retain) IBOutlet UIView *contentView; |
Inside PortMeGameListController.m:
// In synthesize section @synthesize tableView = _tableView; @synthesize contentView = _contentView; // In dealloc section self.tableView = nil; self.contentView = nil; |
Don’t forget to save the files!
3) Hook up outlets
Now go back to PortMeGameListController.xib and connect the first view to the view outlet, the second to the contentView outlet, and the third to the tableView outlet.
Also control-drag from the tableView back to the File’s Owner and set it as the delegate and datasource.
4) Set the NIB name for PortMeGameListController in MainWindow
Open MainWindow.xib and MainWindow-iPad.xib, expand the Navigation Controller, select “Port Me Game List Controller”, and change the nib name to PortMeGameListController.
5) Compile and test to make sure everything works as usual
At this point, compile and run your code and make sure everything works as it usually does – but now you’re using a UIViewController rather than a TableViewController, and you have a XIB laid out in a nice way to use iAds!
6) Follow the steps from the previous section
Now you’re exactly where we were in the previous section with a view controller – so follow the same steps to integrate in this view!
Done!
If all goes well, you should be able to compile and run your project and see advertisements at the top of your table view!
Here is a sample project with all of the code we’ve developed in the above tutorial.
Now you should know how to integrate iAds into your projects – no matter what OSs you wish to support for your app! I’d love to hear your experiences with iAds and how well they are working (or not) for your app!
This video was embedded using the YouTuber plugin by Roy Tanck. Adobe Flash Player is required to view the video.
The finished product is shown below.
Rather than sticking with stock components included in Interface Builder, this demonstration shows a simple technique that gives your app a more professional look. As a developer, it can be difficult to create nice looking graphics for your app. Hiring a designer can quickly exceed the budget. However, creating some nice looking graphics is certainly achievable as a beginning designer. In addition to picking up some new great skills, a polished off UI can lend more creditability to your app and provide an overall better user experience.
In the demo, Photoshop Elements is used, which is 80 bucks at adobe.com with a mail-in rebate. To get started, create a new image with these values:
Select a foreground color from the toolbar, as shown below:
Now select the gradient button, as shown below:
With the gradient selected, swipe from bottom to top on your image. This should produce a gradient color. Now choose File and “Save for Web”. You’ve just created the image we’ll use in our tableview. Let’s switch over to Xcode and implement the image.
In Xcode, we start by creating a navigation based application.
Once the application has been created, add the newly created image by right clicking the Resources folder then “Add” and “Existing Files”, as shown below:
The following code snippet creates two views. Both are then added to the cell’s view. Because the label, which is a view, is added as the last view, we need to ensure its background is transparent (clearColor). Otherwise, it will cover part or all (depending on the size we set) of our background image. Thinking about what this stack of layers will look like, below is a screenshot of the same scenario in Interface Builder:
From the screenshot, you can see the cell’s view owns the imageview and label. Although closer to the bottom, the label is actually top most to the user, hence the reason we need its background to clear.
Let’s take a look at the implementation in Xcode, which is all done in the UITableView delegate method cellForRowAtIndexPath:.
UIImage *image = [UIImage imageNamed:@"gradientcolor.png"]; UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; imageView.contentMode = UIViewContentModeScaleToFill; cell.backgroundView = imageView; [imageView release]; UILabel *label = [[UILabel alloc] init]; label.text = @"testing colors"; label.frame = cell.bounds; label.backgroundColor = [UIColor clearColor]; label.textAlignment = UITextAlignmentCenter; [cell addSubview:label]; return cell;
In the above snippet, we create an image object using the image we added to our app bundle. Next, we create a UIImageView object with the newly created image. The image view will be assigned to our cell’s background view. The backgroundView will retain an instance of the imageView. Therefore, we release our instance, as follows:
[imageView release];
If we had not released our instance, we’d have two instances of the imageView in memory and thus a memory leak. Moving on down in the code, we create an instance of a label. We set the label’s size to cover the entire cell. The label’s background is set to clear or transparent so it doesn’t hide the cell’s background image. Our assigned text is centered and finally, we add the label to the cell’s view, making it the top most view in the cell.
So, in myTableViewController I created custom init function:
- (id) initWithFrame:(CGRect)frm { if ((self = [super initWithStyle: UITableViewStylePlain])){ self.tableView = [[UITableView alloc] initWithFrame:frm style:UITableViewStylePlain]; } return self; }
Obviously, you can use default or your own init function or avoid passing frame argument and hardcode dimensions right here, but the magical line is:
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(x, y, width, height) style:UITableViewStylePlain];
Well, at least it worked for me.
At this point, you need to have a working UITableView-based application.
If you aren’t at this point, you’ll need to take a few steps back and
check with the official Apple iPhone SDK documentation.
The simplest case is adding an accessory icon to the right of all cells in the table. To do this, edit the source file of the table view you want to change and add the following method:
- (UITableViewCellAccessoryType)tableView:(UITableView *)tv accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellAccessoryDetailDisclosureButton;
}
Yeah, that’s a keyboard-full, but it’s not that bad. Because your table view class is the delegate of your actual table view, implementing this method of the UITableViewController class will automatically affect the cells in your table. No other changes are needed to make this work. Very cool.
If you don’t want to show the accessory image for every cell in the table, you’ll need to implement some logic in this method that takes the NSIndexPath parameter (which tells you which row in which section is being asked about) and responds appropriately.
There are three standard accessory images that can be used by this method. Each image is shown below with the matching UITableViewCellAccessoryType (the example uses shows a Disclosure indicator image because it returns UITableViewCellAccessoryDisclosureIndicator.
Disclosure indicator
UITableViewCellAccessoryDisclosureIndicator
Used to indicate that tapping the cell will slide in a new table view one level down in the hierarchy.
Detail disclosure button
UITableViewCellAccessoryDetailDisclosureButton
This button indicates that tapping the cell will reveal a detail view of that item.
Checkmark
UITableViewCellAccessoryCheckmark
Indicates that a row has been selected by the user.
Nothing
UITableViewCellAccessoryNone
This is the default value for the cell accessory indicating that no accessory should be shown.
This isn’t a brain surgery level Tech-Recipe and this information is, of course, covered in the official documentation, but I keep forgetting the method name and knew it would be easier to lookup here than in the docs. I hope someone else finds it helpful, too.
It is really easy to customize your UITableView
s. I'll show you how to completely customize the appearance of UITableView
s without overriding or subclassing and without the need for any tricky hackery.
The core of most iPhone applications is the UITableView
. To make your iPhone application stand out, the simplest way is to make your UITableView
look good.
Customizing your UITableView
can be really easy. You
don't need custom drawing code. You don't need subclasses of anything.
Cocoa Touch provides all the drawing capability you need, all you have
to do is use the right classes in the right ways and provide the layout.
The approach I'll show you will turn the table on the left into the table on the right:
Left: a default UITableView
with three rows. Right: the same table view after customization.
Coming from Mac OS X made it harder for me �UITableView
needs to be customized in a very particular way and structurally, it is very different to Mac OS X's NSTableView
and NSCell
drawing.
The following are all really bad ways to customize a table (even though you can make it work):
UITableView
to customize drawingUITableViewCell
to customize drawingUITableViewCell
s and returning these instead of using dequeueReusableCellWithIdentifier:
About the second point: it is okay to customize UITableViewCell
�but you shouldn't really use it for drawing. The UITableViewCell
class is more of a controller class �it handles behaviors and layout, not drawing. You can customize UITableViewCell
to load a specific contentView
(and do the custom drawing there).
That last point (that you should always use dequeueReusableCellWithIdentifier:
)
is only peripherally related to drawing but it will significantly slow
your drawing down if you avoid the normal cell queuing architecture.
There are only a few points to understand related to table drawing.
First: the UITableView
does not itself draw anything except the background. To customize the background of a UITableView
, all you need to do is set its backgroundColor
to [UIColor clearColor]
and you can draw your own background in a view behind the UITableView
.
Second: The tableHeaderView
(and the
table footer and section headers and footers) need not be just a title.
You can insert your own view, with its own subviews in the table header,
giving layout and custom drawing freedom.
Third: UITableViewCell
is composed of 5 different subviews. Customizing the right subview is the secret to good UITableViewCell
drawing. The subviews are:
backgroundView
�the entire background of the row (including what looks like the UITableView
's background in UITableViewStyleGrouped
style tables.selectedBackgroundView
�replaces the backgroundView
when the row is selected.image
�a customizable image (not actually a subview) at the left of the cell.accessoryView
�a customizable view at the right of the cell.contentView
�a customizable view between the image
and the accessoryView
(technically, it extends behind the image
).You can customize any of these (except image
which must be a UIImage
) using your own custom drawn views.
However, since the pixel size of the table never changes, it is often easiest just to use UIImageView
s
for each of them. Then you can take highly complex views drawn in
separate programs, cut them into the 5 necessary pieces and let the
automatic caching of UIImage
's named image cache manage your memory for you.
There is an argument against drawing your views in code and that is that the iPhone's drawing is not nearly as fast as Mac OS X. Operations like gradients and multiple overlapped components can really tax the iPhone.
Custom drawing code is a good choice for simple and flat colour
drawing. In most other cases �as in this post �I recommend you use UIImageView
to draw your views in a table.
With all custom drawing handled by UIImageView
, that still leaves some work to do. You must handle all layout and configuring of views.
As an example of what that means, have a look at the viewDidLoad
method for this post:
- (void)viewDidLoad { // // Change the properties of the imageView and tableView (these could be set // in interface builder instead). // tableView.separatorStyle = UITableViewCellSeparatorStyleNone; tableView.rowHeight = 100; tableView.backgroundColor = [UIColor clearColor]; imageView.image = [UIImage imageNamed:@"gradientBackground.png"]; // // Create a header view. Wrap it in a container to allow us to position // it better. // UIView *containerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, 60)] autorelease]; UILabel *headerLabel = [[[UILabel alloc] initWithFrame:CGRectMake(10, 20, 300, 40)] autorelease]; headerLabel.text = NSLocalizedString(@"Header for the table", @""); headerLabel.textColor = [UIColor whiteColor]; headerLabel.shadowColor = [UIColor blackColor]; headerLabel.shadowOffset = CGSizeMake(0, 1); headerLabel.font = [UIFont boldSystemFontOfSize:22]; headerLabel.backgroundColor = [UIColor clearColor]; [containerView addSubview:headerLabel]; self.tableView.tableHeaderView = containerView; }
This method handles the configuration of the tableView
(setting the backgroundColor
, rowHeight
and sets an image behind the table) but also creates its own layout for the table header.
The layout of the header here is for the table's header view. You can
include a custom header for every table section by implementing the UITableViewDelegate
method tableView:viewForHeaderInSection:
. There are equivalent properties and methods for the table and section footers.
It is possible to handle this type of layout in Interface Builder and load the XIB files for this type of layout. Sadly though, on the iPhone, reading loading lots of views from XIB files is slow (I suspect this is due to slow reading from the Flash memory) and doesn't always allow configuration of every property.
For this reason, I normally sketch my views in Interface Builder and
then manually recreate the same thing in code. That's what I've done
here: picking coordinates for the headerLabel
that looks balanced in the view.
The cell background needs to incorporate the tops and bottoms of table "sections". For this reason, the backgroundView
and selectedBackgroundView
normally need to be set on a row-by-row basis.
In your tableView:cellForRowAtIndexPath:
method where you are configuring the cell for a given row, this code will handle that behavior:
UIImage *rowBackground; UIImage *selectionBackground; NSInteger sectionRows = [aTableView numberOfRowsInSection:[indexPath section]]; NSInteger row = [indexPath row]; if (row == 0 && row == sectionRows - 1) { rowBackground = [UIImage imageNamed:@"topAndBottomRow.png"]; selectionBackground = [UIImage imageNamed:@"topAndBottomRowSelected.png"]; } else if (row == 0) { rowBackground = [UIImage imageNamed:@"topRow.png"]; selectionBackground = [UIImage imageNamed:@"topRowSelected.png"]; } else if (row == sectionRows - 1) { rowBackground = [UIImage imageNamed:@"bottomRow.png"]; selectionBackground = [UIImage imageNamed:@"bottomRowSelected.png"]; } else { rowBackground = [UIImage imageNamed:@"middleRow.png"]; selectionBackground = [UIImage imageNamed:@"middleRowSelected.png"]; } ((UIImageView *)cell.backgroundView).image = rowBackground; ((UIImageView *)cell.selectedBackgroundView).image = selectionBackground;
Layout of elements within the contentView
need only be set on construction of the contentView
(not on a row-by-row basis).
Sadly, laying out UILabel
s in the contentView
(like the "Cell at row X." and "Some other infomation." lables in this example) is a little verbose.
The following code is run immediately after the allocation of the UITableViewCell
to position the "Cell at row X." label:
const CGFloat LABEL_HEIGHT = 20; UIImage *image = [UIImage imageNamed:@"imageA.png"]; // // Create the label for the top row of text // topLabel = [[[UILabel alloc] initWithFrame: CGRectMake( image.size.width + 2.0 * cell.indentationWidth, 0.5 * (aTableView.rowHeight - 2 * LABEL_HEIGHT), aTableView.bounds.size.width - image.size.width - 4.0 * cell.indentationWidth - indicatorImage.size.width, LABEL_HEIGHT)] autorelease]; [cell.contentView addSubview:topLabel]; // // Configure the properties for the text that are the same on every row // topLabel.tag = TOP_LABEL_TAG; topLabel.backgroundColor = [UIColor clearColor]; topLabel.textColor = [UIColor colorWithRed:0.25 green:0.0 blue:0.0 alpha:1.0]; topLabel.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0]; topLabel.font = [UIFont systemFontOfSize:[UIFont labelFontSize]]; // // Create a background image view. // cell.backgroundView = [[[UIImageView alloc] init] autorelease]; cell.selectedBackgroundView = [[[UIImageView alloc] init] autorelease];
In my mind, it seems like there should be a more efficient way to do this. I hold out the possibility that there is.
This code spends most of its time working out where the label should be placed. It needs to go right of the image, left of the accessoryView
, middle of the row but above the "Some other information." label.
The accessoryView
is just a UIImageView
. The cell.image
is set as a property. These are extremely simple additions but they make the table cells far more impactful.
You can download the EasyCustomTable project as a zip file (60kb).
The code includes a #define
at the top that allows you to toggle the custom drawing on and off.
None of this is particularly revolutionary (it is all in the iPhone documentation) but it is still easy to miss the properties and methods that make it easy.
This does require custom images. If you've never drawn anything, now is a good time to learn inkscape (it's free and very good for the price). You could also use Adobe Illustrator but if you have that much money, pay an artist to draw it for you.
Layout of the content in code is probably the weakest part of the approach I've presented. To make it easier, you can pre-layout everything in Interface Builder and copy the layout into code. For complicated layouts, you could even try using nib2objc to convert your XIB files to code automatically (although I've never done this, I'm just mentioning nib2objc because the idea is so cool).
1 |
NSString *aNumberString = @"123" ; |
2 |
int i = [aNumberString intValue]; |
Convert int to NSString
1 |
int aNumber = 123; |
2 |
NSString *aString = [ NSString stringWithFormat: @"%d" , aNumber]; |
When you use UIDatePicker in your app you will a tumbler that lets users select a date that you may use in other parts of your app.
In order to use UIDatePicker you will need to implement the UIPickerViewDelegate delegate and then add a UIPicker to your subview. To retrieve the date that the user inputs you can use the target-action pattern to assign a method to execute once the user has picked a date. See the video below:
Here is the code from the example:
The first thing we needed to do was indicate that our view controller would be acting as a delegate for the UITextField. To do this you simply need to declare it in the interface file:
#import <UIKit/UIKit.h> //UIPicker requires an object that adopts the UIPickerViewDelegate protocol @interface DatePicker : UIViewController<UIPickerViewDelegate> { } @end
This part is straightforward enough: you essentially create a label and a date picker and add them both to the view controller’s subview collection. This is the typical pattern used to build up a view in code.
#import "DatePicker.h" @implementation DatePicker UILabel *label; UIDatePicker *datePicker; - (void)viewDidLoad { [super viewDidLoad]; //Create label label = [[UILabel alloc] init]; label.frame = CGRectMake(10, 10, 300, 40); label.textAlignment = UITextAlignmentCenter; //Use NSDateFormatter to write out the date in a friendly format NSDateFormatter *df = [[NSDateFormatter alloc] init]; df.dateStyle = NSDateFormatterMediumStyle; label.text = [NSString stringWithFormat:@"%@", [df stringFromDate:[NSDate date]]]; [df release]; [self.view addSubview:label]; [label release]; // Initialization code datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0, 250, 325, 250)]; datePicker.datePickerMode = UIDatePickerModeDate; datePicker.hidden = NO; datePicker.date = [NSDate date]; [datePicker addTarget:self action:@selector(changeDateInLabel:) forControlEvents:UIControlEventValueChanged]; [self.view addSubview:datePicker]; [datePicker release]; } - (void)dealloc { [datePicker release]; [label release]; [super dealloc]; } @end
This is the method that you assigned to the UIControlEventValueChanged event above using the target-action pattern.
- (void)changeDateInLabel:(id)sender{ //Use NSDateFormatter to write out the date in a friendly format NSDateFormatter *df = [[NSDateFormatter alloc] init]; df.dateStyle = NSDateFormatterMediumStyle; label.text = [NSString stringWithFormat:@"%@", [df stringFromDate:datePicker.date]]; [df release]; }
How this works is that when the user stops manipulating the tumbler an event called UIControlEventValueChanged occurs. Since we assigned the changeDateInLabel method as an action for this event the code in that method will execute updating the date in the label.
Learning how to program is 5% reading and 95% action so the best way to learn is to do it yourself. Create a simple iPhone app that uses the date picker code.
Let us know how it goes in the comments below!