2015年12月22日 星期二

用xib換掉storyboard

一、先到info.plist中把Main storyboard file base name這個tag拿掉

二、再到appDelegate的didFinishLaunchingWithOptions中加入rootViewController
例如
let navigationVC: UINavigationController = UINavigationController(rootViewController: yourViewController)
        
let frame = UIScreen.mainScreen().bounds
window = UIWindow(frame: frame)
        
window!.rootViewController = navigationVC
window!.makeKeyAndVisible()

2015年12月2日 星期三

是否需要在用來建TableView Cell的xib中指定file's owner?

不用,
xib是一個圖形與程式的連接控制介面,xib裡面放的UI物件,
file's owner是外部的控制器,所以你的cell不能在xib裡面跟外面同時存在。

意思是,你在xib裡面直接拉UI (cell也是一種UI)
外面用view controller指給file's owner來存取使用。

將狀態列文字改為白色(iOS 7.0以上適用)

先到info.plist中新增一行View controller-based status bar appearance 並設為 no

UIApplication.sharedApplication().statusBarStyle = UIStatusBarStyle.LightContent

2015年7月27日 星期一

為何int的負數會比正數多1

因為C語言中做了一個規定
當最高位的bit為1而其他都為0的時候,這個1既表示負號又表示它的數值。
以32bit為例,有號數的int範圍為
正: 2147483647 ~ 0  負: -1 ~ -2147483648

如此定義有二個好處,
一是計算方便,當你用2的補數往上做加法時,可以直接依二進制計算,不用另外調整正負號。
二是避免重覆定義0, 因為+0跟-0是同一個數,不需要分正負。

2015年7月24日 星期五

使用之前的cookie

//儲存
let urlEnconded = sessionId!.stringByAddingPercentEncodingWithAllowedCharacters(
                NSCharacterSet(charactersInString: "!*'();:@&=+$,/?%#[]").invertedSet)!
            let properties = [NSHTTPCookieDomain: WebURL.URL_MIGME_SERVER,
                NSHTTPCookiePath: "\\",
                NSHTTPCookieName: "eid",
                NSHTTPCookieValue: urlEnconded + MigboClient.cookiePrefix]
            if let cookie = NSHTTPCookie(properties: properties) {
                NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookie(cookie)
                System.setShareProperties(DefaultConfig.SHARE_SYS_COOKIES, value: properties)

            }

//使用
var url = NSURL(string: "yourUrl")
mutableRequest = NSMutableURLRequest(URL:url)
var cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies as! [NSHTTPCookie]
var headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies)
mutableRequest!.allHTTPHeaderFields = headers

self._webview.loadRequest(mutableRequest ?? NSURLRequest(URL:url))

2015年7月22日 星期三

String pool 概念

String Pool是為了解決太多內容同樣的String物件,造成記憶體空間浪費的問題,
實作上建立了一個hashMap,把字串內容轉換為key值來查找String物件實體,如果有則拿來用,如果沒有才new一個新的。

reflection概念


Reflection, 用在 Java 身上指的 是我們可以於執行期載入、探知、使用編譯期間完全未知的 classes。

Java 反射機制 - 侯捷

iOS也可以實現reflection的機制,關鍵字如下:
objc_getClass
objc_property_t
class_copyPropertyList
class_replaceMethod

2015年7月21日 星期二

如何抓到目前的thread標籤

//Objective-C
NSLog(@"%s", dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL));

//swift
println(String(UTF8String: dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))!)

2015年7月20日 星期一

navigationController連續push的crash問題

//實作UINavigationControllerDelegate
func navigationController(navigationController: UINavigationController,
        didShowViewController viewController: UIViewController,
        animated: Bool)
    {
        //TODO:擋住新的push或是在這裡放closure來push新的viewController
    }

2015年7月16日 星期四

Custom Preprocess Flag 設置自定義的預處理的方式

OTHER_CFLAGS (Other C Flags)
// 在Target>Build Setting>Custom Compiler Flags>Other C Flags
// 或Target>Build Setting>Swift Compiler>Custom Flags>Other Swift Flags
// 前面要加-D。舉例來說-DRELEASE -DDEVELOPMENT

GCC_PREPROCESSOR_DEFINITIONS (Preprocessor Macros)
// 在Target>Build Setting> Preprocessing > Preprocessor Macros

INFOPLIST_PREPROCESSOR_DEFINITIONS (Info.plist Preprocessor Definitions)
// 在Target>Build Setting> Packaging > Info.plist Preprocessor Definitions

2015年7月9日 星期四

Xcode的Debug小技巧

加入之後,可以看到解釋清楚的crash原因。

ASTableView的update方式

self._tableView.beginUpdates()
                self._tableView.deleteSections(indexSet, withRowAnimation: UITableViewRowAnimation.Fade)
                self._tableView.reloadSections(indexSet, withRowAnimation: UITableViewRowAnimation.None)
                self._tableView.endUpdates()

a social app needs to have precautions mechanism.

14.3 Details Your app enables the display of user-generated content but does not have the required precautions in place. Next Steps It
July 9, 2015 at 8:43 AM
From Apple
14.3 - Apps that display user generated content must include a method for filtering objectionable material, a mechanism for users to flag offensive content, and the ability to block abusive users from the service
14.3 Details

