開発メモ

開発関係のメモをいろいろと。たぶん。

Nifty Cloud Mobile Backendのファイルストア・基本編

Nifty Cloud Mobile Backendでいろいろやってみようシリーズ、第5弾(?)

今回はファイルストアを利用して、画像データのアップロード/ダウンロードの処理を試してみました。

f:id:see_ku:20160309201403p:plain

ソースコードを公開してあるので、よくわからない部分は直接見て下さい。

See_Ku / NcmbFilestoreDemo — Bitbucket
https://bitbucket.org/See_Ku/ncmbfilestoredemo

動作を確認した環境

環境 情報
Xcode 7.2.1 (7C1002)
iOS 9.2
Swift 2.1.1
Date 2016/3/9
NCMB SDK 2.3.0

画像データの保存(アップロード)

UIImageJPEGRepresentation()でNSDataにして、あとはNCMBFile::saveInBackgroundWithBlock()で保存しているだけです。

画像データのアップロードする場合、Nifty Cloud Mobile Backend側のファイルサイズ制限に引っ掛からないようにする点が問題になりそうですが、ここでは『UIImagePickerControllerの編集結果なら大丈夫』という楽観論で通してあります。

本番用のアプリでは、このような手抜きはやめましょう。

@IBAction func onUpload(sender: AnyObject) {

    // 画像ファイルをJPEG形式でデータ化
    guard let image = imageView.image else { return }
    guard let data = UIImageJPEGRepresentation(image, 0.95) else { return }

    // ※本来ならここでサイズのチェックが必要

    // NCMBFileを生成
    let fn = NSUUID().UUIDString + ".jpg"
    let file = NCMBFile.fileWithName(fn, data: data)

    // アップロード
    file.saveInBackgroundWithBlock() { error in
        if let error = error {
            print("File save error : ", error)
        } else {
            print("File save OK: \(fn)")
        }
    }
}

画像データの取得(ダウンロード)

ファイルの情報を取得

ファイルの情報を取得する所まではデータストアとほぼ同じです。使用するクラスがNCMBFile()になったぐらいでしょうか?

この後、実際のデータを取得する処理が入ります。

/// データを読み込み ※情報取得までは同期処理
func readDataSync() {
    let query = NCMBFile.query()
    query.limit = 20

    do {
        fileArray = try query.findObjects() as? [NCMBFile]
        fetchData()

        print("read ok: \(fileArray?.count)")
    } catch {
        fileArray = nil

        print("read error: \(error)")
    }
}

実際のデータを取得

NCMBFileのgetDataInBackgroundWithBlock()でデータを取得しているだけです。簡単そうに見えますよね?

/// データの取得処理を実行
func fetchData() {
    guard let ar = fileArray else { return }

    for file in ar {

        // 取得済みのデータはスキップ
        if imageDic[file.name] != nil {
            print("imageDic: \(file.name)")
            continue
        }

        // データを取得
        file.getDataInBackgroundWithBlock() { data, error in
            if let error = error {
                print("get error: \(error)")

            } else {
                let image = UIImage(data: data)
                self.imageDic[file.name] = image
                self.collectionView.reloadData()

                print("get file.name: \(file.name)")
            }
        }
    }
}

実は、この部分の処理にはいろいろと課題や問題点があります。

  • ファイルをローカルにキャッシュする仕組みが無い
    少なくとも、Nifty Cloud Mobile Backend iOS SDK v2.3.0には、そのような機能は無いようです。getDataInBackgroundWithBlock()を呼び出すと、その回数だけ、毎回毎回、サーバーに取りに行ってくれます。必要であれば自力でキャッシュする仕組みを作りましょう。

  • getDataInBackgroundWithBlock()は内部でシリアライズされない
    getDataInBackgroundWithBlock()を10回呼んだら10本、20回呼んだら20本、同時に処理が走ってしまいます。出来れば、ある程度呼び出しを制限する仕組みを自力で作った方が良いでしょう。

  • 同じファイルへの同時取得を考慮してくれない
    同じファイルにアクセスに行った場合、素直に、同時に取得処理が走ります。この部分も、自力でどうにかして無駄にアクセスしないような仕組みを作るべきでしょう。

・・・SDKを使っても、意外と面倒な事が多いですね。

