Posts tagged ‘Cocoa Programming’

Objective-C Coding Style : Naming

“ภาษา” เป็นสิ่งที่ถูกประดิษฐ์ขึ้น เพื่อสื่อสารระหว่างกัน ภาษาไทย, ภาษาอังกฤษ, ภาษามือ, ภาษากาย ฯลฯ ล้วนแล้วแต่เป็นสิ่งที่ถูกประดิษฐ์ขึ้นเพื่อการสื่อสารทั้งสิ้น รวมทั้ง “ภาษาโปรแกรมมิ่ง” ก็คือสิ่งที่โปรแกรมเมอร์ใช้สื่อสารกับคอมพิวเตอร์เช่นเดียวกัน แต่นอกเหนือจากนั้น มันก็เป็นภาษาที่สื่อสารระหว่างโปรแกรมเมอร์เช่นเดียวกัน

สิ่งที่ผมเกริ่นมาข้างต้น คือความสำคัญของหัวเรื่องเรื่องนี้ เรื่อง Coding Style ของ Objective-C จริงๆเรื่องของ style มันเป็นของใครของมัน แต่ใน Entry นี้ผมขอโฟกัสกับ style ที่ “พึงกระทำด้วยประการทั้งปวง” การที่โค้ดของคุณอ่านง่ายเข้าใจง่ายไม่ซับซ้อน หรือที่ฝรั่งเรียกว่า “Clean Code” นั้น ช่วยเพิ่มประสิทธิภาพการทำงานเป็นทีม การลดเวลาการทำงานของคนที่มาทำงานต่อจากคุณ(เช่นการแก้บั๊ก) และง่ายต่อการ Optimize, Refactor หรือประโยชน์อื่นๆทั้งปวงในกระบวนการ Software Development

ใน Entry นี้ผมขอ Cover แค่เรื่อง “การตั้งชื่อ” ในการเขียน Code ภาษา Objective-C เท่านั้นนะครับ จริงๆเรื่องมันเล็กมาก มากจนผมไม่เคยคิดว่าจะมีคนทำด้วยซ้ำ แต่กระนั้น …ผมก็เจอกับตัวเองมาทุกเคส (จนต้องมาเขียนบลอกนี่แหละ) …แต่อย่างที่บอกว่าเรื่องของ Style เป็นเรื่องส่วนบุคคล มีแค่บางข้อเท่านั้นที่ผมอยากระบุเลยว่า “ควรทำตาม” แต่บางข้อก็ “แล้วแต่คุณ” ว่าแล้วก็ไปดูกันเลย

ใน Objective-C การตั้งชื่อตัวแปรโดยรูปแบบของคำ ควรเป็นลักษณะที่เรียกว่า “Camel Case” คือขึ้นต้นแต่ละคำในชื่อด้วยตัวพิมพ์ใหญ่ ส่วนตัวแรกจะเป็นพิมพ์ใหญ่ก็ต่อเมื่อเป็นชิื่อ Class เท่านั้น นอกนั้นขึ้นด้วยพิมพ์เล็กหมด เช่น MyClass, myAttribute, -(void)myFunctionWithParameter:(id)param เป็นต้น ข้อนี้จัดเป็นเรื่อง “พึงปฏิบัติตามด้วยประการทั้งปวง” แต่ข้อนี้ขอเพิ่มเติมว่า “เขียนภาษาไหนก็ตามแบบนั้นไป” ดีที่สุด

การตั้งชื่อตัวแปร หรือ attribute ของ Class ในภาษา Obj-C นั้น ถูกระบุในเอกสารของ apple อย่างชัดเจนว่า “ขึ้นต้นด้วยตัวพิมพ์เล็ก เสมอ!!” ใครที่กำลังติดนิสัยนี้อยู่ เปลี่ยนพฤติกรรมเถอะครับ เรื่องการตั้งชื่อเป็นเรื่องสามารถทำให้เกิดความเข้าใจผิดได้อย่างมากมาย เพราะการตั้งชื่อขึ้นต้นด้วยตัวพิมพ์ใหญ่ มันหมายถึง “ชื่อ Class” อันนี้เรื่องใหญ่ครับ ขอจัดความสำคัญอยู่ในระดับ “พึงปฏิบัติตามด้วยประการทั้งปวง”

ส่วนการตั้งชื่อโดยใช้ _(Underscore) ในการเติมด้านหน้าหรือต่อท้าย เป็นเรื่องที่ “ทำได้” ครับ เป็นการป้องกันการเรียกใช้ตัวแปรที่ชื่อซ้ำกันได้ เช่น tableView ที่เป็น attribute กับ tableView ที่เป็นค่าที่ return มาจาก delegate method หรืออาจใช้เพื่อระบุว่าเป็น class attribute เจาะจงว่าเป็น private class attribute แบบนี้ก็ยังโอเคครับ ไม่ผิดกฎอะไรใดๆ และเป็นที่ใช้กันอยู่ทั่วไป