Your app enables the display of user-generated content but does not have the required precautions in place.

Next Steps

It is necessary that you put all of the following precautions in place:

- Require that users agree to terms (EULA) and these terms must make it clear that there is no tolerance for objectionable content
- Use moderators to flag and remove inappropriate content and offensive users
- Users need a mechanism to flag objectionable content and report users generating this content
- Developer must act on objectionable content reports within 24 hours by removing the content and ejecting the user who provided the offending content
- Developer needs a method for ejecting users who violate the terms of the EULA

Reply Use the field below to ask questions or provide additional information to the App Review team. Learn More Submit an appeal to the App Review Board.This field is required. 4000
Attach File
Send

Therefore, we have three task need to be finished:1. Term agree, 2. block user, 3. report abuse content

2015年7月6日 星期一

在yourUIView.swift中與xib做聯結

required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.view = NSBundle.mainBundle().loadNibNamed("ChatPanelView", owner: self, options: nil)[0] as! UIView
        self.view.frame = CGRectMake(0, 0, DefaultConfig.SCREEN_WIDTH, 87)
        self.addSubview(self.view)
        self.setNeedsUpdateConstraints()
        self.inputTextField.text = Language.get("TYPE_A_MESSAGE", comment: "Type a message")
        self.inputTextField.scrollEnabled = false

    }

2015年6月24日 星期三

更新ASTextNode文字

//重新給文字
self.textNode.attributedString = mutableAttributedString.copy() as! NSAttributedString
//取消上次的計算,然後重算大小
self.textNode.invalidateCalculatedSize()
self.textNode.measure(CGSizeMake(DefaultConfig.SCREEN_WIDTH, CGFloat(FLT_MAX)))

self.textNode.setNeedsLayout()

2015年6月23日 星期二

swift Range <---> objc NSRange 互換

extension String {
    func rangeFromNSRange(nsRange : NSRange) -> Range<String.Index>? {
        if let from = String.Index(self.utf16.startIndex + nsRange.location, within: self),
            let to = String.Index(self.utf16.startIndex + nsRange.location + nsRange.length, within: self) {
                return from ..< to
        }
        return nil
    }

    func NSRangeFromRange(range : Range<String.Index>) -> NSRange {
        let utf16view = self.utf16
        let from = String.UTF16View.Index(range.startIndex, within: utf16view)
        let to = String.UTF16View.Index(range.endIndex, within: utf16view)
        return NSMakeRange(from - utf16view.startIndex, to - from)
    }
}

2015年6月22日 星期一

利用closure的特性來解決callback時間差的問題

如果你要更新你的cell,但你的cell是reuse的,那第一時間用url去抓的image,可能在你滑動的過程中,callback回來時cell已經改放其他的data了。
原本我是想在download image的callback中加入一個key(在下例中是userName)來比對是否跟目前cell的userName相同,但這麼一來,就會破壞了download image API的內聚性。

在swift中比較好的做法是利用closure的特性來解決callback時間差的問題,
closure會把參數copy一份出來,而用self.去接的則是將外部的參數call by reference,
於是我們比較userName == self?.userName 就會等於是比較目前的跟之前的變數差異。
這樣就解決了。

    func setData(displayName: String, userName: String) {
        self.userName = userName
        
        WebImageFetcher.sharedInstance.getImageWithDisplayId(userName, size: ImageSizeEnum.SIZE_96X) {
            [weak self] (data) in
            if let img = data {
                if userName == self?.userName {
                    self?.iconView.image = data
                }
            }
        }
    }

2015年6月17日 星期三

客製化調整xib constraint的正確方式

//在其他func中呼叫setNeedsUpdateConstraints(在這以layoutSubviews為例)
override func layoutSubviews() {
        setNeedsUpdateConstraints()
    }

//把你要做的調整放在updateConstraints中
    override func updateConstraints() {
        textViewWidthConstraint.constant = scrollView.frame.width - imageViewWidthConstraint.constant
        super.updateConstraints()
    }

調整scrollView的內容位置到目前的textView的位置

 func fitScrollView(shouldMove: Bool) {
        if let scrollView = scrollView {
            var frame = inputTextField.frame
            var newSize = inputTextField.sizeThatFits(CGSizeMake(frame.width, CGFloat.max))
            frame.size.height = inputTextField.contentSize.height
            textViewHeightConstraint.constant = newSize.height
            textViewWidthConstraint.constant = scrollView.frame.width - imageViewWidthConstraint.constant
           
            let cursorPosition = inputTextField.caretRectForPosition(inputTextField.selectedTextRange?.start).origin
            if cursorPosition.y.isInfinite {
                var offset = inputTextField.frame.origin.y - scrollView.frame.size.height + newSize.height
                if offset > 0 {
                    if shouldMove {
                        scrollView.setContentOffset(CGPointMake(0, offset), animated: true)
                    } else {
                        scrollView.setContentOffset(CGPointMake(0, 0), animated: true)
                    }
                }
            }
        }
    }

2015年6月10日 星期三

抓到目前遊標(cursor)的位置

let cursorPosition = self.inputTextField.caretRectForPosition(self.inputTextField.selectedTextRange?.start).origin
        println("x:\(cursorPosition.x) y:\(cursorPosition.y)")

2015年5月30日 星期六

