onthewavesのコードスニペット

SwiftやObjective-Cを中心にiPhoneアプリ開発に関するコードスニペットを書きます。

コードスニペット(Push通知編 アプリ側)

Push通知のコードスニペット

実装手順

  1. Push通知の設定
  2. デバイストークンの送信
  3. Push通知を受け取る

1. Push通知の設定

iOS7以下とiOS8以上で仕様が違う

import UIKit

class RemotePushManager {
    
    class func registerForRemoteNotifications(application: UIApplication) {
        
            if #available(iOS 8.0, *) {
                let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
                application.registerUserNotificationSettings(settings)
                application.registerForRemoteNotifications()
            } else {
                application.registerForRemoteNotificationTypes([.Alert, .Badge, .Sound])
            }
    }
}
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        RemotePushManager.registerForRemoteNotifications(application)
        return true
    }

2. デバイストークンの送信

自前サーバーへデバイストークンを送信する

    //MARK: - リモートプッシュ通知
    func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
        
        let characterSet: NSCharacterSet = NSCharacterSet(charactersInString: "<>")
        
        let deviceTokenString: String = (deviceToken.description as NSString)
            .stringByTrimmingCharactersInSet(characterSet)
            .stringByReplacingOccurrencesOfString(" ", withString: "") as String
        print(deviceTokenString)

        //TODO : デバイストークンの送信        
    }
    
    func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
    }

3. Push通知を受け取る

import UIKit

class RemotePushManager {
        
    class func received(application: UIApplication, launchOptions: [NSObject: AnyObject]?) {
        
        if let launchOptions = launchOptions {
            
            if let aps = launchOptions["aps"] as? [NSObject: AnyObject] {
                
                print(aps["alert"])
                print(aps["sound"])
                print(aps["badge"])
            }
        }
    }
}
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        RemotePushManager.registerForRemoteNotifications(application)
        
        if let launchOptions = launchOptions {
            
            if let option : NSDictionary = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
            
                self.application(application, didReceiveRemoteNotification: option as [NSObject : AnyObject])
            }
        }
        return true
    }

    //Push通知受信時とPush通知をタッチして起動したとき
    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
        
        RemotePushManager.received(application, launchOptions: userInfo)
        
        switch application.applicationState {
         case .Inactive:
            break
        case .Active:
            break
        case .Background:
            break
        }
    }
}

APNSから受信するデータは、下記のようなイメージ

{
  "aps":{
    "alert": "New Message",
    "badge": 3,
    "sound": "sound1.aiff",
    "content-available": 1  // -> サイレントプッシュ
  },
  "name": "value"
}

サーバー側

サーバーサイトは、下記のようなライブラリを使うと便利です。

github.com

コードスニペット (UIWebView編)

UIWebViewのコードスニペット

よく使いそうなメソッドをExtension化しました。

import UIKit

extension UIWebView {
    
    /**
     UIWebViewを表示する(サーバーのファイル)
     */
    func open(urlString: String) {
        self.loadRequest(NSURLRequest(URL: NSURL(string: urlString)!))
    }
    
    /**
     UIWebViewを表示する(ローカルファイル)
     */
    func openLocalFile(fileName: String, ofType: String) {
        
        let path: NSString = NSBundle.mainBundle().pathForResource(fileName, ofType:ofType)!
        self.open(path as String)
    }

    /**
     ユーザーエージェントの取得
    */
    func userAgent() -> String {
        return self.stringByEvaluatingJavaScriptFromString("navigator.userAgent") ?? ""
    }

    /**
     ユーザーエージェントの変更
     */
    func updateUserAgent(str: String) {

        if let userAgent = self.stringByEvaluatingJavaScriptFromString("navigator.userAgent") {
            
            let customUserAgent = userAgent.stringByAppendingString(" \(str)")
            let dic = ["UserAgent" : customUserAgent]
            NSUserDefaults.standardUserDefaults().registerDefaults(dic)
        }
    }

    /**
     JSの関数を呼ぶ(戻り値なし)
    */
    func jsFunc(jsString: String) {
        self.stringByEvaluatingJavaScriptFromString(jsString)
    }