เรื่องก้ำกึ่งอยู่เรื่องนึงเกี่ยวกับการตั้งชื่อคือเรื่องการ “ย่อ” เรื่องนี้อยากจะบอกถึงน้องๆที่เริ่มเขียนโปรแกรมลงไปเลยด้วยซ้ำ ว่า “อย่าติดนิสัยการตั้งชื่อโดยไม่สื่อความหมายเลยครับ” ไอ้ int a, bool b, char *c เนี่ย หยุดเถอะครับ โอเคว่ามันไม่ใช่กฎ แต่เรียกว่าเป็น “มารยาท” ก็แล้วกันครับ จะมียกเว้นไว้บ้างบางตัวก็ถ้าเป็น i แล้ววนลูป อันนี้เชื่อว่าเป็น de-facto (หมายถึงมาตรฐานที่รู้ๆกันทั่วไป) แต่ผมก็ใช้ idx มากกว่านะ อันนี้ก็แนะนำว่า ชื่อยาวนิด แต่สื่อความหมาย ดีกว่าสั้นกุดแต่ไม่รู้ห่าอะไรเลยครับ”

…นี่แหละครับ หลักๆของการตั้งชื่อในภาษา Objective-C แค่นี้เอง ง่ายมาก บางคนอาจไม่เข้าใจเพราะคุ้นกับภาษาอื่นมากกว่า เลยติดมา อันนี้ก็คงต้องปรับตัวให้เหมาะสมกับงานที่ทำและภาษาที่ใช้ไปนั่นแหละครับ ส่วนใครที่เริ่มหัดเขียนก็เริ่มฝึกให้ถูกต้องนะครับ

Entry นี้อาจจะดูเขียนแบบ “ใส่อารมณ์” เข้าไปบ้าง เพราะเจอกับตัวเองมาตรงๆทุกข้อ(เจอความลำบากในการแก้งานที่ตั้งชื่ออย่างมีปัญหา) ใครไม่พอใจก็ผ่านไปนะครับ อย่าว่ากัน สำหรับ Entry นี้ ขอจบแต่เท่านี้ครับ Happy clean coding ครับ :)

สิงหาคม 17, 2011 at 4:33 pm 2 ของความคิดเห็น

Magic of NSLog

การ Log ที่ดี ก็มีผลให้การทำงานเราสะดวกรวดเร็วขึ้นนะครับ แน่นอน การใส่สัญลักษณ์ไว้ใน Log ก็เป็นวิธีการแบบลูกทุ่งๆอย่างนึงที่ใช้ง่ายและได้ผล แต่เยอะๆเข้าแทนที่จะช่วยให้เร็วขึ้นมันจะเป็นตรงกันข้าม เพราะลายตากับ Log ไปหมด หรือจะให้ระบุรายละเอียดกันทุกบรรทัด ก็เสียเวลาเกินไป ใน Entry นี้ ผมเอา Trick การ Log ให้มีประสิทธิภาพมากขึ้นมาฝากครับ

Trick #1 : รู้รึเปล่า ว่าการทำงานพร้อมกับการ Log ค่าไปด้วย ทำให้เป็นการลดประสิทธิภาพการทำงานของ App ลงไปเยอะเลย ก่อน  Submit app ขึ้น App store เราควรจะ remove log ของเราออกไปด้วย …จริงอยู่ว่า App ทั่วไปอาจไม่มีผลต่อประสิทธิภาพโดยนัย แต่ถ้าเป็นเกม …มีนัยครับ ดังนั้น เราจะสร้าง Log ของเราเองขึ้นมา ที่ให้ Log ค่า ตอนที่ XCode ตั้งค่าเป็น Debug mode เท่านั้น ใส่โค้ดพวกนี้ในไฟล์ xxx_Prefix.pch ของโปรเจค เพื่อให้มีผลกับทั้งโปรเจคครับ

#ifdef DEBUG
# define JELog(fmt,...) NSLog(@"%@",[NSString stringWithFormat:(fmt), ##__VA_ARGS__]);
#else
# define JELog(...)
#endif

อธิบายโค้ดดังกล่าวได้ว่า เราสร้าง JELog ขึ้นมาเพื่อให้มันทำงานเหมือน NSLog เลย แต่จะพิมพ์ออกทางหน้าจอเมื่อ Preprocessor Macro ถูกเซตคำว่า DEBUG เอาไว้ แต่ถ้าไม่ได้เซตก็ไม่ต้องทำอะไร

ส่วนการเซตค่า  Preprocessor Macro ไปเลือกที่ Project->Edit Project Settings เลือก Debug ในช่อง Configuration และใส่ค่า DEBUG (หรือคำอื่นๆที่นิยามไว้) ใน Preprocessor Macro เซคชั่น GCC 4.2 – Preprocessing

…แค่นี้แหละครับ เสร็จแล้ว คราวนี้เรา Build ในโหมด Release ก็ไม่มี Log มาบั่นทอนประสิทธิภาพ App เราแล้ว

Trick #2: จะดีแค่ไหน ถ้าการ Log ทำให้เรารู้ได้ว่า มันโดน Log มาจาก Function อะไร และบรรทัดไหน เราสามารถเพิ่มมันได้เอง ในรูปแบบที่เราต้องการ และไม่ต้องเหนื่อยมาใส่แบบลูกทุ่งทีละบรรทัดทีละ Log ด้วย ทำได้ง่ายๆแบบนี้ครับ