podfile的版號表達方式

Besides no version, or a specific one, it is also possible to use logical operators:
  • '> 0.1' Any version higher than 0.1 超過0.1
  • '>= 0.1' Version 0.1 and any higher version 0.1以上
  • '< 0.1' Any version lower than 0.1 未滿0.1
  • '<= 0.1' Version 0.1 and any lower version 0.1以下
In addition to the logic operators CocoaPods has an optimisic operator ~>:
  • '~> 0.1.2' Version 0.1.2 and the versions up to 0.2, not including 0.2 and higher 0.2以下(不含0.2),0.1.2以上(含0.1.2)
  • '~> 0.1' Version 0.1 and the versions up to 1.0, not including 1.0 and higher 1.0以下(不含1.0),0.1以上(含0.1)
  • '~> 0' Version 0 and higher, this is basically the same as not having it. 0以上(跟沒設定一樣)

2015年5月29日 星期五

簡單整合原生的Camera拍照功能(swift)

要先在class宣告中加入下列兩個Delegate
UIImagePickerControllerDelegate, UINavigationControllerDelegate

在你要用來trigger的function中寫
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera) {
            var cameraCaptureController = UIImagePickerController()
            cameraCaptureController.delegate = self
            cameraCaptureController.allowsEditing = true
            cameraCaptureController.sourceType = UIImagePickerControllerSourceType.Camera
            self.yourParentViewController?.presentViewController(cameraCaptureController, animated: true, completion: nil)
        }

最後再實作你的Delegate
//MARK: UIImagePickerControllerDelegate, UINavigationControllerDelegate
    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
        dismissCameraCaptureController(picker)
        //TODO: Pick camera capture here
        if let chosenImage = info[UIImagePickerControllerEditedImage] as? UIImage {
            //TODO: use chosenImage here
        } else {
            //TODO: Alert no picture selected.
        }
    }
    
    func imagePickerControllerDidCancel(picker: UIImagePickerController) {
        dismissCameraCaptureController(picker)
    }
    
    private func dismissCameraCaptureController(picker: UIImagePickerController) {
        picker.dismissViewControllerAnimated(true) { }
    }

2015年5月15日 星期五

一些設計原則

//global variable class 存取原則
  • 最好以某個專門負責的class(例如,web api)來作為唯一的修改點,其他地方都直接唯讀取用。
  • 如果是要存放修改點在其他class的變數,則要另外設local variable在修改完成後再直接copy過去
  • 如果不得已非要多方存取與修改時,則要設為synchonize以免在存取的中途被修改。

//TableViewCell 設定原則
  • 不要用同一種cell去換成新的UI style
  • 要針對各種不用的style去設計不同的cell來用
  • 用在viewController決定你要用什麼cell,把style obj傳入tableView中

//集合元素的設計原則
  • 不要在多對一之後,再一對多
  • 要把多個物件加入在同一個集合元素時,先把多組key統一整理成同一個唯一碼,再用這組唯一碼來還原為一對多。

//儘量避免函數副作用
  • side effects是有特別定義的,在IT中專指你的function會對呼叫它的地方造成影響。
  • 例如,函數被呼叫時會改動到全域變數,或是你用了inout參數,讓你的function可以直接改動它的輸入參數。
  • 比較好的作法
    • 儘量少用沒有參數的函數,因為這通常代表它會改動到全域變數。
    • 儘量讓每個函數都只實作輸入參數跟輸出之前轉換的邏輯。
//在swift 2.0之後,應該要優先使用struct和enum,需要繼承的時候再用class
  • swift 強化了struct和enum的功能,可以實踐class的大部份功用。
  • 原來的root class也可以用protocol加上protocol extension來實踐
  • 特別是要用RxSwift的時候,因為會用到大量的closure,struct可以確保線程安全。

//用struct的時候不要用到mutating來宣告function
  • swift的實作上struct的self物件是immutable的,所以如果你用了mutating,它會幫你再包一層操作,最後還是把你輸入的修改值重新new一個新的instance給你。

//RxSwift設計原則:如果可以,就別在viewModel中用subscribe
  • There are ideally no Variables, Subjects ....
  • There are no subscribe, bind, drive methods or subscriptions being made in any way.
  • Everything is just a definition. 
    • Because there are no subscriptions, there are no DisposeBags, etc .., no resource management is needed because all operators are already chaining disposing mechanism.
  • Everything is decoupled from the UI, there are no UIElements there, only pure logic

2015年4月24日 星期五

在textField輸入時,即時抓取text

//要用stringByReplacingCharactersInRange把textField delegate的回傳值給拚起來

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
        var username = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
        if self.checkUsernameRule(username) {
            self.submitButton.enabled = true
        } else {
            self.submitButton.enabled = false
        }
        
        // return NO to not change text
        return true

    }

func checkUsernameRule(username: String) -> Bool {
        //Username should be 6–20 characters long, and start with a letter
        println(username)
        if username =~ "^[a-zA-Z]{1}(\\w{5,20})$" {
            return true
        }
        return false
    }

2015年4月23日 星期四

swift inout 參數

swift會把參數都用let來設,如果要改變參數原來的值的話,就要把參數設inout。

2015年4月15日 星期三

build app指令

