Entertainer Engineering

人を楽しませられる技術者を目指すあおかびんのブログ

Swiftでカメラロールから写真データを保存する話

おはようございます。あおかび (ん)です。

この間やっとアプリケーション作りがひと段落したので、
その間に学んだことをずらずらっと書いていこうと思っております。

今回はSwiftを使ってカメラロールから写真データを引っ張ってくる方法を書いていきたいと思います。

ボタンを一つ置いて、そのボタンがクリックされたらカメラロールにアクセス、
その画像をローカルデータとして保存しておくところまでやっておきたいと思います。
DBの部分は今回は割愛します。


1.ボタンを配置して、カメラロールにアクセス
ViewController.swiftにまず、AssetsLibraryをimportした後、
UIViewControllerに加えてUIImagePickerControllerDelegateとUINavigationControllerDelegateを継承(?)しましょう。

/* ViewController.swift */
import UIKit
import AssetsLibrary

class testViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

つづいてviewDidLoadメソッドにてボタンを配置、アクションは今回はaccessCamerarollとしました。

    override func viewDidLoad() {
        self.view.backgroundColor = UIColor.whiteColor()
        super.viewDidLoad()
        
        let button: UIButton = UIButton()
        button.frame = CGRectMake(100, 100, 100, 100)
        button.setTitle("Access!", forState: UIControlState.Normal)
        button.backgroundColor = UIColor.blueColor()
        button.addTarget(self, action: Selector("accessCameraroll:"), forControlEvents: UIControlEvents.TouchUpInside)
        self.view.addSubview(button)
    }

そのaccessCamerarollメソッドは次のようになっています。

    // カメラロールから写真を選ぶためのメソッド
    func accessCameraroll(button: UIButton) {
        if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary) {
            let controller = UIImagePickerController()
            controller.delegate = self
            controller.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
            self.presentViewController(controller, animated: true, completion: nil)
        }
    }

ここまででとりあえずカメラロールにアクセスすることはできました!
次はカメラロールから選択した写真を保存しましょう。

2.選択した写真をローカルストレージに保存
写真を選択した時に呼ばれるメソッド

    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
    }

というメソッドがあります。この関数内で、選択した画像を保存していきましょう。

    // 写真を選択した時に呼ばれるメソッド
    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
        if info[UIImagePickerControllerOriginalImage] != nil {
            let image: UIImage? = info[UIImagePickerControllerOriginalImage] as! UIImage?
            let pictURL: NSURL = info[UIImagePickerControllerReferenceURL] as! NSURL
            var library: ALAssetsLibrary = ALAssetsLibrary()
            var data: NSData?
            
            let sema: dispatch_semaphore_t = dispatch_semaphore_create(0)
            let queue: dispatch_queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
            
            var getImage: UIImage?
            
            dispatch_async(queue, {() -> Void in
                library.assetForURL(pictURL, resultBlock: {(asset: ALAsset!) -> Void in
                    let representation: ALAssetRepresentation? = asset.defaultRepresentation()
                    getImage = UIImage(CGImage: representation!.fullResolutionImage().takeUnretainedValue())!
                    data = UIImagePNGRepresentation(getImage!)
                    dispatch_semaphore_signal(sema)
                    }, failureBlock: {(error: NSError!) -> Void in
                        println(error)
                        dispatch_semaphore_signal(sema);
                })
            })
            dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
            
            saveImage(data!)
        }
        picker.dismissViewControllerAnimated(true, completion: nil)
        let mainViewController: MainViewController = MainViewController()
        self.navigationController?.pushViewController(mainViewController, animated: true)
    }

    // 画像をローカルストレージに保存するメソッド
    func saveImage(data: NSData) {
        let dataPath = (NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as! Array<String>).first!.stringByAppendingPathComponent("test.jpg")
        var fileManager: NSFileManager = NSFileManager()
        data.writeToFile(dataPath, atomically: true)
    }

これで画像が保存されたはずです。

最初はAVFoundationとか使ってカメラにアクセスしたりしてたんですが、なんかうまくいかなかったですね...