    /**
     JSの関数を呼ぶ(戻り値あり)
    */
    func jsFuncCallBack(jsString: String) -> NSDictionary? {
        let result = self.stringByEvaluatingJavaScriptFromString(jsString)
        if let resultData = result?.dataUsingEncoding(NSUTF8StringEncoding) {
            return try! NSJSONSerialization.JSONObjectWithData(resultData, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
        }
        return nil
    }
    
    /**
     ページ内アンカー移動
    */
    func internalAnchor(request: NSURLRequest, anchorName: String) {
        
        let names = request.URL?.absoluteString.componentsSeparatedByString("#")
        jsFunc("window.location.hash = \"\(names?.first!)\"")
    }
}

UIWebViewのDelegateをクラス化しました。

import UIKit

typealias WebViewDidFinishLoad = (UIWebView) -> ()
typealias WebViewDidStartLoad = (UIWebView) -> ()
typealias WebViewDidFailLoadWithError = (UIWebView, NSError?) -> ()

class WebViewDelegate: NSObject, UIWebViewDelegate {
    
    var webViewDidFinishLoad: WebViewDidFinishLoad?
    var webViewDidStartLoad: WebViewDidStartLoad?
    var webViewDidFailLoadWithError: WebViewDidFailLoadWithError?
    
    func setupWebViewDidFinishLoad(webViewDidFinishLoad: WebViewDidFinishLoad) {
        self.webViewDidFinishLoad = webViewDidFinishLoad
    }
    
    func setupWebViewDidStartLoad(webViewDidStartLoad: WebViewDidStartLoad) {
        self.webViewDidStartLoad = webViewDidStartLoad
    }
    
    func setupWebViewDidFailLoadWithError(webViewDidFailLoadWithError: WebViewDidFailLoadWithError) {
        self.webViewDidFailLoadWithError = webViewDidFailLoadWithError
    }
    
    //MARK: - UIWebViewDelegate
    //Pageがすべて読み込み終わった時呼ばれるデリゲートメソッド.
    func webViewDidFinishLoad(webView: UIWebView) {
        
        if webViewDidFinishLoad != nil{
            
            if let block = webViewDidFinishLoad {
                block(webView)
            }
        }
    }
    
    //Pageがloadされ始めた時、呼ばれるデリゲートメソッド.
    func webViewDidStartLoad(webView: UIWebView) {
        
        if webViewDidStartLoad != nil{
            
            if let block = webViewDidStartLoad {
                block(webView)
            }
        }
    }
    
    func webView(webView: UIWebView, didFailLoadWithError error: NSError?) {
        
        if webViewDidFailLoadWithError != nil{
            
            if let block = webViewDidFailLoadWithError {
                block(webView, error)
            }
        }
    }
}

外部HTMLファイルを読みには、

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var webView: UIWebView!
    var webViewDelegate =  WebViewDelegate()
    let requestURL = "http://www.apple.com/jp/iphone-6s/"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }
    
    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)        
        webView.open(requestURL)
    }

    func setup() {
        
        webViewDelegate.setupWebViewDidStartLoad { (webView) -> () in
            print(__FUNCTION__)
        }
        
        webViewDelegate.setupWebViewDidFinishLoad { (webview) -> () in
            print(__FUNCTION__)            
        }
        
        webViewDelegate.setupWebViewDidFailLoadWithError { (WebView, error) -> () in
            print(error)
        }
        webView.delegate = webViewDelegate
    }
}

ローカルHTMLファイルを読むには

    webView.openLocalFile("index", ofType: "html")

HTMLを直接書くには、

    func createHTML() -> String {
        
        let sMessage  = "Hello"
        let sMeta     = "<meta name=\"viewport\" content=\"initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no\">";
        let sStyle    = "<style>html,body{background-color:#eaeae9;}p{font-size:14px;font-weight:bold;width:80%;marg

in:50px auto;}</style>";
        let sHtmlHead = "<head>" + sMeta + sStyle + "</head>";
        let sHtmlBody = "<body><p>" + sMessage + "</p></body>";
        return  "<html>" + sHtmlHead + sHtmlBody + "</html>";
    }

    webView.loadHTMLString(createHTML(), baseURL: nil)

コードスニペット (UIButton編)ボタン押下時のアニメーション

ボタン押下時のアニメーションをつけるコードスニペット

UIButtonのExtensionにアニメーションを追加してみました。

import UIKit

extension UIButton{
    