その他

SDKのソースを読んでいて、少し気になった点です。

  • NCMBFile::isDirtyはサーバーでの変更に関知してない?
    単純に、サーバーに保存済みかどうかのフラグのようです。サーバー側でデータが更新されたかどうかのチェックには使えない模様です。

  • NCMBFile::isDataAvailableが機能してない?
    まったく、何の処理もしていないように思えるんですが・・・ 何なんでしょうね、これ。

最大容量/最大サイズを指定してUIImageをNSDataに変換する

せっかくUIImagePickerControllerで画像を選べるようになったので、ネット経由でいろいろしようかと思ったのですが、最近のiPhoneはカメラが良い事もあって、画像データがそこそこ大きかったりします。

そこで、データにした後の容量と長辺の最大サイズを制限した上で、NSDataにしてくれるような処理を書いてみました。

f:id:see_ku:20160306193032p:plain

ソースコードはこちら。(前回のデモの流用です)

See_Ku / ImagePickerDemo — Bitbucket
https://bitbucket.org/See_Ku/imagepickerdemo

ちなみに、UIImagePickerControllerの話はこちら。

UIImagePickerControllerを使ってみた - 開発メモ
http://seeku.hateblo.jp/entry/2016/02/29/210359

動作を確認した環境

環境 情報
Xcode 7.2.1 (7C1002)
iOS 9.2
Swift 2.1.1
Date 2016/3/6

ポイント解説

やってる事は単純です。

  1. 指定された範囲に収まるように画像をリサイズ
  2. JPEGの圧縮率を下げながら何度もデータ化

基本はこれだけです。簡単ですね。

指定された範囲に収まるように画像をリサイズ

画像のリサイズはこんな感じ。

/// イメージのサイズを変更
func resizeImage(src: UIImage) -> UIImage {

    // リサイズが必要か?
    let ss = src.size
    if maxLongSide == 0 || ( ss.width <= maxLongSide && ss.height <= maxLongSide ) {
        resizedSize = ss
        return src
    }

    // TODO: リサイズ回りの処理を切りだし

    // リサイズ後のサイズを計算
    let ax = ss.width / maxLongSide
    let ay = ss.height / maxLongSide
    let ar = ax > ay ? ax : ay
    let re = CGRect(x: 0, y: 0, width: ss.width / ar, height: ss.height / ar)

    // リサイズ
    UIGraphicsBeginImageContext(re.size)
    src.drawInRect(re)
    let dst = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    resizedSize = dst.size

    return dst
}

アスペクト比の計算から実際のリサイズまで、ほとんど、お約束の流れのままです。

JPEGの圧縮率を下げながら何度もデータ化

単純に、forでループを回してるだけです。

// JPEG形式でデータ化
for qualityUse = qualityMax; qualityUse >= qualityMin; qualityUse -= qualityDif {
    let data = UIImageJPEGRepresentation(resize, qualityUse)
    if checkDataSize(data) {
        return data
    }
}

こういう処理を書いてると、C言語風のforループもあって良い気がするんですけど・・・ 本当に無くすんでしょうかね?

SK4ShrinkRepresentationの使い方

実際にイメージをデータに変換する処理をまとめたのが、SK4ShrinkRepresentationクラスになります。最大容量/最大サイズを制限してデータにする場合、こんな感じになります。

// 単純に制限付きでデータにするだけならこんな感じ
let data = SK4ShrinkRepresentation.makeData(image, maxDataByte: 1024 * 1024 * 4, maxLongSide: 1024 * 2)

もっと面倒な使い方は・・・ たぶん、必要ないと思うんですけど、気になる方はソースコードを読んで下さい。

XcodeからgitとGitHubを使う方法・応用編

Xcodeで開発を行うときにgitやGitHubとあわせて使う方法の応用編です。

XcodeからgitとGitHubを使う方法・基本編 - 開発メモ
http://seeku.hateblo.jp/entry/2016/03/01/232409

今回の内容はこんな感じ。応用編のつもりでいたのですが、よく考えると基本編で基本的な操作の説明が抜けていたので、その部分もまとめてあったりしますorz

  • 最後にコミットした状態に戻す
  • 古いソースコードを参照する
  • 複数のバージョンを並行して開発する
  • 後からgitを使えるようにする
  • コミットと同時にGitHubにプッシュ
  • GitHub上の変更を取りこむ
  • GitHubでREADMEを追加
  • GitHubでLICENSEを追加