#define JELog(fmt,...) NSLog((@"%s-%d:" fmt),__PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);

เพียงเท่านี้ เราก็ Log สิ่งที่เราต้องการพร้อมชื่อ Function และบรรทัดนั้นได้ง่ายๆแล้วครับ นอกจากนี้ยังมี __FILE__ เพื่อ log ชื่อไฟล์ชื่อนั้นอีก แต่ผมไม่ใส่เพราะผมไม่ใช้ อิอิ

ครับ สำหรับ Entry นี้ก็คงกล่าวถึงแค่นี้ ส่วนถ้าใครยังไม่สะใจอยากแต่งอยากเติมอีก ก็เข้าไปดูในเครดิตครับ มีคนทำไว้หลายแบบเหมือนกันสำหรับผม แค่นี้ก็สบายแล้ว หุหุ …ว่าแล้วก็จบเท่านี้ดีกว่า Happy Logging นะครับ ;)

Credits:

@memogames
iphoneprogrammingfordummies.blogspot.com
Stack Overflow : NSLog Tip & Trick

มีนาคม 24, 2011 at 5:23 pm ใส่ความเห็น

How to make XCode 3 with iOS SDK4 can build iOS SDK3.1.x

จาก Entry ก่อนๆหน้านี้ทีเคยบ่นๆไปเรื่องที่ XCode3 ตัวล่าสุด จะมาพร้อมกับ iOS ตั้งแต่เวอร์ชั่น 3.2 ขึ้นไป ผลก็คือ เราไม่สามารถ test application ของเราได้ว่า มันรันบน iOS3 ได้มั้ย

แอปเปิลคงตั้งใจจะดัน iOS4 เป็นมาตรฐานของ iPhone App แต่ในความเป็นจริง มาตรฐานการใช้งานมันอยู่ที่ “เครื่องมัน Jailbreak ได้ถึงเวอร์ชั่นไหนแล้ว” ดังนั้นสำหรับผู้พัฒนาบางทีก็ต้อง test กับ iOS เก่าๆซักนิดอย่าง iOS3 ด้วย Entry นี้จึงนำเสนอวิธีการ “ทำให้ XCode with iOS4 สามารถ Build กับ iOS3 ได้ด้วย!! ” เริ่มกันเลยครับ

สิ่งที่ต้องการ

1. iPhone SDK with iOS3 และ iOS4 ที่ติดตั้งกันแบบแยกโฟลเดอร์

วิธีการติดตั้งแบบแยกโฟลเดอร์ จะเปลี่ยนตอนเลือก Destination หรือติดไปก่อน เสร็จแล้วค่อยสร้าง folder ไว้อันนึงแล้วลากมาใส่จาก folder Developer แบบดิบๆเลยก็ได้ครับ

2. Terminal หาไม่ยากเลยครับ เครื่องมันมีมาแล้ว

วิธีการที่จะทำก็คือ สร้าง Symbolic link (หรือที่ในวินโดวส์เรียกว่า Shortcut) ที่ XCode จะทำการค้นหา Library ของ iOS3.x ลงไป ซึ่งก็จะอ้างอิงจาก iPhone SDK with iOS3 นั่นแหละครับ ขั้นตอนมีดังนี้

ผมขอแทน ${SDK3} = path ของ iOS3 SDK และ ${SDK4} = path ของ iOS4 SDK นะครับ

จากนั้นเปิด Terminal ขึ้นมา จากนั้น พิมพ์คำสั่งดังนี้

sudo ln -s ${SDK3}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.?.sdk ${SDK4}/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.?.sdk

จากนั้นใส่ password ของเครื่อง หลังจากนั้นเมื่อเปิด XCode ขึ้นมาใหม่ จะพบว่ามี Library ของ iOS3.1.? แล้วครับ ง่ายมากๆ

แต่อย่างไรก็ตาม วิธีการนี้ “ใช้ได้กับ Device เท่านั้นนะครับ Simulator ไม่ได้” เพราะ Simulator เป็น Simulator ที่ลง iOS4 อยู่แล้วครับ (หรือจะมีใครเขียน Script ให้เลือก simulator ตอน จะ run บน simulator ได้ก็แบ่งผมมั่งนะครับ อิอิ)

Entry นี้ก็ขอจบแต่เพียงเท่านี้ครับ หวังว่าจะมีความสุขจากการทำแอพ 2 เวอร์ชั่นโดยไม่ตัองสลับ XCode แล้ว …Happy XCoding ครับ :D

สิงหาคม 8, 2010 at 1:57 am 2 ของความคิดเห็น

iPhone’s project multi-target programming

ท่ามกลางสถานการณ์บ้านเมืองรุนแรงอย่างนี้ อัพบลอกดีกว่า (เอ๊ะ ไม่เกี่ยว) บลอก Entry นี้จริงๆควรจะอัพขึ้นไปตั้งแต่อาทิตย์ที่แล้ว แต่ด้วยเหตุผิดพลาดบางประการ ที่พิมพ์ไปเสร็จแล้วพออัพขึ้น ที่พิมพ์ไว้ดันหายไปไหนหมดก็ไม่รู้ จากนั้นก็ต้องมานั่งบิ๊วอารมณ์กันใหม่อีกรอบ บัดนี้ก็ถึงเวลาเขียนอีกรอบแล้ว