    /**
     * ボタン押下時のアニメーション
     */
    func pushAnimation() {
        
        let defaultButton = self.bounds
        
        UIView.animateWithDuration(1.5,
            delay: 0.0,
            usingSpringWithDamping: 0.2,
            initialSpringVelocity: 10,
            options: UIViewAnimationOptions.CurveEaseInOut,
            animations: { () -> Void in
                
                self.bounds = CGRectMake(
                    defaultButton.origin.x,
                    defaultButton.origin.x,
                    defaultButton.size.width + 40,
                    defaultButton.size.height)
                
            }) { (finished) -> Void in
                
                self.bounds = CGRectMake(
                    defaultButton.origin.x,
                    defaultButton.origin.x,
                    defaultButton.size.width,
                    defaultButton.size.height)
        }
    }
}

使い方

    @IBAction func tappedButton(sender: UIButton) {
        sender.pushAnimation()
    }

コードスニペット (UIView編)プロパティへのショートカット

UIViewのプロパティへのショートカット用のコードスニペット

extension化しました。

extension UIView {
    
    /**
     上の位置を取得
     
     :returns: 上の位置
     */
    func top() -> CGFloat {
        return self.frame.origin.y
    }
    
    /**
     右の位置を取得
     
     :returns: 右の位置
     */
    func right() -> CGFloat {
        return self.frame.origin.x + self.frame.size.width
    }
    
    /**
     下の位置を取得
     
     :returns: 下の位置
     */
    func bottom() -> CGFloat {
        return self.frame.origin.y + self.frame.size.height
    }
    
    /**
     左の位置の取得
     
     :returns: 左の位置
     */
    func left() -> CGFloat {
        return self.frame.origin.x
    }
    
    /**
     UIViewの幅を取得
     
     :returns: UIViewの幅
     */
    func width() -> CGFloat {
        return self.frame.size.width
    }
    
    /**
     UIViewの高さを取得
     
     :returns: UIViewの高さ
     */
    func height() -> CGFloat {
        return self.frame.size.height
    }
    
    /**
     UIViewのoriginを取得
     
     :returns: UIViewのorigin
     */
    func origin() -> CGPoint {
        return self.frame.origin
    }
    
    /**
     UIViewのサイズを取得
     
     :returns: UIViewのサイズ
     */
    func size() -> CGSize {
        return self.frame.size
    }
}

使い方

        print("w : \(self.view.width()) - h : \(self.view.height())")

コードスニペット(Carthageインストール、設定編)

Carthageインストールと設定のコードスニペット

ゴール

Alamofire(通信用ライブラリ)を利用できるようにする。

手順

  1. Carthageのダウンロードとインストール
  2. 外部ライブラリの導入
  3. プロジェクトに外部ライブラリを追加

1.Carthageのダウンロードとインストール

以下、ターミナルからコマンドを入力する。 インストール済みの方は、飛ばしてください。

1.1. Homebrewの導入

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
brew update

1.2. Carthageの導入

brew install carthage

2.外部ライブラリの導入

以下、ターミナルからコマンドを入力する。

2.1. ライブラリを導入したいプロジェクトのディレクトリへ移動

2.2. Cartfileを作成

vim Cartfile

2.3. 外部ライブラリの追加 今回は、Alamofireというライブラリを追加する

github "Alamofire/Alamofire"

2.4. 外部ライブラリをビルド

carthage update --platform iOS

上記まで進むと、Buildディレクトリ配下にフレームワークが作られる。 /Carthage/Build/iOS/Alamofire.framework

f:id:onthewaves:20151206152515p:plain

3.プロジェクトに外部ライブラリを追加

3.1. Buil Phasesタブから、「Linked Frameworks and Library」にある+ボタンを押す

f:id:onthewaves:20151206152821p:plain

3.2. 「Add Other...」を選択する

f:id:onthewaves:20151206152834p:plain

3.3. プロジェクトのディレクトリ内から、Carthage/Build/iOSと移動し、ライブラリの.frameworkファイルを選択する

f:id:onthewaves:20151206152856p:plain

3.4. Build Phasesタブに移動し、+ボタンから、「New Run Script Phase」を選択する

f:id:onthewaves:20151206152921p:plain

3.5. Shell」の下にある黒い部分に以下のコマンドを記述します。

/usr/local/bin/carthage copy-frameworks

f:id:onthewaves:20151206152939p:plain

3.6. 「input Files」にて+ボタンを押し、以下のようにframeworkの情報を記述する

$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework 

f:id:onthewaves:20151206153019p:plain

さいごに

あとは、「import Alamofire」すれば利用可能です。

コードスニペット (UIColor編)

テーマ

カラーの指定は、デフォルトで用意されていもの以外は、使いづらい ということで、extensionを作りました。