コーディング能力、プログラミング能力の両方がこのままではやばい気がするよなー
勉強頑張っていきます。ありがとうございました、かびでした。。。


P.S.

コードの全容

import UIKit
import AssetsLibrary

class testViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    override func viewDidLoad() {
        self.view.backgroundColor = UIColor.whiteColor()
        super.viewDidLoad()
        
        let button: UIButton = UIButton()
        button.frame = CGRectMake(100, 100, 100, 100)
        button.setTitle("Access!", forState: UIControlState.Normal)
        button.backgroundColor = UIColor.blueColor()
        button.addTarget(self, action: Selector("accessCameraroll:"), forControlEvents: UIControlEvents.TouchUpInside)
        self.view.addSubview(button)
        
    }
    
    // カメラロールから写真を選ぶためのメソッド
    func accessCameraroll(button: UIButton) {
        if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary) {
            let controller = UIImagePickerController()
            controller.delegate = self
            controller.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
            self.presentViewController(controller, animated: true, completion: nil)
        }
    }
    
    // 写真を選択した時に呼ばれるメソッド
    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
        if info[UIImagePickerControllerOriginalImage] != nil {
            let image: UIImage? = info[UIImagePickerControllerOriginalImage] as! UIImage?
            let pictURL: NSURL = info[UIImagePickerControllerReferenceURL] as! NSURL
            var library: ALAssetsLibrary = ALAssetsLibrary()
            var data: NSData?
            
            let sema: dispatch_semaphore_t = dispatch_semaphore_create(0)
            let queue: dispatch_queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
            
            var getImage: UIImage?
            
            dispatch_async(queue, {() -> Void in
                library.assetForURL(pictURL, resultBlock: {(asset: ALAsset!) -> Void in
                    let representation: ALAssetRepresentation? = asset.defaultRepresentation()
                    getImage = UIImage(CGImage: representation!.fullResolutionImage().takeUnretainedValue())!
                    data = UIImagePNGRepresentation(getImage!)
                    dispatch_semaphore_signal(sema)
                    }, failureBlock: {(error: NSError!) -> Void in
                        println(error)
                        dispatch_semaphore_signal(sema);
                })
            })
            dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
            
            saveImage(data!)
        }
        picker.dismissViewControllerAnimated(true, completion: nil)
        let mainViewController: MainViewController = MainViewController()
        self.navigationController?.pushViewController(mainViewController, animated: true)
    }
    
    // 画像をローカルストレージに保存するメソッド
    func saveImage(data: NSData) {
        let dataPath = (NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as! Array<String>).first!.stringByAppendingPathComponent("test.jpg")
        var fileManager: NSFileManager = NSFileManager()
        data.writeToFile(dataPath, atomically: true)
    }
    

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

Swiftで NavigationBarを出したり引っ込めたりする話

こんばんは、かびさん@あおかびんです。

21日間ブログを書くというチャレンジに失敗しまくっている次第ですが
また気をとりなおしていきたいと思います。

さて、NavigationBarで前の画面に戻れる機能は便利なのですが
場合によっては表示させたくないということがあるかと思います。

そこで出したり引っ込めたりする方法を書いていきたいと思います。

流れは

  1. 画面遷移はNavigationControllerに任せる
  2. 画面を描画する前にNavigationBarの設定をする

こんな感じになるかと思います。

実際に書いてみましょう。

1.AppDelegate.swift

class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // 最初に表示する画面を指定する
        let firstView: MainViewController = MainViewController()
        // NavigationControllerを設定する
        let myNavigationController: UINavigationController = UINavigationController(rootViewController: firstView)
        
        // おまじない
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        self.window?.rootViewController = myNavigationController
        self.window?.makeKeyAndVisible()
        return true
    }
    ・・・
}

最初に表示する画面からNavigationControllerを設定する感じですね。
次はMainViewControllerを設定します。

2. MainViewController.swift (NavigationBarを表示しない)