ในการพัฒนา App ไม่ว่าจะบน Mac หรือ iPhone ก็ตาม คงจะเคยเห็นกันว่าบาง App มีหลายเวอร์ชัน เช่น Lite, Standard, Full, Pro, etc.. ซึ่งความแตกต่างส่วนมากก็มักจะเป็นเรื่องของการจำกัด Feature หรือ App บางตัวที่อยู่บน Platform Mac ก็มาอยู่บน iPhone ต่อ อย่างเช่นเกม plant vs zombie หรือ twitter client อย่าง echofon เป็นต้น

ถ้าถามหลายๆคนถึงวิธีการ Develop App พวกนี้ให้เป็นหลายๆเวอร์ชั่นดังกล่าว หลายๆคนอาจจะใช้วิธีการ Duplicate project โดยการ Copy file ทั้งโปรเจคมา แล้วเข้าไปแก้โค้ด ตัด feature ไปตามเวอร์ชั่นๆไป (ดูไม่ make sense เท่าไหร่ แต่ผมเชื่อว่ามี และเยอะด้วยที่ทำแบบนี้) ผมเองก็เคยเช่นกันครับ ทำ App 8 เวอร์ชั่นเลยทีเดียว(จริงๆก็ไม่ได้เห็นด้วย แต่เขาสั่งมาก็ต้องทำ) ปัญหาที่เจออย่างหนักหน่วงเลยก็คือ “การแก้ไข Bug ยุ่งยากและเสียเวลาเป็นที่สุด” เพราะบั๊ก 1 จุดต้องใช้เวลาเพิ่มเป็น 8 เท่าและความแน่นอนว่าการแก้แบบเดียวกันกับโปรเจคเวอร์ชั่นต่างๆกัน มันอาจใช้ไม่ได้ผลซะด้วย

ดังนั้นการทำ App หลายๆเวอร์ชั่นด้วยวิธีการ Duplicate project ไม่น่าจะเป็นทางเลือกที่ควรนัก สำหรับการทำโปรเจคหลายๆเวอร์ชั่น โดยเฉพาะตอนที่ iPad มันเริ่มเข้ามาแล้วอย่างเช่นตอนนี้ ตามจริงมันก็ไม่ควรเป็นแบบนั้นอีกเช่นกัน ในเมื่อ XCode มี Target ให้ใช้อยู่ แล้วทำไมเราถึงไม่ใช้มันล่ะ

Target คืออะไร? Target เปรียบเสมือนพิมพ์เขียวที่กำหนดค่าต่างๆในขั้นตอนการ build ผลก็คือ เราสามารถกำหนดได้ว่าตั้งแต่ target ไหนชื่ออะไร ใช้ไอค่อนตัวไหน ไฟล์ไหนบ้างที่จำเป็นต่อการ build ยันรูปแบบผลที่ได้ว่าต้องการเป็น Application หรือ static library

เวลาเรา Create project ขึ้นมาซักตัว Target จะมีมาให้โดยปรกติเลย 1 ตัว ซึ่งจะอยู่ใน Section Targets ซึ่งเราสามารถเพิ่มได้ตอนไหนก็ได้ โดยการไปที่ Menu Project > New Target… จากนั้นจะขึ้น Window ขึ้นมาให้เลือก Target แบบที่เราต้องการ จากนั้นก็ตั้งชื่อ Target ที่สร้างขึ้น กด Finished ก็เป็นอันเสร็จเรียบร้อย (ตอนนี้เรากำหนดรูปแบบได้แล้ว)

ผลที่ได้จะได้ Target อันใหม่มา 1 อันและ Plist สำหรับ Target นั้นอีก 1 ไฟล์ Plist ก็เป็นที่รู้กันว่าไว้สำหรับการตั้งค่าต่างๆเช่น icon, bundle id, bundle name, etc… ซึ่งเราก็สามารถกำหนดได้ตามต้องการ Target ตัวใหม่ก็เช่นกันเราสามารถกำหนด Framework ที่ใช้ product name, platform ของ Application, etc… ตั้งค่าได้ทั้งหมดราวกับว่ามันเป็นคนละโปรเจคนั่นแหละครับ

องค์ประกอบของ Target ก็มีต่างกันไป ใน Entry นี้ขอพูดถึงแต่แบบ Application เท่านั้นนะครับ Application จะประกอบด้วย 3 ส่วนคือ

component.png

  1. Copy Bundle Resources – เป็นส่วนของ Resource ต่างๆใน Project พวก รูปภาพ, ไฟล์เสียง, วิดีโอ, ฯลฯ
  2. Compile Sources – เป็น Source code ล้วนๆครับ เอาแต่ไฟล์ .m เท่านั้น แล้วก็สำหรับ project จะต้องมี main.m ด้วยนะครับ อย่าลืม ไม่งั้น App จะรันไม่ได้นะครับ สำคัญมาก!!
  3. Link Binary With Libraries – เป็น Framework ที่ใช้ร่วมครับ ปรกติก็จะมี UIKit.framework ตัวนึงละ (สำหรับ iPhone/iPad) หรือไม่ก็ Cocoa.framework (สำหรับ Mac App)