xcodebuild clean archive -scheme migme-debug -workspace iOSClient.xcworkspace -archivePath archive/migme.xcarchive -destination generic/platform=iOS
mkdir -p ipa

xcodebuild -exportArchive -archivePath archive/migme.xcarchive -exportPath ipa/migme.ipa -exportProvisioningProfile "migme adhoc distribution"

2015年4月14日 星期二

計算NSAttributedString的size

如果用先new好的textview來放,就會一直失敗
internal func calculateTextSize(attributedText: NSAttributedString, maxWidth: CGFloat) -> CGSize {
        var measureTextView = UITextView()
        measureTextView.attributedText = attributedText
        let size = measureTextView.sizeThatFits(CGSizeMake(maxWidth, CGFloat(FLT_MAX)))
        return size

    }

2015年4月11日 星期六

inline function 內嵌函數

在你宣告的function之前加上inline關鍵字
代表的是你想要在編譯時,直接把這個函式的內容寫進呼叫它的函式中。
原因是
在機器碼中,每個函式會寫到不同的記憶體位置,程式在執行呼叫時,會先停下來目前的狀態,跳到被呼叫的函式的記憶體區塊,執行完之後再跳回來。
在這段跳轉的過程中是會消耗系統資源的......
所以如果你的函式很短,或沒有要做什麼複雜計算,可以在編譯時,把被呼叫函式中的程式碼直接寫到目前的函式之中,來省下這段記憶體跳轉所造成的消耗。

note. inline只能用來建議編譯器你"想"這麼做,實際結果還是要看編譯器考慮到有沒有什麼限制而定。

2015年4月4日 星期六

判斷iOS現在是否為24時制

class func is24HourTimeFormat() -> Bool {
        var dateFormatter = NSDateFormatter()
        dateFormatter.dateStyle = NSDateFormatterStyle.NoStyle
        dateFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
        dateFormatter.locale = (NSLocale.currentLocale())
        var dateString = dateFormatter.stringFromDate(NSDate())
        let amRange = dateString.rangeOfString(dateFormatter.AMSymbol)
        let pmRange = dateString.rangeOfString(dateFormatter.PMSymbol)
        return (amRange == nil && pmRange == nil)

    }

2015年3月31日 星期二

設定textView的邊界留白

//在scrollView是contentInset,但textView被改到textContainerInset屬性
self.textView.textContainerInset = UIEdgeInsetsMake(25, 25, 25, 25)

2015年3月26日 星期四

Literal Expression

println(__FILE__) //顯示當下檔案的路徑
println(__LINE__) //顯示當下在這支檔案的那一行
println(__COLUMN__) //顯示當下在這支檔案的那一列
println(__FUNCTION__) //顯示當下func的名稱

全域函式 vs 巢狀函式 vs 閉包

全域函式
在swift中你可以直接開一個檔案來宣告func,這些func可提供全域使用

巢狀函式
一般宣告在class的 func都是,這種可以取用他domain range中的其他變數來用

閉包
這裡不需要宣告func name,也只可以取用宣告給它的參數來用

2015年3月19日 星期四

想在非主線程加入NSTimer,要放進Runloop中跑才能執行function

//建立Timer
let timer = NSTimer(timeInterval: self.responseTimeoutInterval, target: self, selector: Selector("responseTimeout:"), userInfo: Int(packet.header.transactionId), repeats: false)
            NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSRunLoopCommonModes)

//接收事件的function
dynamic func responseTimeout(timer: NSTimer) {
        logDebug(TAG, "responseTimeout")
        if let transactionId = timer.userInfo as? Int {
            self.resetResponseTimer(transactionId)
        }
        self.delegate?.onError(NSError(domain: fusionErrorDomain, code: Int(FusionErrorCode.ResponseTimeout.rawValue), userInfo: nil))
        self.socket?.disconnect()
    }

2015年3月18日 星期三

統一收notification再以delegate分派出去的設計方式

//在UIEvents統一接收notification
@objc protocol UIEvents {
    func onError(notification: NSNotification)
}

class UIEventHelper {

    class func registerFusionEvents(eventReceiver: AnyObject) {
        NSNotificationCenter.defaultCenter().addObserver(
            eventReceiver, selector: "onFusionError:",
            name: FusionService.fusionErrorNotification, object: nil)

    }
}

//在你要使用的class,加入註冊跟實作delegate
//viewWillAppear
self.registerNotifications()
//viewWillDisappear
self. unregisterNotifications()

    // MARK: - Notification
func registerNotifications() {
    UIEventHelper.registerFusionEvents(self)
}
    
func unregisterNotifications() {
    UIEventHelper.unregisterUIEvents(self)
}

func onError(notification: NSNotification) {
//TODO:實作你的code
}


2015年3月17日 星期二

學習資源

讀得多不如讀得好,iOS 開發者的 15 個閱讀清單http://punnode.com/archives/28835

十個必備的swift open source
http://m.csdn.net/article/2014-10-14/2822083-swift-ios-open-source-projects

swift學習資料大全
https://github.com/ipader/SwiftGuide/blob/master/README.md

2015年3月16日 星期一

在attributedString中加入subAttributed

//http://stackoverflow.com/questions/19551856/how-would-i-create-a-uibutton-with-two-lines-of-text-and-the-first-line-of-text