class MainViewController: UIViewController {
    …
    // ボタンが押された時の処理
    func touchButton(sender: UIButton){
        let subViewController: SubViewController = SubViewController()
        self.navigationController?.pushViewController(subViewController, animated: true)
    }
    // 画面をロードする前に呼ばれるメソッド
    override func viewWillAppear(animated: Bool) {
        // NavigationBarを隠す処理
        self.navigationController?.setNavigationBarHidden(true, animated: true)
    }
    …
}

これによって画面をロードする時にNavigationBarを表示しない設定をします。
次は、このボタンを押したあとでSubViewControllerの方ではNavigationBarを表示しましょう。

3. SubViewController.swift (NavigationBarを表示する)

class SubViewController: UIViewController {
    ・・・
    override func viewWillAppear(animated: Bool) {
        self.navigationController?.setNavigationBarHidden(false, animated: true)
    }
    ・・・
}

これを追加することによって、<BackのついたNavigationBarを表示できると思います。

こんな感じですかね。

ブログを更新するのを忘れちゃうのは嫌だよな。。。
やっぱ通知つけようかな。。。

以上、あおかびんでした。

SwiftのUIViewController間でデータを受け渡す話

おはようございます、かびさん@あおかびんです。

iPhoneアプリSwiftを使って開発しているのですが
UIViewController間でデータを渡したいときに
AppDelegate.swiftに変数を追加すると渡せるということだったので
その方法を記述したいと思います。

1.AppDelegate.swift

AppDelegate.swiftファイルの中に、値を保持する領域を宣言します。
具体的には、

class AppDelegate: UIResponder, UIApplicationDelegate {
    var message: String?
    ・・・
}

こんな感じですかね。

2.SendViewController.swif (受け渡しView)

値に書き込むためにはAppDelegateクラスの変数を用意して、
その変数のクラス変数(?)を経由して書き込む形になります。

class SendViewController: UIViewController {
    var delegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    ・・・

    // ボタンを押したときの処理
    func touchButton(sender: UIButton){
        // AppDelegateのmessageに押されたボタンのtagを代入
        self.delegate.message = String(sender.tag)
        // NavigationControllerを使ったページの遷移
        let receiveViewController: ReceiveViewController = ReceiveViewController()
        self.navigationController?.pushViewController(receiveViewController, animated: true)
    }
}

こんな感じで記述しました。
(今回NavigationControllerを使っているので、ちょっとややこしいかもしれない)

3.ReceiveViewController.swift (値の受け取りView)

class ReceiveViewController: UIViewController {
    var delegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    override func viewDidLoad() {
        super.viewDidLoad()
        println(self.delegate.message)

    }
    ・・・
}

2と同じように呼び出しを行って、値を吐き出しています。
多分うまくいくんじゃないかな

ダメだったら連絡ください、修正します。。。

これを知らなかったからViewのinitをoverrideしようと苦戦してた。。。
スキル無さすぎダメダメのダメ。

すみません、以上あおかびんでした。

学校の課題を解くためにHaskellを使った話

こんにちは、かびさん@あおかびんです。

学校の課題で、ちょっとした計算問題を解くときに
普通に解くのが面倒くさいなーと思って
前まではPythonの対話を使って計算をしていたのですが、
この間確率統計のときに
先生「100万の中から1万個取り出す組み合わせはやろうと思ってもできない」
という発言があり(実際にはマクローリン展開かなんか使えばできるみたいな解説のため)
「できそう」と思ってしまったが故にPythonで書こうとしたら案の定計算できない。

また、3の20乗を計算する問題が出て、
計算するのにわざわざコード書くのも面倒だなと。

そこでHaskellを導入してやりました。その手順を書きます。

1. Haskell PlatformのDLとインストール

Download Haskell

こちら、Haskell Platformの公式DLサイトよりHaskell PlatformをDLします。
ちなみに、brewを使わないのは
Haskell関係のパッケージ(?)をインストールするcabalが
brewだとうまく動いてくれないので、こちらのHaskell Platformを使います。