วิธีการก็คือ Target ไหนใช้อะไร Compile บ้างก็ลากใส่เข้าไปตามหมวดหมู่ที่บอกข้างต้นครับ ไม่มีอะไรมาก หรือเวลาที่เรา Add ไฟล์อะไรก็ตามเข้ามาใน Project จะมี Sheet ขึ้นมาถามเราว่า ไฟล์นั้นจะ Copy หรือไม่ จะ link กับ Project อะไรยังไง ก็ให้ลองสังเกตด้านล่างครับ ว่าจะมีถาม Target ไหนด้วย เราก็สามารถเพิ่ม Resource ให้กับแต่ละ Target ผ่านทางนี้ได้เช่นกัน

addtoproj.png

อันนี้เป็นผลพลอยได้ของการสร้าง Target ตั้งแต่แรกๆครับ ส่วนถ้าไม่ได้ทำไว้แต่แรกก็ต้องมาลกาเพิ่มเอาเอง หรืออีกวิธีนึงคือ Duplicate target เอาแล้วค่อยเพิ่ม/ตัดออก เอาก็ยังได้ แต่วิธีนี้ต้อง config อะไรเยอะหน่อยเพื่อไม่ให้มันทับกัน และผมจะไม่ขอพูดถึงในที่นี้นะครับ มันค่อนข้างเยอะ

ต่อไปเป็นวิธีการเลือก Target วิธีการไม่ยากเลย กำหนดเอาที่ปุ่มด้านซ้ายบน กดแล้วจะขึ้น Menu ขึ้นมา ก็เลือก Target ที่ต้องการ build แค่นี้แหละครับ เสร็จแล้ว ง่ายจัง

settarget.png

เราได้ Target ใหม่แล้วสามารถ Config ได้ตามต้องการ คราวนี้เราจะสามารถใช้ source code เดียวกันแต่กำหนดการทำงานให้ต่างกันตาม target ได้หรือไม่ เพราะไม่งั้นการสร้าง target ก็ไม่มีประโยชน์อะไรเท่าไหร่ เพราะต้องเขียน class มากกว่ารอบเดียวขึ้นไปอยู่ดี ? คำตอบคือ “ได้ครับ” วิธีการที่ผมใช้ก็คือ “การตั้ง Tag ให้ Target แล้วเขียน source code กำหนดไปแต่ละ target โดยดูค่าจาก tag นั่นแหละ” (งงมั้ยเนี่ย)

ขั้นตอนก็คือ ไปตั้ง Tag ให้ Target โดยการ Double click ที่ target ที่ต้องการตั้ง tag (ขอเรียกแบบนี้แล้วกัน) ไปที่ Tab build แล้ว search Setting ว่า Other C Flags (อยู่ใน GCC 4.2 Language ครับ) ตั้งค่าเป็น “-DTARGET_NAME=1” ค่าหลังเครื่องหมาย = ก็แล้วแต่จะตั้งนะครับ

targetname.png

จากนั้นในส่วนของ Source code ที่ต้องการให้มีความแตกต่างในแต่ละ Target ก็จะใช้คำสั่ง #if เข้ามาช่วยครับ คำสั่งนี้จะเป็น if ในระดับของการ build เท่านั้น ไม่เกี่ยวกับ application logic ครับ ตัวอย่างการโค้ดก็เป็นแบบนี้

// Implement for each target

#if TARGET_NAME == 1
[aButton setHidden:NO];
[bButton setHidden:NO];
#elif TARGET_NAME == 2
[aButton setHidden:YES];
[bButton setHidden:NO];
#else
[aButton setHidden:YES];
[bButton setHidden:YES];
#endif

จากโค้ดตัวอย่างเป็นการ implement ง่ายๆนะครับ แค่ทำให้ดูว่า คำสั่ง #if, #elif, #endif รูปแบบเป็นไงบ้าง น่าจะพอเห็นภาพนะครับ

สำหรับ Entry นี้ก็คงจะพอแค่นี้นะครับ Entry หลังๆเกี่ยวกับ Target และ iPad ไว้ได้ลองของจริงแล้วจะเอามาแชร์ให้กันอีกนะครับ ตอนนี้ก็ happy coding สวัสดีครับ ;)

เมษายน 10, 2010 at 4:57 pm ใส่ความเห็น

How to send email from my iPhone app?

ในการพัฒนา iPhone app ถ้าเราต้องการจะส่ง email ด้วย Application ของเรา เราจะสามารถทำได้อย่างไร?

หลายๆคน (เช่นผม) คงคิดว่า “เรียก protocol mail:// เพื่อเปิดโปรแกรม mail.app  ของ iPhone ขึ้นมา” หรือไม่ก็ “generate html ที่เป็น tag mailto ขึ้นมา แล้วใช้่ openUrl ของ appDelegate เรียกเอา” ซึ่งผมเองก็ไม่เคยทดสอบวิธีพวกนี้(แต่คิดว่าน่าจะทำได้) แต่ถึงอย่างนั้นก็มีข้อเสียอย่างรุนแรงทั้งสองวิธี