動作を確認した環境

環境 情報
Xcode 7.2.1 (7C1002)
iOS 9.2
Swift 2.1.1
Date 2016/3/2

gitに関する操作編

最後にコミットした状態に戻す

ソースコードにいろいろ手を入れたけどどうしても気に入らないとき、gitを使っていれば、最後にコミットした状態まですぐに戻す事が出来ます。

f:id:see_ku:20160302231401p:plain

メニューの [Source Control] - [Discard All Changes] を選択すると確認画面が出るので、よく確認して実行するだけです。簡単ですね。

古いソースコードを参照する

リポジトリに登録された古いソースコードと、現在のソースコードを見比べながら作業する事も出来ます。

f:id:see_ku:20160302231420p:plain

Xcodeの右上にある矢印が左右を向いてるっぽいアイコンを押すと、画面が分割されて古いバージョンを表示できるようになります。ちなみに、メニューから操作する場合は [View] - [Version Editor] - [Show Version Editor] で表示できます。

f:id:see_ku:20160302231446p:plain

Version Editorの下の部分をクリックする事で、過去の、どのソースコードを表示するか選ぶ事が出来ます。いざというときに助かります。

複数のバージョンを並行して開発する

例えば『Ver 1.0をリリースしたのでVer 2.0に取りかかりたいけど、Ver 1.0のバグ取りもしたい』みたいなときに、gitを使ってると簡単に平行して作業する事が出来ます。

ブランチとは?

ブランチというのは、平行して作業する時に作業をまとめて管理する単位です。ブランチを分ける事で、同じリポジトリでもそれぞれの作業を独立して行う事が出来ます。・・・言葉で説明するとわかりにくいですね。実際に使ってみれば、すぐに慣れると思います。

それぞれのバージョンに対応するブランチを作成

Xcodeでは、メニューの [Source Control] - [プロジェクト名 - 現在のブランチ名] - [New Branch] で新しいブランチを作る事が出来ます。

f:id:see_ku:20160302231507p:plain

特に切り替え作業を行ってない場合、masterブランチで作業が管理されます。複数のバージョンを並行して開発する場合、masterで最新の開発を行い、それ以外のバージョンは対応するブランチで作業するやり方が多い・・・ のかな? この辺りは、個人の好みが大きいような気がします。

ブランチを切り替える

開発を進めるバージョンを切り替える場合、対応するブランチを切り替える事になります。

Xcodeでは、メニューの [Source Control] - [プロジェクト名 - 現在のブランチ名] - [Switch Branch] でブランチを切り替える事が出来ます。

ブランチを切り替える際に、こんなエラーが出る事があります。

f:id:see_ku:20160302231556p:plain

ローカルのブランチから、リモートのブランチに切り替えようとするとこのエラーが出るようですが・・・ 詳細は不明です。とりあえず、ローカルのブランチだけ使っていれば問題ないと思われます。

後からgitを使えるようにする

Xcodeのプロジェクトを作成する時点で、gitのリポジトリも作っておくのがおすすめなのですが、もし、万が一、運悪くリポジトリの作成を忘れていた場合でも、後から簡単に対応する事が出来ます。

メニューの [Source Control] - [Create Working Copy] を押すと確認画面が出るので、リポジトリを作成しましょう。

GitHubに関する操作編

コミットと同時にGitHubにプッシュ

毎回毎回、コミットしてからプッシュする作業を繰り返してるような人は、コミット画面の左下にある『Push to remote』をチェックしておくと、幸せになれるかもしれません。

f:id:see_ku:20160302231623p:plain

GitHub上の変更を取りこむ

複数人で開発していると、自分のXcodeで作業しているソースコードより、GitHubリポジトリにあるソースコードの方が新しい状況になる事があります。

このような場合、メニューの [Source Control] - [Pull] で、GitHubリポジトリを取りこむ事が出来ます。

GitHubでREADMEを追加

READMEが登録されていないリポジトリを開くと、画面の右下の方に『Add a README』ボタンがあります。

f:id:see_ku:20160302231649p:plain