あとはDLしたpkgを開いてインストールしてください。

2.ターミナルでhaskellの対話シェルを起動する。

インストールが終わったら対話形式でhaskellを実行してみましょう。
コマンドライン

ghci

と入力すると、最初の激重initの後haskellインタラクティブシェルが起動します。

閉じるときは

:q

コマンドを入力すれば大丈夫。

(発展)3.100万から1万個を取り出す組み合わせ問題を解かせる。

適当なエディタを使って、test.hsに次のソースコードを書き込んで保存しましょう。

手元にファイルがなかったので、後で追記します。

(発展)4.分数の累乗とかを求めてみる。

haskell先輩は分数も扱うことができます。ありがたいですね!

ghciを起動したら、次のコードを入力してください。

import Data.Ratio

すると、%区切りで分数を扱うことができます。

1 % 3 * 1 % 4 -- 3分の1 × 4分の1
1 % 12 -- 12分の1

また、このまま累乗も計算できる。

((1 % 3) * (1 % 4)) ^ 20 -- (3分の1 × 4分の1)の20乗
1 % 3833759992447475122176 -- おおきなかず分の1

簡単な数式を入れるときはhaskellを使うようになりました。

すごく便利なghciでしたね。
以上、あおかびんでした。

Swiftで画面遷移の時に値を渡したい話

こんばんは、かびさん@あおかびんです。

今回はうまくいったわけではないので、今後の課題としてメモ書き。

qiita.com

こちらのサイトを参考に、ページ遷移の時に文字を送ろうとしているのですが
ソースコードを移植してもちょっとうまくいかないですね・・・。

何がダメなんだろう・・・?

SwiftのUIButtonで画面遷移をする話

こんばんは、かびんさん@あおかびんです。

今日はSwiftのUIButtonで画面遷移を行ったので
それをメモ書きします。

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.backgroundColor = backgroundColor
    
    for i in 0...2 {
        let task: UIButton = UIButton()
        let icast:CGFloat = CGFloat(i - 1)
        let action: Selector = Selector("touchButton" + String(i) + ":")
        task.backgroundColor = UIColor.whiteColor()
        task.layer.masksToBounds = true
        task.setTitle("task" + String(i), forState: UIControlState.Normal)
        task.setTitleColor(UIColor.blackColor(), forState: .Normal)
        task.addTarget(self, action: action, forControlEvents: .TouchUpInside)
        task.layer.masksToBounds = true
        task.frame = CGRectMake(width / 2 - fieldWidth / 2, height / 2 - fieldHeight + (40.0 * icast), fieldWidth, fieldHeight)
        self.view.addSubview(task)
    }
}

internal func touchButton0(sender: UIButton){
    // 遷移するViewを定義する.
    let editViewController: TaskEditViewController = TaskEditViewController(i: 0)
    editViewController.modalTransitionStyle = UIModalTransitionStyle.CoverVertical
    self.presentViewController(editViewController, animated: true, completion: nil)
}

タッチイベントを取得したらその名前のセレクタが実行されるという形式。

UITextfieldのデザインを調整したいんだよな・・・。

以上あおかびんでした。

Slackにてhubotで会話監視を導入した話

こんにちは、かびさん@あおかびんです。

今回はSlackすごく便利ですよね、よく使っています。
Slackのgeneralチャンネルを全体報告の連絡のみに絞るため
@channelが付いていない発言には小言を言うbotを作りました。

ソースコードはこんな感じ。

attention.coffee

module.exports = (robot) ->
    robot.hear /(.*)/i, (msg) ->
        if ///^@channel: ///i.test(msg.match[1])
            return
        else
            msg.send "<君ぃ・・・連絡事項は文頭に@channel: をつけないとダメじゃないか。それ以外の話は #random でやりたまえ。"

ちょっとウザみのあるbotに仕上げました。

ただ、先頭からしか@channelを検知しないのと、
ファイルのアップロードとかには対応できていないのが現状です。

誰かいい方法を教えてください。

以上、あおかびんでした。