const CGFloat fontSize = 13;
UIFont *boldFont = [UIFont boldSystemFontOfSize:fontSize];
UIFont *regularFont = [UIFont systemFontOfSize:fontSize];
//UIColor *foregroundColor = [UIColor whiteColor];

// Create the attributes
NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys:
                               regularFont, NSFontAttributeName, nil];
NSDictionary *subAttrs = [NSDictionary dictionaryWithObjectsAndKeys:
                                  boldFont, NSFontAttributeName, nil];
const NSRange range = NSMakeRange(iFrom,iTo - iFrom);

// Create the attributed string (text + attributes)
NSMutableAttributedString *attributedText =
[[NSMutableAttributedString alloc] initWithString:aText
                                               attributes:attrs];
[attributedText setAttributes:subAttrs range:range];

// Set it in our UILabel and we are done!
[aLabel setAttributedText:attributedText];

2015年3月12日 星期四

navigationController點擊Back鍵的事件

//參考http://stackoverflow.com/questions/5217992/back-button-callback-in-navigationcontroller-in-ios

//trick寫法
-(void) viewWillDisappear:(BOOL)animated {
    if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
       // back button was pressed.  We know this is true because self is no longer
       // in the navigation stack.  
    }
    [super viewWillDisappear:animated];
}
//正常寫法
- (void) viewDidLoad
{
// change the back button to cancel and add an event handler
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@”back”
style:UIBarButtonItemStyleBordered
target:self
action:@selector(handleBack:)];

self.navigationItem.leftBarButtonItem = backButton;
[backButton release];

}
- (void) handleBack:(id)sender
{
// pop to root view controller
[self.navigationController popToRootViewControllerAnimated:YES];

}

UTC timestamp表示法

以1970年1月1日 00:00:00為基準
直接計算秒數
目前這一刻為1426131085
要使用時,再把它換算回正確的日期

2015年3月10日 星期二

swift程式碼小碎片筆記

//判斷是否為nil
//let image = UIImage(named: "foo")
if let image = UIImage(named: "foo") {
    //TODO: image != nil
} else {
    //TODO: image == nil
}

//用","將Array組成字串
let commaSeparatedArrayElements: String = ",".join(myArray)

//casting:用"as"來達成強轉型
var destinationViewController: Anyobject
let calcVC = destinationViewController as CalculatorViewController
//為了避免程式crash
if let calcVC = destinationViewController as? CalculatorViewController { ... }
//也可以用is來判斷型別
if destinationViewController is CalculatorViewController { ... }
//用括號強轉型
let button: AnyObject = UIButton.buttonWithType( UIButtonType.System)
let title = (button as UIButton).currentTitle

//取得substring
var s = "hello"
let index = advance(s.startIndex, 2) //取得"l"的index
s.splice("abc", index) //"heabcllo"
let startIndex = advance(s.startIndex, 1)
let endIndex = advance(s.startIndex, 6)
let substring = s[index..<endIndex] //"eabcl"

//雙問號代表"如果a不等於nil則,value = a,如果a為nil,則value = b"
let value = a ?? b 中的 a??b 代表 a != nil ? a! : b

//PropertyList就是plist,是定義為一個AnyObject的容器,這個容器只用來存放下列之中某一種(而且只能一種)Class:NSString、NSArray、NSDictionary、NSNumber、NSData、NSDate。

//String與NSString,Array與NSArray,Dictionary與NSDictionary可以直接casing所以as後面不用加問號
let string1 = "haha"
let string2:NSString = string1 as NSString

//Optional只是一種enum,在swift中沒有objective-C那種萬物皆空的nil(空指標),swift裡的nil也是一個值,但物值只要宣告出來就會指給它一個Optional<T>.None值,所以也可以直接判斷是否等於nil。

Conceptually it is like this (the <T> is a generic like as in Array<T>) ...
enum Optional<T> { 
 case None
 case Some(T) 
 }
let x: String? = nil
... is ... 
let x = Optional<String>.None 

let x: String? = “hello”
... is ... 
let x = Optional<String>.Some(“hello”) 

var y = x! 
... is ... 
switch x { 
 case Some(let value): y = value
 case None: // raise an exception 
}

//UIView的初始化有下列兩種,如果要用的話,請兩個都implement
init(frame: CGRect)  // initializer if the UIView is created in code
init(coder: NSCoder) // initializer if the UIView comes out of a storyboard

//這不是init方法,它只有在透過storyboard或nib去呼叫UIView時會被觸發,觸發的時機是在init(coder: NSCoder)之後,立刻執行
awakeFromNib

//在view座標系中,都要使用CGFloat
let cgf = CGFloat(aDoubleValue)

//view座標系
  • 原點在左上角
  • 單位是用point不是pixel
    • 如果你想知道當下的device中一個point等於幾個pixel,你可以呼叫UIView的var contentScaleFactor: CGPoint來查看(目前大部份是2)
  • 在view中繪圖(draw)的邊界
    • var bounds: CGPoint //這個view之內的座標系
  • 這個UIView的位置
    • var center: CGPoint  //這個view的中心點在superview中的座標
    • var frame: CGRect   //這個view在superview中的origin與size