これを押すとREADMEの編集画面になるので、その場で編集&コミットする事が出来ます。便利ですね。

GitHubでLICENSEを追加

READMEの追加に比べると、LICENSEの追加はちょっと面倒です。

f:id:see_ku:20160302231704p:plain

GitHubリポジトリのページを開いて、ボタンの並んでるところにある『New file』をクリック。

f:id:see_ku:20160302231718p:plain

あとは、画像の通りに操作して、ライセンスを選択&コミットするだけです。ファイル名に『LICENSE』を入力するのがポイント?

XcodeからgitとGitHubを使う方法・基本編

Xcodeで開発を行うときに、gitやGitHubとあわせて使う方法について調べてみたのですが、いまいち、しっくりくるサイトが見つからなかったので自分でまとめてみました。

ここでは、以下のような情報についてまとめてあります。

あくまで基本編と言う事で、あまり難しい話はしません。
SSH鍵を登録する話やgitignoreの話は無し)

応用編はこちら。

XcodeからgitとGitHubを使う方法・応用編 - 開発メモ
http://seeku.hateblo.jp/entry/2016/03/02/232151

動作を確認した環境

環境 情報
Xcode 7.2.1 (7C1002)
iOS 9.2
Swift 2.1.1
Date 2016/2/25

基礎知識編

gitとは?

主に、プログラムのソースコードを管理するためのシステムです。gitをちゃんと使えば、古いソースコードを取り出したり、複数のバージョンを並行して開発する事が簡単になります。

ちなみに、『ギット』って呼ぶ人が多いですが『ジット』って呼ぶ人もいます。たまに。はい。

GitHubとは?

簡単に言うとgitをネット経由で使えるようにして、いろいろと便利な機能を付け加えたサービスです。ソースコードを公開するときに便利です。上手く使えば、複数人での開発が簡単になったりします。

リポジトリとは?

ソースコードを管理する単位です。プロジェクトごとに対応するリポジトリが作られると思っておけば、だいたい合ってます。gitでソースコードを管理する場合、同じようなリポジトリを複数の場所に分散しておく事が出来ます。

コミットとは?

ソースコードリポジトリに登録する操作の事です。コミットしてないと、gitは何の役にも立ちません。キリが良いと思ったら、どんどんコミットしましょう。

※とにかく、コミット大事

プッシュとは?

Xcodeで作ったリポジトリGitHubに送る事だと覚えておけば、だいたい合ってます。たぶん。

各種操作編

gitの初期設定

最低でも、名前とメールアドレスの設定はしておきましょう。こんな感じで設定する事が出来ます。

$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

ユーザー名やメールアドレスには、この後、GitHubで登録するのと同じものを指定しておくのが良いでしょう。後々の事を考えると、開発専用に新しいメールアドレスを用意するのがおすすめです。

gitの設定に関して、詳しい説明は以下のサイトを参考にしてください。

Git - Git の設定
https://git-scm.com/book/ja/v1/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%81%AE%E8%A8%AD%E5%AE%9A

GitHubのユーザー登録

以下のサイトでユーザー登録を行います。ユーザー名は慎重に。

GitHub · Where software is built
https://github.com/

プロジェクトと同時にリポジトリを作成

Xcodeでプロジェクトを作成する時点で、gitのリポジトリも作るように指定する事が出来ます。gitのリポジトリを作成する事によるデメリットはほぼ無いので、必ず作るようにしましょう。

f:id:see_ku:20160301231957p:plain

とにかくコミットが大事

開発を進めていくと、Project Navigatorのファイル名の横に、何かマークが現れる事があります。これは、リポジトリに登録されていたファイルに何らかの変更があった事を意味します。ファイルの編集だったり、追加だったり、削除だったりですね。

f:id:see_ku:20160301232013p:plain

マークが出ると言う事は開発が進んだと言う事なので(?)、キリが良いところでソースコードリポジトリに収めます。

メニューの[Source Control] - [Commit...]を選択すると、コミットに関する情報を入れる画面になります。

f:id:see_ku:20160301232032p:plain

最初のうちは、プロジェクトで使ってるファイルは、全てリポジトリに登録する方針で問題ないでしょう。たぶん。