วิธีเรียก protocol mail:// แอพเราจะถูกปิดตัวลง ส่วนวิธี mailto: ก็ยุ่งยากในการ attach ไฟล์ เช่นการแนบรูปภาพ ต้องแปลง UIImage เป็น NSData ที่ Encode ด้วย Base64 เพื่อที่จะได้ URI Scheme มา attach เป็น body (และผมก็ลองแล้วด้วย springboard crash ไปเลยละครับ)

อันที่จริง มันไม่ได้ยุ่งยากอะไรเลย เพราะ framework มันมีมาให้แล้วหล่ะครับ ชื่อว่า MessageUI.framework ครับ วิธีการ Add ก็ double click ที่ Project Target ของเรา ใน Tab general กด + ข้างล่างใน general link library เลือก MessageUI.framework กด Add แค่นี้ก็เสร็จแล้ว ง่ายซะไม่มี

ลองกดดูที่ Framework จะพบว่า “มี Class ให้ใช้แค่คลาสเดียวเอง คือ MFMailComposeViewController แค่ชื่อคลาสก็บอกแล้วว่าไม่ต้องการอะไรไปมากกว่านี้แล้ว

การใช้งาน ขั้นแรก ให้ import framework ลงไปในคลาสที่เราต้องการใช้งาน framework นี้ แนะนำให้ import ลงในไฟล์ header เลย (.h) เพราะเราจะต้องใช้ protocol ของ framework นี้ด้วย

// myVC.h

#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>

@interface myVC : UIViewController <MFMailComposeViewControllerDelegate> {}

– (IBAction)sendMail:(id)sender;

ในตัวอย่าง Class myVC ของผม ให้มี Action ไว้ปุ่มนึงเพื่อรับ action touch เพื่อส่งเมล์ สังเกตว่าผม Import protocol MFMailComposeViewControllerDelegate ไว้ด้วยนะครับ เอาไว้ทำอะไร เดี๋ยวมาดูกัน ต่อไปเราจะมา Implement method sendMail: กัน

// myVC.m


– (IBAction)sendMail:(id)sender {
MFMailComposeViewController *mail = [[MFMailComposeViewController alloc] init];
mail.mailComposeDelegate = self;

[mail setSubject:@”Hello, Developer”];
[self presentModalViewController:mail animated:YES];
}
การ Implement ก็เท่าที่เห็นครับ เนื่องจาก MFMailComposeViewController Subclass มาจาก UIViewController เพราะฉะนั้น เราจึงสามารถใช้คำสั่งแสดงผลแยกออกมาจาก NavigationController อย่างคำสั่ง presentModalViewController:animated: ก็ได้ หรือจะ push ลงใน Navigation Controller ก็ได้อีกเช่นกัน ในส่วนของค่าเริ่มต้น จะใส่หรือไม่ใส่อะไรก็ได้ อย่างในตัวอย่าง ทำการตั้ง Subject เอาไว้ว่า Hello, Developer ซึ่งส่วนอื่นๆก็เพิ่มได้หมด ตั้งแต่ ผู้ส่ง, ผู้รับ , CC, BCC หรือแม้แต่แนบไฟล์ก็ยังทำได้ จะซึ่งการแนบไฟล์ทำได้ดังนี้


[mail setSubject:@”Hello, Developer”];
[mail addAttachmentData:imageData mimeType:@”image/png” fileName:imagePath];
[self presentModalViewController:mail animated:YES];

นี่ครับ เพิ่มบรรทัดต่อบรรทัด imageData นี่เป็นไฟล์ NSData นะครับ ไม่ใช่ UIImage ถ้าไม่รู้ว่าแปลงยังไงก็ใช้คำสั่ง UIImagePNGRepresentation(<#your uiimage#>) หรือ UIImageJPGRepresentation(<#your uiimage#>) ก็ได้ครับ ขึ้นกับว่าต้นฉบับเป็นนามสกุลอะไร แต่ถ้าเป็นไฟล์อื่นก็หาวิธีแปลงเอาเองนะครับ บอกหมดคงไม่ไหว (ฮา) ส่วน mimeType ถ้าไม่รู้ Type ไหนต้องตั้งเป็นอะไร ลองดูที่นี่ครับ http://www.sfsu.edu/training/mimetype.htm มีบอกให้น่าจะเพียงพอ

ต่อไปก็เรื่องที่ค้างกันไว้ Protocol ที่ import ไว้ตั้งแต่ Header จะเห็นตอนเรา allocate ด้วยว่าเรามีการ set delegate มาที่ตัวเอง ซึ่ง method ที่เราใช้เรียกผ่าน delegate ก็คือ mailComposeController:didFinishWithResult:error: แปลง่ายๆก็คือ “ส่งเสร็จแล้วทำอะไร” จาก Parameter จะเห็นว่าจะ return ผลการส่งมาให้ทั้งหมด ส่งเสร็จหรือเปล่า ถ้าไม่เสร็จแล้ว Error ยังไง หน้าที่ของเราก็คือ “กำหนดว่า หลังจากส่งแล้วจะทำอะไรต่อ” ซึ่งผมจะไม่ทำอะไร นอกจาก “ส่งเสร็จแล้วก็ปิดหน้าส่ง email ไปซะ ทำได้ดังนี้

– (void)mailComposeController:(MFMailComposeViewController*)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError*)error
{
[controller dismissModalViewControllerAnimated:YES];
}