//Never call drawRect!!
因為UIView會跟很多其他的view有關聯,所以在drawRect之前系統要先處理這些交互作用。
用下列的func代替,iOS會處理完這些事之後,再幫你呼叫drawRect()
setNeedsDisplay()
setNeedsDisplayInRect(regionThatNeedsToBeRedrawn: CGRect)

//swift字串截取
var string="1234567890" let index = advance(string.startIndex, 5) let index2 = advance(string.endIndex, -6); var range = Range<String.Index>(start: index2,end: index) var s1:String=string.substringFromIndex(index) var s2:String=string.substringToIndex(index2) var s3=string.substringWithRange(range) println(s1)//67890 println(s2)//1234 println(s3)//5

//closure
//在class中宣告為property
var didLinkTapped: ((chatroomName: String!) -> Void)?
//在要執行的時機點呼叫
self.didLinkTapped!(chatroomName: chatroomName)
//在你對外的方法中傳入實做的部份
init(id: String, text: String, didLinkTapped: ((chatroomName: String!) -> Void)?) {
        super.init(id: id)
        
        self.didLinkTapped = didLinkTapped
}

//dispatch_after in swift
let delayTime = dispatch_time(DISPATCH_TIME_NOW,
            Int64(sec * Double(NSEC_PER_SEC)))
        dispatch_after(delayTime, dispatch_get_main_queue()) { () -> Void in
            self.removeFromSuperview()

        } //sec要設為double

//enum轉數字
yourEnum.rawValue

//加入一個AlertView,按下任何地方即離開
let alertController = UIAlertController(
            title: NSLocalizedString("Error", comment: "Error"),
            message: error?.localizedDescription,
            preferredStyle: .Alert)
        let closeAction = UIAlertAction(title: NSLocalizedString("CLOSE", comment: "Close"), style: .Cancel) {
            (action) in
            alertController.dismissViewControllerAnimated(true, completion: nil)
        }
        alertController.addAction(closeAction)
        self.presentViewController(alertController, animated: true) {
            alertController.view.userInteractionEnabled = true
            if let window = alertController.view.window {
                var tapGestureRecognizer: UITapGestureRecognizer?
                tapGestureRecognizer = window.tapped() {
                    (sender: UITapGestureRecognizer) -> Void in
                    if sender.state == UIGestureRecognizerState.Ended {
                        let location: CGPoint = sender.locationInView(alertController.view)
                        // check tap point in alert window
                        if alertController.view.pointInside(location, withEvent: nil) == false {
                            alertController.dismissViewControllerAnimated(true, completion: nil)
                        }
                    }
                    window.removeGestureRecognizer(tapGestureRecognizer!)
                }
                tapGestureRecognizer?.cancelsTouchesInView = false
            }

        }

//UIImageView rotation extension
import Foundation

extension UIImageView {
    func rotate360WithDuration(duration: Double, repeatCount: Float) {
        var fullRotation: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation")
        fullRotation.fromValue = 0
        fullRotation.toValue = ((360 * M_PI) / 180)
        fullRotation.duration = duration
        fullRotation.speed = 2.0
        if repeatCount == 0 {
            fullRotation.repeatCount = MAXFLOAT
        } else {
            fullRotation.repeatCount = repeatCount
        }
        self.layer.addAnimation(fullRotation, forKey: "360")
    }
    
    func stopAnimation() {
        self.layer.removeAllAnimations()
    }

}

//更換UIBarButtonItem
let loadingImageView = UIImageView(image: UIImage(named: "IOS_loading_static") )
loadingImageView.rotate360WithDuration(2.0, repeatCount: 0)
let barButton = UIBarButtonItem(customView: loadingImageView)

self.navigationItem.rightBarButtonItem = barButton

//NSUserDefault
let defaults = NSUserDefaults.standardUserDefaults()
//read
let plist: AnyObject = defaults.objectForKey(String)
//write
defaults.setObject(AnyObject, forKey: String)
//大部份會自動儲存,但你可以用這個判斷來處理少部份的情況
if !defaults.synchronize() { /*TODO:error handling*/}

//在enum中使用switch
public enum LogLevel: Int {
    case Error = 0
    case Warning
    case Info
    case Debug
    case Verbose
    
    var prefix : String {
        get {
            switch self {
            case .Error: return "E"
            case .Warning:  return "W"
            case .Info:     return "I"
            case .Debug:    return "D"
            case .Verbose:  return "V"
            }
        }
}
}

//回到上一頁(不管是present或push出來的vc)
dynamic func popToPrevious() {
        logInfo(TAG, "popToPrevious")
        if self.parentNavigationController != nil {
            //self.parentNavigationController是自己創的,看有沒有被給值
            self.parentNavigationController?.popViewControllerAnimated(true)
        } else if let navigation = self.navigationController {
            navigation.popViewControllerAnimated(true)
        } else if self.presentingViewController != nil
            && (self.presentingViewController is NewLoginViewController) == false {
                self.dismissViewControllerAnimated(true, completion: nil)
        }

    }

//在textView中加入圖片
var textAttachment = NSTextAttachment()
        textAttachment.image = UIImage.scaleToSize(image, size: CGSize(width: UIMessageTheme.AvatarTitle.size, height: UIMessageTheme.AvatarTitle.size))
        let imageString = NSAttributedString(attachment: textAttachment)
        var mas = NSMutableAttributedString(attributedString: inputTextField.attributedText)
        mas.replaceCharactersInRange(NSMakeRange(mas.length, 0), withAttributedString: imageString)

        self.inputTextField.attributedText = mas;

