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