コミットメッセージは、自分なりに何をどうしたのかを書けば良いです。特に思いつかなかったら適当に埋めても問題ありません。個人的なプロジェクトであれば。

一番大事なのはコミットする事です。

キリが良いなと思ったら、どんどんコミットしましょう。大事な事なので。

GitHubリポジトリを登録

ソースコードGitHubで公開する場合、まず、GitHubリポジトリを登録します。

GitHubの自分のページを開いて、右上の『+』をクリック。出てきたメニューから『New repository』を選択します。

f:id:see_ku:20160301232147p:plain

次の画面で新しいリポジトリの情報を入力します。Xcodeで作成したプロジェクトをGitHubに登録する場合、『Initialize this repository with a README』のチェックは外しておくのが良さそうです。
(READMEやLICENSEは後から追加する事も出来ます)

f:id:see_ku:20160301232211p:plain

GitHubリポジトリが出来たらURLをコピーしておきます。

f:id:see_ku:20160301232223p:plain

HTTPSの方です。

Xcodeでリモートリポジトリを登録

Xcodeで作成したリポジトリに、GitHubで作成したリポジトリを登録します。

f:id:see_ku:20160301232239p:plain

まず、メニューの [Source Control] - [プロジェクト名 - ブランチ名] - [Configure - プロジェクト名] で設定画面を出します。

f:id:see_ku:20160301232301p:plain

あとは、GitHubリポジトリを追加するだけ。

f:id:see_ku:20160301232315p:plain

Nameはoriginのままで、AddressにGitHubリポジトリを指定します。

GitHubにプッシュ

メニューの [Source Control] - [Push]でプッシュします。

GitHubのユーザー名とパスワードを聞かれるので、入力しましょう。

簡単ですね。

UIImagePickerControllerを使ってみた

iPhone / iPadで、単純に画像を一枚取得するだけであればUIImagePickerControllerを使うのが便利です。

そこでいろいろ調べたのですが、最近のSwiftで書かれた良い感じの例が見つからなかったので、自分で作ってみました。

ソースコードはこちら。
(このページで紹介しているコードのブランチはv1.0です)

See_Ku / ImagePickerDemo — Bitbucket
https://bitbucket.org/See_Ku/imagepickerdemo

動作を確認した環境

環境 情報
Xcode 7.2.1 (7C1002)
iOS 9.2
Swift 2.1.1
Date 2016/3/5

ポイント解説

1. 使える機能を確認

画像を選ぶ前に、そもそも、その機能が使えるのかを確認する必要があります。具体的にはこんな感じになります。

// カメラは使えるか?
if UIImagePickerController.isSourceTypeAvailable(.Camera) == false {
    cameraButton.enabled = false
}

この場合、カメラ機能が使えるかを調べて、使えないときは対応するボタンを無効にしています。今のところ、カメラ以外はどんな環境でも使えるような気がしますが、念のために他の機能を使うときもチェックするのが良いでしょう。たぶん。

2. メディアタイプを確認

本来ならそれぞれの機能でどんなメディアタイプが使えるかを調べないといけません。 (availableMediaTypesForSourceType(_:) を使います)

が、画像はどの機能でも確実に取れるようなので、ここではチェックを省いています。いけませんね。

3. UIImagePickerControllerを作成&画面遷移

UIImagePickerControllerを作成し、必要な情報を設定して画面を遷移させます。具体的にはこんな感じ。

/// Cameraから画像を取得
@IBAction func onCamera(sender: AnyObject) {

    // ImagePickerを作成
    let ipc = UIImagePickerController()
    ipc.delegate = self
    ipc.sourceType = .Camera
    ipc.allowsEditing = true

    // Cameraは全画面で選択
    presentViewController(ipc, animated: true, completion: nil)
}

ポイントとしては、 カメラの場合は全画面で、それ以外の時はモーダルフォーム で使用する必要があります。厳密に言うとカメラをモーダルフォームで使う事も出来るようですが、アップル推奨は全画面、と。そういう事のようです。

4. 画像の取得/キャンセルに対応

あとは、画像が正常に取得できたときとキャンセルされたときの処理を書くだけです。

/// 画像を取得できた
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {

    // 編集後のイメージを取り出し
    if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
        imageView.image = image
    }

    // ImagePickerを終了
    dismissViewControllerAnimated(true, completion: nil)
}