UIColorのコードスニペット

extension UIColor {
    
    //色をRGBで指定する
    class func rgb(rgbValue: UInt) -> UIColor {
        return UIColor(
            red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
            green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0,
            blue: CGFloat(rgbValue & 0x0000FF) / 255.0,
            alpha: CGFloat(1.0)
        )
    }
    
    //画像を繰り返し表示する
    class func patternImage(fileName: String) -> UIColor {
        return UIColor(patternImage: UIImage(named: fileName)!)
    }
}

ついでに、画像の色を変える方法も載せておきます。

画像を色ごとにいちいち用意するのが面倒なときは、画像の色ごと変えちゃいましょう。

    //画像の色を変更する
    func changeColorImage() {
        
        imageView.image = UIImage(named: "alarm.png")?.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
        imageView.tintColor = UIColor.redColor()
    }

おすすめ本のご紹介

Amazon.co.jp: 詳細! Swift 2 iPhoneアプリ開発 入門ノート Swift 2 + Xcode 7対応: 大重 美幸: 本

コードスニペット(画像をピンチイン・ピンチアウト、回転、移動したい)

テーマ

UIImageViewをピンチイン・ピンチアウトしたり、回転したり、移動できるようにカスタマイズします。

実装手順

  1. UIImageViewの拡張クラスを作る
  2. UIImageViewに1で作成したクラスを指定する

1. UIImageViewの拡張クラスを作る

import UIKit

class FlexibleImageView: UIImageView,UIGestureRecognizerDelegate {
    
    var scale: Float = 0.0
    var rotation: Float = 0.0
    var isChange = false
    var isReset = false
    var defaultTransform: CGAffineTransform?
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        self.setup()
    }
    
    func setup() {
        self.userInteractionEnabled = true
        
        let rotation = UIRotationGestureRecognizer(target: self, action: "doAnimation:")
        rotation.delegate = self
        self.addGestureRecognizer(rotation)
        
        let pinch = UIPinchGestureRecognizer(target: self, action: "doAnimation:")
        pinch.delegate = self
        self.addGestureRecognizer(pinch)
        
        self.isChange = false
        self.scale = 1.0
        self.rotation = 0.0
        
        let pan = UIPanGestureRecognizer(target: self, action: "doPanAciton:")
        self.addGestureRecognizer(pan)
    }
    
    //MARK: - Action
    func doPanAciton(sender: UIPanGestureRecognizer) {
        
        let translation = sender.translationInView(self.superview!)
        let movedPoint = CGPointMake(self.center.x + translation.x, self.center.y + translation.y)
        self.center = movedPoint
        sender.setTranslation(CGPointZero, inView: self)
    }
    
    func doAnimation(sender: UIGestureRecognizer) {
        
        if !isChange && sender.state == UIGestureRecognizerState.Began {
            
            isChange = true
            defaultTransform = self.transform
            
        } else if isChange && sender.state == UIGestureRecognizerState.Ended {
            reset()
            return
        }
        
        if sender.state == UIGestureRecognizerState.Ended {
            return
        }
        
        if sender.isKindOfClass(UIRotationGestureRecognizer) {
            
            self.rotation = Float((sender as! UIRotationGestureRecognizer).rotation)
        } else {
            self.scale = Float((sender as! UIPinchGestureRecognizer).scale)
        }
        
        var transform = CGAffineTransformConcat(
            CGAffineTransformConcat(self.defaultTransform!, CGAffineTransformMakeRotation(CGFloat(self.rotation))),
            CGAffineTransformMakeScale(CGFloat(self.scale), CGFloat(self.scale)))
        
        self.transform = transform
    }
    
    //MARK: - Private
    func resetAnimation() {
        
        if isReset == true {
            
            UIView.animateWithDuration(0.2, animations: { () -> Void in
                self.transform = self.defaultTransform!
            })
        }
    }
    
    func reset() {
        
        self.isChange = false
        self.scale = 1.0
        self.rotation = 0.0
        NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "resetAnimation", userInfo: nil, repeats: false)
    }
    
    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

2. UIImageViewに1.で作成したクラスを指定する

StoryBoardで利用する場合は、Custom ClassのClassに「FlexibleImageView」を指定してください。 あとは、何もしなくても、ピンチイン・ピンチアウト、回転、移動できます。

おすすめ本のご紹介

エントリーにも良いと思います。

www.amazon.co.jp