//偵測status bar點擊事件(寫在AppDelegate中)
public override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        super.touchesBegan(touches, withEvent: event)
        let touchesSet = touches as NSSet
        if let touch = touchesSet.anyObject() as? UITouch {
            let location = touch.locationInView(self.window)
            //你偵測的高度
            if location.y > 0 && location.y < 20 {
                self.touchStatusBar()
            }
        }
    }
    
    private func touchStatusBar() {
        NSNotificationCenter.defaultCenter().postNotificationName(DefaultConfig.STATUS_BAR_CLICK, object: nil)

    }

//設定UITextView的文字邊界
self.yourTextView.textContainerInset = UIEdgeInsets(top: 10.5, left: 9, bottom: 9.5, right: 15)

//加上模糊效果
//should import UIKit
func insertBlurView (view: UIView,  style: UIBlurEffectStyle) {
    view.backgroundColor = UIColor.clearColor()
    var blurEffect = UIBlurEffect(style: style)
    var blurEffectView = UIVisualEffectView(effect: blurEffect)      
    blurEffectView.frame = view.bounds
    view.insertSubview(blurEffectView, atIndex: 0)
}

//取得現在時間(原來是用[NSDate date])
let date = NSDate()

//去掉空尾空白和換行
let dataString = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())

//data to string (utf8)
let data = command.dataUsingEncoding(NSUTF8StringEncoding)

//data to string (utf8)
let string = String(data: data!, encoding: NSUTF8StringEncoding)


//移除present出來的viewController
func dismissViewController(viewController: UIViewController, animated: Bool) {
    if viewController.isBeingDismissed() || viewController.isBeingPresented() {
        dispatch_async(dispatch_get_main_queue()) {
            dismissViewController(viewController, animated: animated)
        }
        return
    }
 
    if viewController.presentingViewController != nil {
        viewController.dismissViewControllerAnimated(animated, completion: nil)
    }
}

// UIColor Extension
    // 自定義color
    class func styleDarkColor() -> UIColor {
        return UIColor.hexToColor(0xFF008CEE)
    }
    
    class func hexToColor(hex: UInt32) -> UIColor {
        return UIColor(red: CGFloat((hex & 0xFF0000) >> 16) / 255, green: CGFloat((hex & 0xFF00) >> 8) / 255 ,
            blue: CGFloat(hex & 0xFF) / 255 , alpha: CGFloat((hex & 0xFF000000) >> 24) / 255)
    }
    
    // 十六進制String轉UIColor
    class func hexStringToColor(hexString: String) -> UIColor {
        var rgbValue: UInt32 = 0
        let scanner = NSScanner(string: hexString)
        scanner.scanLocation = 0
        scanner.scanHexInt(&rgbValue)
        return UIColor(red: CGFloat((rgbValue & 0xFF0000) >> 16)/255.0,
            green: CGFloat((rgbValue & 0xFF00) >> 8)/255.0,
            blue: CGFloat(rgbValue & 0xFF)/255.0,
            alpha: 1.0)
    }
    
    // 以這個顏色產生一個長度為1的小圖(在button好用)
    func tinyImage() -> UIImage {
        let rect = CGRectMake(0.0, 0.0, 1.0, 1.0)
        UIGraphicsBeginImageContext(rect.size)
        let context = UIGraphicsGetCurrentContext()
        CGContextSetFillColorWithColor(context, self.CGColor)
        CGContextFillRect(context, rect)
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return image
    }

//改變navigationController的顏色
    //navigation bar
    navigationController.navigationBar.barTintColor = UIColor.yellowColor()
    //navigation bar text
    navigationController.navigationBar.titleTextAttributes =  [UITextAttributeTextColor: UIColor.yellowColor()]

//改變tabBarController的顏色
    //tab bar
    tabBarController.tabBar.barTintColor = UIColor.yellowColor()
    //tab bar text
    tabBarController.tabBar.tintColor = UIColor.yellowColor()

//不讓app進入休眠

UIApplication.sharedApplication().idleTimerDisabled = true
同時最好去實作appDelegate裡面的func applicationWillResignActive(application: UIApplication) { ... }
把可以發生的中斷事件都處理好,例如phone call、SMS等。

//客製化UITextField的輸入鍵盤
    //換掉inputView就行
        yourTextField.inputView = yourPickerView
    //換回來系統預設的
        yourTextField.inputView = nil
        yourTextField.reloadInputViews()
    //這是換掉鍵盤上面那一條控制用的Bar

        yourTextField.inputAccessoryView = yourCustomAccessoryView

//使用其他預設的鍵盤-改變UIKeyboardType
self.phoneTextField.keyboardType = .PhonePad

self.emailTextField.keyboardType = .EmailAddress

// UIButton預設是置中對齊,如果要改成向左就這樣設

yourButton.contentHorizontalAlignment = .Left

//object to data
let yourViewToData = NSKeyedArchiver.archivedDataWithRootObject(yourView)
if let yourViewFromData = NSKeyedUnarchiver.unarchiveObjectWithData(yourViewToData) as? UIView {
  // Do what you want with your view
}

2015年3月6日 星期五