/// キャンセルされた
func imagePickerControllerDidCancel(picker: UIImagePickerController) {

    // ImagePickerを終了
    dismissViewControllerAnimated(true, completion: nil)
}

わざわざ、辞書からimageを取り出さないといけないのが、ちょっと面倒かもしれません。

情報元

UIImagePickerController Class Reference
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIImagePickerController_Class/

履歴

  • 2016/03/05 Deprecatedなメソッドを使っていたのを修正

枠線付きのボタン

UIButtonに枠線をつける方法はいろんなところで紹介されていますが、どれもいまいち満足できなかったので、自分で作ってみました。

f:id:see_ku:20160225222956p:plain

動作を確認した環境

環境 情報
Xcode 7.2.1 (7C1002)
iOS 9.2
Swift 2.1.1
Date 2016/2/25

できる事

以下のような機能が実装されています。

  • 押したときに枠線の色がちゃんと変わる
  • currentTitleColorから自動で色を設定
  • disableにした時の色も自動で設定
  • アラートが表示されたときの色変更にも対応
  • Storyboardから枠線の太さ/コーナー半径の変更に対応

特にすごい事はやってません。

ソースコード

こんな感じになりました。

import UIKit

/// 枠線付きのボタン
@IBDesignable
public class SK4BorderButton: UIButton {

    @IBInspectable var borderWidth: CGFloat = 1.0 {
        didSet {
            layer.borderWidth = borderWidth
            setNeedsDisplay()
        }
    }

    @IBInspectable var cornerRadius: CGFloat = 4.0 {
        didSet {
            layer.cornerRadius = cornerRadius
            setNeedsDisplay()
        }
    }

    override public var enabled: Bool {
        didSet {
            layer.borderColor = currentTitleColor.CGColor
        }
    }

    override public var highlighted: Bool {
        didSet {
            let col = currentTitleColor
            let key = "borderColor"

            if highlighted {
                layer.borderColor = col.colorWithAlphaComponent(0.2).CGColor
                layer.removeAnimationForKey(key)

            } else {
                layer.borderColor = col.CGColor
                let anim = CABasicAnimation(keyPath: key)
                anim.duration = 0.2
                anim.fromValue = col.colorWithAlphaComponent(0.2).CGColor
                anim.toValue = col.CGColor
                layer.addAnimation(anim, forKey: key)
            }
        }
    }

    override public init(frame: CGRect) {
        super.init(frame: frame)
        setupBorder()
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupBorder()
    }

    override public func tintColorDidChange() {
        super.tintColorDidChange()
        layer.borderColor = currentTitleColor.CGColor
    }

    public func setupBorder() {
        layer.borderWidth = borderWidth
        layer.cornerRadius = cornerRadius
        layer.borderColor = currentTitleColor.CGColor
        clipsToBounds = true
    }
}

UITableViewCellに置いたUIButtonがすぐに反応するようにする方法

かなりマニアックなtipsシリーズです。

f:id:see_ku:20160223201915p:plain

UITableViewCellにUIButtonを置いた場合、タッチしてすぐに指を離すとボタンがハイライト状態になりません。プログラム的に見ると、ちゃんと押されているのですが・・・ 使ってる側から見ると、まったく反応していないように見えて不親切です。

しばらく押してから離すとハイライト状態になりますが・・・ わかりにくいですよね? これをどうにかする方法のメモです。

動作を確認した環境

環境 情報
Xcode 7.2.1 (7C1002)
iOS 9.2
Swift 2.1.1
Date 2016/2/23

すぐに反応するようにする方法

すぐに反応してくれない原因はUIScrollViewのdelaysContentTouchesがtrueになっていることです。たぶん。

ですから、これをfalseにしてやればいいんですが、UITableViewは内部でUIScrollViewを使用しているため、一工夫必要になります。

具体的にはこんな感じ。

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // UITableViewのdelaysContentTouchesをfalseに
        tableView.delaysContentTouches = false

        // 中のUIScrollViewのdelaysContentTouchesをfalseに
        for vi in tableView.subviews {
            if let vi = vi as? UIScrollView {
                vi.delaysContentTouches = false
            }
        }
        
        // 実際にUITableViewを使う処理

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

簡単ですね。