…เท่านี้ละครับ เสร็จแล้ว ง่ายอย่างไม่น่าเชื่อ สำหรับ Entry นี้ก็จบแค่นี้ละครับ มีอะไรเจ๋งๆง่ายๆ จะมาเขียนแนะนำกันอีก

Happy Coding ครับ

มีนาคม 23, 2010 at 3:26 pm 2 ของความคิดเห็น

Array in Objective-C & How to shuffle members

Class Array container ในภาษา Objective-C คือ NSArray และ NSMutableArray (จริงๆมีอีกอย่างคือ NSArrayController แต่ตอนนี้ยังมีอยู่ใน Cocoa เท่านั้น ไม่มีใน iPhone) ซึ่งความต่างระหว่าง NSArray และ NSMutableArray ก็คือ “ความสามารถในการแก้ไขภายหลังการ Initialize แล้ว” พูดง่ายๆ NSArray มี access attribute เป็น read-only แต่ NSMutableArray เป็น read-write นั่นเอง

คำถามต่อมาคือ “แล้วใช้ตัวไหนดี?” คำตอบก็คือ “ใช้ได้ทั้งคู่ครับ แตกต่างกันที่ Performance เล็กน้อยเท่านั้น” โดยส่วนตัวผมใช้ตามความสะดวก ถ้าจำเป็นต้อง read-write ก็ใช้ NSMutableArray ซึ่งการใช้งาน NSMutableArray ก็ดูตาม Document ได้เลย

อย่างที่บอกว่า NSMutableArray สามารถแก้ไขหลังจาก Initialize ได้ ดังนั้นแน่นอนว่า “ต้อง sort ได้ด้วย” ซึ่งใน Entry นี้ผมจะนำเสนอการ sort แบบ Random ผมต้องการ sort members ใน mutable array แบบสุ่ม ผมสามารถทำได้ดังนี้

int randomSort(id obj1, id obj2, void *context ) {
    // returns random number -1 0 1
    return (arc4random()%3 - 1);
}

- (void)shuffle {
    // call custom sort function
    NSMutableArray *puzzles = [NSMutableArray arrayWithObject:@"1", @"2", @"3", @"4", @"5", nil];
    [puzzles sortUsingFunction:randomSort context:nil];

    NSLog(@"Members after shuffle = %@", puzzles); 
}

จากโค้ด ผมสร้าง mutable array ขึ้นมา 1 ชุดแล้วสั่งให้ sort ด้วยฟังก์ชั่น randomSort ซึ่งใน randomSort จะทำการสุ่มค่า 3 ค่า คือ -1, 0, 1 ซึ่ง -1 หมายถึง การเรียงแบบตามลำดับ(NSOrderAscending) 0 หมายถึง การเรียงแบบปกติ (NSOrderSame) และ 1  หมายถึงการเรียงแบบกลับด้าน(NSOrderDescending) ซึ่งฟังก์ชั่น sort จะจัดลำดับของ member แต่ละตัวจากผลที่ได้จากสุ่มนั่นเอง

ส่วน arc4random เป็นฟังก์ชั่นสุ่มของ ภาษา C ครับ การใช้คำสั่ง rand() หรือ random() มันเป็นการสุ่มที่ไม่ dynamic เท่าไหร่นัก ผลลัพธ์ที่ได้จากการสุ่มจะเหมือนกันทุกครั้ง

เพียงเท่านี้เราก็สามารถสุ่มสมาชิกใน mutable array ได้แล้ว นอกจากนี้ยังมีการ sort อื่นๆได้อีกนะครับ เจออันไหนน่าสนใจผมจะมาแนะนำอีกใน Entry ต่อๆไปนะครับ

#reference http://stackoverflow.com/questions/56648/whats-the-best-way-to-shuffle-an-nsmutablearray

กุมภาพันธ์ 4, 2010 at 12:45 am ใส่ความเห็น

iPhone Development – View, Drawing

สำหรับ Entry นี้อาจจะพูดถึงส่วนของ View ใน iPhone นะครับ ซึ่งอยู่ใน Lecture ที่ 5 ของ stanford course ซึ่งจะกล่าวถึงคลาส UIView ของ iPhone, การใช้เครื่องมือสำหรับการวาดอย่างง่ายครับ

ไม่แน่ใจว่ามันจะก้าวกระโดดไปนิดรึเปล่า เอาเป็นว่า Entry นี้ผมจะหลักเลี่ยงการโค้ดนะครับ เริ่มกันที่ View นะครับ

ส่วนที่เป็น View คือส่วนที่เป็นสี่เหลี่ยมผืนผ้าตรงหน้าจอนั่นแหละครับ ทั้งหมดเลย ซึ่งภายใน View จะมีการทำงาน 2 อย่างอยู่ด้วยกัน คือ ส่วนของ Content และ Event