git (指令/概念) 筆記

//指令參數
git commit -m "Fixed a typo." //短指令
git commit --message="Fixed a typo." //長指令
通常短指令是長指令相對應的縮寫,但有些指令只存在在其中之一。

//容器 = repository
一、建立repository
1.先開一個folder
mkdir ~/public_html
cd ~/public_html
echo 'My Website is alive' > index.html
2.轉換成repository
git init

二、在repository中加入檔案
git add index.html
//查詢status
git status
//commit(用-m參數加說明)
git commit -m "Your Commend"

三、在commit前要先將你的姓名跟信箱給註冊好,不然無法執行
git config user.name "Harvey"
git config user.email "harveyhu@hotmail.com"

四、檢視你的commit記錄
git log
將以時間最近往下排到時間最遠
log會顯示:作者(email)、commit時間、註解、commit id
//如果要查看更詳細的內容
git show yourCommitID
//如果不指名commitID,則會顯示最近的一筆
git show
//查看目前branch的詳細資料,--more=10代表最多顯示10筆commit
git show-branch --more=10

五、檢視兩個commit程式的差異(較早的commit \ 較近的commit)
git diff olderCommitID \ newerCommitID

六、移除檔案和更改檔名
//移除檔案
git rm poem.html
git commit - m "Remove a poem"
//更改檔名有兩種方式
1.先改檔名,再改git
mv foo.html bar.html
git rm foo.html
git add bar.html
git commit -m "Moved foo to bar"
2.直改用git mv
git mv foo.html bar.html
git commit -m "Moved foo to bar"

七、clone你的repository
//先到你要放new repository的folder,就會將my_website建立在這個folder中
git clone public_html my_website

//如果你有空的話,也可以用linux指令來看看兩邊有什麼差異
ls -lsa public_html my_website
diff -r public_html my_website

八、設定config檔
//git的config檔都是以.ini為副檔名的文字檔案
//它的優先順序如下

  1. .git/config:這個config的scope只影響repository之中的檔案,可以用--file來設定
  2. ~/.gitconfig:以使用者為scope,使用--global來設定
  3. /etc/gitconfig:影響範圍可以到全系統,這檔案可能在/usr/local/etc/gitconfig或者根本不存在。
//如果想要看目前的config設定,可以用-l
git config -l

//可以用--unset來移除某設定
git config --unset --global user.email

九、建立指令的縮寫
//建一個show-gragh別名,並讓它可在所有我建立的repository中都可以使用,用來取代冗長的log指令
git config --global alias.show-gragh \ ''log --graph --abbrev-commit --pretty=oneline

十、找出之前改出bug的commit
//使用git bisect
git bisect start
//告知目前HEAD是壞的
git bisect bad
//告知之前正確的版本是在何時
git bisect good v2.6.27
//接下來git會自動幫你用二分法挑選你之前的commit,你只要回應它是好的或是壞的
git bisect good
git bisect bad
//你可以看這次bisect的所有log
git bisect log
//你可以目前仍在範圍中的commit (--pretty是用來對輸出結果排版的參數)
git bisect visualize --pretty=oneline
//你如果查看目前的分支,就會發現git是幫你新建一個branch來執行bisect,當你要結束查詢時,git會幫你switch回原本的分支
git bisect reset
//如果你想重新來一次
git bisect replay

十一、檢視分支
//列出本地端所有分支名稱
git branch
//列出遠端追踨分支
git branch -r
//同時列出本地和遠端
git branch -a
//列出詳細branch資料
git show-branch

十二、diff的四種用法
git diff //比較目前工作目錄與上一次add的差異
git diff HEAD //比較目前工作目錄與上一次commit的差異
git diff --cache  //比較現在add的與上次commit的差異
git diff version1 version2 //比較兩次commit之間的差異(參數順序有差異)

2015年3月3日 星期二

GDB on xcode

GDB(GNU Debugger)是在linux上的除錯工具

網路上看到已經有人做了詳盡說明
http://tetralet.luna.com.tw/?op=ViewArticle&articleId=187&blogId=1

2015年2月16日 星期一

.txt file to NSString

NSString *txtFilePath = [[NSBundle mainBundle] pathForResource:@"/text" ofType:@"txt"];

NSString *content = [NSString stringWithContentsOfFile:txtFilePath usedEncoding:nil error:nil];

2015年1月29日 星期四

NSUserDefaults無預警變為nil的問題

//在每次改變其值之後都執行它的synchronize方法即可
[[NSUserDefaults standardUserDefaults] synchronize];

2015年1月28日 星期三

解決alertController無法像alertView那樣重覆推出來的問題

UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;
        //因為一個ViewController只能present新的VC一次,故如果它present過了,就去抓取它present出來的VC來再present新的VC
        if (presentingViewController.presentedViewController != nil)
        {
            presentingViewController = presentingViewController.presentedViewController;
        }

        [presentingViewController presentViewController:alertController animated:YES completion:nil];

2015年1月27日 星期二

如何將NSString轉成NSNumber

//新增一個numberFormatter
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
//將style設定為NSNumberFormatterDecimalStyle
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
//NSString轉成NSNumber

NSNumber *piNumber = [numberFormatter numberFromString:@"3.141592654"];

Note.若數字格式不正確,則piNumber會被指定為nil。