Content ก็คือตัวอักษร, รูปภาพหรือสิ่งที่ถูกวาดขึ้นบนจอภาพนั่นเองครับ ใน View แต่ละหน้าจะมี View หลักตัวหนึ่ง(เทียบตาม Tree ก็เป็น Root) เราเรียกว่า Superview ครับ ภายใน Superview ก็จะมี View ย่อยๆ เรียกว่า Subview ถ้านึกไม่ออกว่าเป็นไง ก็ลองนึกถึงกระดานปักหมุด(Superview)ที่มีกระดาษ(subview)แปะอยู่แล้วกันครับ ส่วน Event คือเหตุการณ์ที่เกิดขึ้นบนหน้าจอนั่นเองครับ ไม่ว่าจะเป็นการ Tab, การ Scroll ฯลฯ ทั้งหมดที่จะสั่งการให้ iPhone ทำงานได้ ถือเป็น Event หมด

เนื่องจาก View เป็นการวาดรูปภาพขึ้นมาบนส่วนของ Content ดังนั้นโครงสร้างต่างๆที่เกี่ยวข้องกับ View ก็เป็นเกี่ยวกับการวาดเช่นกัน ได้แก่ CGPoint หมายถึง ตำแหน่งที่อยู่(x,y) ,CGSize หมายถึง ขนาด(Width, Height) และ CGRect หมายถึงกรอบ(CGPoint, CGSize) ลักษณะ Coordinate ของ iPhone จะไม่เหมือนกับพิกัดการวาดรูปทั่วๆไปครับ จุด origin จะอยู่ที่มุมซ้ายบน (จากปกติมักจะอยู่ซ้ายล่าง)

เกี่ยวกับการวาดนั้น การอ้างอิงพิกัดหรือพ้ืนที่สำหรับวาด จะมีอยู่ 2 แบบ คืออ้างอิงจาก Frame และอ้างอิงจาก Bound ทั้งสองแบบแตกต่างกันตรงที่ “มันเป็นตำแหน่งเมื่อเทียบกับขอบกระดานปักหมุกหรือเทียบกับขอบกระดาษที่แปะ” ถ้า Frame คือเมื่อเทียบกับขอบกระดานปักหมุด แต่ถ้า Bound ก็เทียบกับกระดาษบนกระดานปักหมุด ถ้ายังไม่เข้าใจลองดูที่สไลด์ที่ 19 ก็อธิบายได้ชัดเจนดีครับ มีคำแนะนำอยู่นิดหน่อย (อยู่ในสไลด์ที่ 20) เกี่ยวกับการใช้งาน Frame และ Bound

สิ่งที่อยู่เหนือกว่า Superview คือ Window แต่ละ App จะมี 1 Window และ Window มักจะถูกจัดการมาให้เราใน template เรียบร้อยแล้ว

ส่วนเรื่องของการโค้ด มีตัวอย่างอยู่ใน สไลด์ที่ 25-27 นะครับ ไม่ขอเอามาเขียนซ้ำนะครับ แล้วก็คิดว่าโค้ดไม่น่าจะยากอะไรด้วย แต่ถ้าสงสัยก็โพสถามเอาละกันนะครับ

ต่อไปเรื่องของการ Drawing ครับ

การ Drawing ลงไปบน Content View นั่น จะทำโดยการ override คำสั่งการวาดไปในฟังก์ชั่น Drawrect ครับ แต่การสั่งให้ Draw จะไม่ได้ใช้วิธีการเรียกฟังก์ชั่น Drawrect ครับ แต่จะใช้คำสั่ง setNeedDisplay แทน งงมั้ยครับ? ลักษณะนี้เรียกว่า “Lazy Loading” ครับ view ของเราจะไม่ถูกวาดอยู่ตลอดเวลา แต่วาด(ตามที่สั่งใน drawRect)แค่เฉพาะตอนที่สั่ง(ว่า setNeedDisplay)เท่านั้น

เครื่องมือเกี่ยวกับการวาด จะเป็น Core Graphics และ Quartz 2D ทั้งสองตัวที่กล่าวถึงเป็น Graphic Library บน Cocoa ครับ ใช้โดย Library นี้เป็น C-Base API นะครับ ไม่ใช่ Objective-C ส่วนวิธีการวาดอย่างละเอียดนั้น คงไม่ขอพูดถึงในที่นี้ เพราะจะยาวจนเกินไปครับ(รวมทั้งอยากหลีกเลี่ยงการ Coding ใน Entry นี้ไปก่อนด้วย) คงจะมาอัพเดตในคราวหน้า แต่ถ้าสนใจก็ดูในสไลด์ก่อนก็ได้ครับ ใน Entry นี้ไว้เท่านี้ก่อนครับ

มีนาคม 23, 2009 at 2:03 am ใส่ความเห็น

Older Posts


del.icio.us For iPhone dev

Post Calendar

สิงหาคม 2019
พฤ อา
« ธ.ค.    
 1234
567891011
12131415161718
19202122232425
262728293031