Xcode7 – Swift2 UIImageViewのタッチイベントを検知する

Xcode 7.0.1
Swift 2.0

ボタンだと、下記のようにできます。

self.myButton.addTarget(self, action: "onClickMyButton:", forControlEvents: .TouchUpInside)

UIImageViewだと、addTargetは使えません。

参考:SwiftにてUIImageViewをTap検知後、詳細画面に遷移したい

上記を参考に、下記のように作成してみましたが、エラーはでませんが、検知しません。

let gesture = UITapGestureRecognizer(target:self, action: "onTouchImg:")
imgView.addGestureRecognizer(gesture)

func onTouchImg(recognizer: UITapGestureRecognizer){
    print("touched!")
}

touchesBeganを使って座標で計算しようかなと思いまして、下記をとりあえずつくりましたが、これも反応しません。

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    super.touchesBegan(touches, withEvent:event)
    print("123")
}

参考:UILABELやUIIMAGEVIEWが応答しない場合の対処法

userInteractionEnabledはUIViewクラスのプロパティで、デフォルトではNOらしい。これをtrueにしないと反応しないらしい。

imgView.userInteractionEnabled = true

上記を追加したが、反応しない。

どうも、UIScrollViewを使っていることが原因っぽい。UIScrollViewを外すと反応する。

参考:UILabel・UIImageViewでタッチイベントを拾う方法の例外

しかし 「UIScrollViewの上に乗っているUILabeやUIImageView」 の場合は、
タップしてもtouchesBeganメソッドやtouchesEndedメソッドが反応しない。
(UIScrollViewのtouchesBegan/touchesEndedメソッドの実装によるものと推測)

UIScrollView上にある、UIImageViewとかUILabelのタッチイベントを検知するには、UITapGestureRecognizerを使い、対象のviewのuserInteractionEnabledをtureにしたら反応した。

Xcode7 – Swift2 ウインドウズサイズ取得

Xcode 7.0.1
Swift 2.0

参考:SwiftでカスタマイズしたViewを追加する方法

下記で取得できる。

self.view.bounds.width
self.view.bounds.height

上記は、CGFloat型というやつらしい。計算したりするときは、下記のように型を合わせる必要がある。

let img_h:CGFloat = 500.0 * self.view.bounds.width / 750.0
let img_y:CGFloat = img_h * CGFloat(idx)
imgView.frame = CGRectMake(0, img_y, self.view.bounds.width, img_h);

Xcode Swift – URLから画像を表示する

Xcode 7.0.1
Swift 2.0

参考:UIImage

下記のようにやってみましたが、NSDataに関するエラーがでました。

let url = NSURL(string: img_url); //img_urlにhttp://hoge.com/img.jpg等のStringが入っている
var err: NSError?;
var imageData = NSData(contentsOfURL:url!,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &err)!;
var img = UIImage(data:imageData);

let imgView = UIImageView(image:img);
imgView.frame = CGRectMake(0, 0, 100, 50);
self.view.addSubview(imgView);

エラー内容

Cannot invoke initializer for type ‘NSData’ with an argument list of type ‘(contentsOfURL: NSURL, options: NSDataReadingOptions, error: inout NSError?)’

参考:[swift] CustomCellを利用したユーザ一覧画

Swift 2.0より、JavaではおなじみのTry-Catch構文が導入され、例外処理(エラー発生時処理)が処理されることになりました。それにともない、NSErrorを使うメソッド等の書式が変更を受けているようです。

参考:Swift 2.0 の try, catch ファーストインプレッション

let url = NSURL(string: img_url);
let imgData: NSData

do {
    imgData = try NSData(contentsOfURL:url!,options: NSDataReadingOptions.DataReadingMappedIfSafe)
    let img = UIImage(data:imgData);
    let imgView = UIImageView(image:img);
    imgView.frame = CGRectMake(0, 0, 100, 50);
    self.view.addSubview(imgView);
} catch {
    print("Error: can't create image.")
}

これでできた。

Xcode – HTTP接続できない

下記のようなエラーが出た。
App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app’s Info.plist file.

参考:iOS9でHTTP接続できない場合の対処法

iOS9より実装された、ATS(App Transport Security)という機能による制限のようです。
公式のドキュメントによると、今後はなるべくHTTPS接続に統一するよう求められています。

info.plist に下記の設定を追加することで、ATSによる制限を回避し、従来通りにHTTP接続を行うことができます。

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

これ設定したら接続できた。

Xcode Swift – SKShapeNodeと重力

SKShapeNode

SpriteKitを使ってるときに、四角形とか丸とかを作りたいときに使う。

//長方形
let rect = SKShapeNode(rectOfSize: CGSize(width: 50, height: 50))
rect.position = CGPoint(x: self.size.width * 0.5, y: self.size.height * 0.5)
rect.lineWidth = 5.0
rect.strokeColor = SKColor.blueColor()
rect.fillColor = SKColor.blueColor()
self.addChild(rect)

//円
let circle = SKShapeNode(circleOfRadius: 10)
circle.lineWidth = 0.0
circle.fillColor = SKColor.greenColor()
circle.position = CGPoint(x: self.size.width * 0.5, y:self.size.height - 20)
self.addChild(circle)

重力加えてみる

下記のようにphysicsWorld.gravityにCGVectorを設定する。

class GameScene : SKScene{
    
    override func didMoveToView(view: SKView) {
        //重力
        self.physicsWorld.gravity = CGVector(dx: 0.0, dy: -2.0)
        
        //長方形
        let rect = SKShapeNode(rectOfSize: CGSize(width: 50, height: 50))
        rect.position = CGPoint(x: self.size.width * 0.5, y: self.size.height * 0.5)
        rect.lineWidth = 5.0
        rect.strokeColor = SKColor.blueColor()
        rect.fillColor = SKColor.blueColor()
        
        let physicsBody = SKPhysicsBody(rectangleOfSize: rect.frame.size)
        physicsBody.dynamic = false //重力無効にする
        rect.physicsBody = physicsBody
        self.addChild(rect)
        
        //円
        let radius:CGFloat = 10
        let circle = SKShapeNode(circleOfRadius: radius)
        circle.lineWidth = 0.0
        circle.fillColor = SKColor.greenColor()
        circle.position = CGPoint(x: self.size.width * 0.5, y:self.size.height - 20)
        
        circle.physicsBody = SKPhysicsBody(circleOfRadius: radius) //重力有効にする
        
        self.addChild(circle)
    }
    
}

画像も追加してみる

画像はSKSpriteNodeというのを使ってつくれる。

class GameScene : SKScene{
    
    override func didMoveToView(view: SKView) {
        //重力
        self.physicsWorld.gravity = CGVector(dx: 0.0, dy: -2.0)
        
        //長方形
        let rect = SKShapeNode(rectOfSize: CGSize(width: 50, height: 50))
        rect.position = CGPoint(x: self.size.width * 0.5, y: self.size.height * 0.5)
        rect.lineWidth = 4.0
        rect.strokeColor = SKColor.blueColor()
        rect.fillColor = SKColor.blueColor()
        let physicsBody = SKPhysicsBody(rectangleOfSize: rect.frame.size)
        physicsBody.dynamic = false //重力無効にする
        rect.physicsBody = physicsBody
        self.addChild(rect)
        
        //円
        let radius:CGFloat = 10
        let circle = SKShapeNode(circleOfRadius: radius)
        circle.lineWidth = 0.0
        circle.fillColor = SKColor.greenColor()
        circle.position = CGPoint(x: self.size.width * 0.5 - 28, y:self.size.height - 20)
        circle.physicsBody = SKPhysicsBody(circleOfRadius: radius) //重力有効にする
         self.addChild(circle)
        
        //画像
        let texture = SKTexture(imageNamed: "rocket")
        let img = SKSpriteNode(texture: texture)
        img.position = CGPoint(x: self.size.width * 0.5, y:self.size.height - 100)
        img.physicsBody = SKPhysicsBody(texture: texture, size: img.size)
        self.addChild(img)
    }
    
}

Xcode Swift – AutoLayoutをコードで書く(Visual Format Language)

Visual Format Languageの使い方。Visual Format Languageは、VFLと略されているらしい。appleの説明ページは、ここっぽい。けどまだしっかり読んでいない。

VFLを使うときは、設定対象のviewで、setTranslatesAutoresizingMaskIntoConstraints(false)を設定する必要がある。また、VFLを使う前に、addSubviewをしておく必要がある。そして、設定対象のview達を下記のように配列に格納する。

let views = ["l1":label1, "b1":btn1, "img1": img1, "v1": vw1, "v2":vw2]

そして、下記のように使う。

self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[l1]-10-|", options: nil, metrics: nil, views: views))

optionsに何を入れたらどうなるのかはまだわかっていない。metricsは、使いたい長さとかを事前に計算しておき、metricsに渡すと、設定の際に使えるようになる。

VFLサンプル

override func viewDidLoad() {
    super.viewDidLoad()
    
    self.view.backgroundColor = UIColor.blueColor()
    
    //背景画像配置
    let img1 = UIImageView()
    img1.image = UIImage(named: "IMG_5045.JPG")
    img1.setTranslatesAutoresizingMaskIntoConstraints(false)
    img1.contentMode = UIViewContentMode.ScaleAspectFill
    self.view.addSubview(img1)
    
    //Label作成
    let label1: UILabel = UILabel()
    label1.setTranslatesAutoresizingMaskIntoConstraints(false)
    label1.text = "Logicky"
    label1.backgroundColor = UIColor.whiteColor()
    label1.textAlignment = NSTextAlignment.Center
    label1.font = UIFont(name: "HiraKakuProN-W3", size: 30)
    self.view.addSubview(label1)
    
    //Button作成
    let btn1 = UIButton()
    btn1.setTranslatesAutoresizingMaskIntoConstraints(false)
    btn1.setTitle("ボタン", forState: .Normal)
    btn1.backgroundColor = UIColor.orangeColor()
    btn1.addTarget(self, action: "onClickMyButton:", forControlEvents: .TouchUpInside)
    self.view.addSubview(btn1);
    
    //viewの作成
    let vw1 = UIView()
    let vw2 = UIView()
    vw1.setTranslatesAutoresizingMaskIntoConstraints(false)
    vw2.setTranslatesAutoresizingMaskIntoConstraints(false)
    vw1.backgroundColor = UIColor.redColor()
    vw2.backgroundColor = UIColor.greenColor()
    self.view.addSubview(vw1);
    self.view.addSubview(vw2);
    
    let views = ["l1":label1, "b1":btn1, "img1": img1, "v1": vw1, "v2":vw2]
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[l1]-10-|", options: nil, metrics: nil, views: views))
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-50-[l1]-50-[b1]-30-[v1(100)]", options: nil, metrics: nil, views: views))
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[b1]-30-[v2(100)]", options: nil, metrics: nil, views: views))
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[v1]-0-[v2(==v1)]|", options: nil, metrics: nil, views: views))
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-50-[b1]-50-|", options: nil, metrics: nil, views: views))
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[img1]|", options: nil, metrics: nil, views: views))
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[img1]|", options: nil, metrics: nil, views: views))
}

結果

スクリーンショット 2015-08-16 11.01.15

スクリーンショット 2015-08-16 11.01.28

参考:
Introduction to the Visual Format Language
http://swift-salaryman.com/autolayout.php
http://www.slideshare.net/classmethod/i-os-auto-layout
SwiftでAuto Layoutがめっちゃ楽に書けるライブラリ「Crew」

Xcode Swift – 動かそうとしたらyou don’t have permission to view itというエラーがでる

cmd+rで動かそうとしたら、you don’t have permission to view itというエラーがでる。

ここに同じエラーの人いるけど、chmodで権限を777にしてもエラーがでる。

“The file “MyApp.app” couldn’t be opened because you don’t have permission to view it” when running appこのサイトによると、キャッシュ的な問題らしい。このサイトのリンク先に下記が書いてあってやってみたら、解決した。

I’ve fixed it by cleaning a build folder. Just went to ‘Product’ menu and Option+Click ‘Clean’. After that a problem was resolved.

Xcode Swift – モーダル

モーダルとは、ポップアップ的だけど全画面ででてくるようなやつのことをいうと考えております。

internal func show_modal(sender: UIButton){
    let nextViewController: UIViewController = SecondViewController()
    nextViewController.modalTransitionStyle = UIModalTransitionStyle.FlipHorizontal
    self.presentViewController(nextViewController, animated: true, completion: nil)
}

こんな感じで表示させます。

modalのアニメーションには、色々な種類があってかっこいいです。
アニメーション設定は、上記の下記でやります。

nextViewController.modalTransitionStyle = UIModalTransitionStyle.FlipHorizontal

FlipHorizontal以外には、下記のようなものがあります。これで全部ではないでしょうか?

UIModalTransitionStyle.FlipHorizontal //ひっくり返る
UIModalTransitionStyle.CoverVertical //下から上がってくる
UIModalTransitionStyle.CrossDissolve //浮かび上がる
UIModalTransitionStyle.PartialCurl //本がめくれるみたいになる

Xcode Swift – イベント

ボタン押したよイベント

viewDidLoadメソッドは、viewがロードされたときに呼ばれるメソッド。ここでイベントの設定できる。
btn1をTouchUpしたら押されたよとコンソールに表示するには下記のようにする。

override func viewDidLoad() {
    super.viewDidLoad()
    btn1.addTarget(self, action: "btn1TouchUp:", forControlEvents: .TouchUpInside)
}

func btn1TouchUp(sender: UIButton){
    println("押されたよ")
}

下記のようにremoveTargetで、イベントとの関連を消せる

sender.removeTarget(self, action: "btn1TouchUp:", forControlEvents: .TouchUpInside)

ジェスチャーしたよイベント

UIGestureRecognizerというのを使うと、ジェスチャーを検知できる。

スワイプ

override func viewDidLoad() {
    super.viewDidLoad()
    
    let swipeRightRecognizer = UISwipeGestureRecognizer(target: self, action: "swipeRightHandler:")
    swipeRightRecognizer.direction = .Right
    self.view.addGestureRecognizer(swipeRightRecognizer)
}

func swipeRightHandler(ender: UISwipeGestureRecognizer){
    println("right swipe")
}

回転

override func viewDidLoad() {
    super.viewDidLoad()
    
    let rotateRecognizer = UIRotationGestureRecognizer(target: self, action: "rotateHandler:")
    self.view.addGestureRecognizer(rotateRecognizer)
}

func rotateHandler(sender: UIRotationGestureRecognizer){
    println("回転中 \(sender.rotation)")
}

タップとダブルタップ
ダブルタップをすると最初にタップを検知し、次にダブルタップを検知する。ダブルタップだった場合タップを検知したくない場合は、requireGestureRecognizerToFailというのを使う。

override func viewDidLoad() {
    super.viewDidLoad()
    
    let tapRecognizer = UITapGestureRecognizer(target: self, action: "tapHandler:")
    self.view.addGestureRecognizer(tapRecognizer)
    
    let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: "doubleTapHandler:")
    doubleTapRecognizer.numberOfTapsRequired = 2
    self.view.addGestureRecognizer(doubleTapRecognizer)
    
    tapRecognizer.requireGestureRecognizerToFail(doubleTapRecognizer)
}

func tapHandler(sender: UITapGestureRecognizer){
    println("Tap!")
}

func doubleTapHandler(sender: UITapGestureRecognizer){
    println("DoubleTap!")
}

画面のどこをタップしたのか知りたい

イベントを送ってきたやつはsenderというのですが、sender.locationInView(self.view)とやると、view内の位置を取得できる。

func tapHandler(sender: UITapGestureRecognizer){
    let location = sender.locationInView(self.view)
    println("Tap! Location => (\(location.x), \(location.y))")
}

Xcode Swift – AutoLayout

Alignアイコンをクリックすると、こういうのが出ます。cssのtext-align: center;と同じで、左揃え・中央揃え等の設定を行うことができます。これによって、いろいろな画面サイズに対応できます。

Alignment

それぞれの意味は下記になります。

複数UI部品間の設定

Leading Edges: 文章の読み始め側の端を揃える
Trailing Edges: 文章の終わり側の端を揃える
Top Edges: 上端を揃える
Bottom Edges: 下端を揃える
Horizontal Centers: 水平方向の中央揃え
Vertical Centers: 垂直方向の中央揃え
Baselines: ベースラインを揃える

特定部品と親View間の設定

Horizontal Center in Container: 親Viewの水平方向中央に表示
Vertical Center in Container: 親Vierの垂直方向中央に表示

最背面にしたい

storyboardのEditor->Arrangeを利用すればできる。
「send to Back」が、最背面、「send Backward」が1つ
下。

画像の配置を調整したい

Image Viewに画像を設定すると表示されますが、空白埋めたいとか、画像の比率は変えたくないとかいう問題が発生します。
Modeで設定します。「Scale to Fill」はよくわかりません。「Aspect Fit」は画像の比率を変えずにViewに収まる最大のサイズで表示します。「Aspect Fill」は、画像の比率を変えずに、確実にViewが埋まるように表示します。よって、画像の一部が非表示になる可能性があります。

上記のAspect Fillをコードで書く

myImageView = UIImageView(frame: CGRectMake(0,0, self.view.bounds.width, self.view.bounds.height))
let myImage = UIImage(named: "HOGE.JPG")
myImageView.image = myImage
myImageView.layer.position = CGPoint(x: self.view.bounds.width/2, y: self.view.bounds.height/2)
myImageView.contentMode = UIViewContentMode.ScaleAspectFill
self.view.addSubview(myImageView)

UIImageViewのcontentModeに、UIViewContentMode.ScaleAspectFillとかを設定する。
(参考:UIImageViewのサイズを自動的に画像に合わせる方法のSwift版

AutoLayoutをコードで書く

参考:
コードでAutolayout
http://www.slideshare.net/classmethod/i-os-auto-layout
SwiftでAuto Layoutがめっちゃ楽に書けるライブラリ「Crew」

Xcode Swift – AppDelegateに変数をもたせて共有する

AppDelegateクラスに下記のように変数を設定する

var hoge = "ほげ"

ViewControllerに下記のように書くとAppdelegateの変数にアクセスできる

var myAp = UIApplication.sharedApplication().delegate as! AppDelegate
println(myAp.hoge);

ちなみに、viewWillAppear(animated: Bool)というメソッドは、画面が表示されたときに呼ばれるやつらしい。
なので、下記のように書くと、画面が表示されたら、コンソールにほげと表示される。

override func viewWillAppear(animated: Bool){
    var myAp = UIApplication.sharedApplication().delegate as! AppDelegate
    println(myAp.hoge)
}

Xcode Swift – 2画面を行ったり来たりする

Main.storyboardでViewControllerを追加しても、ViewController.swiftと連携させないといけない。1画面に1つのViewControllerが必要。新しくViewController.swiftを作成して、Xcodeの右側にある設定画面でひもづける。最初の画面から2番目の画面の遷移を設定するのは、Segue接続で簡単にできる。2番目の画面から1番目の画面に戻る場合は、2番目画面のボタンをExitにひもづけるが、1番目のViewController.swiftに下記コードを入れておく必要がある。

@IBAction func returnMenu(segue: UIStoryboardSegue){
}

Exitにひもづけると、上記のreturnMenuが選択できるようになる。

2番目から1番目にSegue接続しない理由は、Segue接続だと新たに画面をつくるからで、行き来するたびに画面が量産される状態はよろしくないということらしい。

Segueには名前(identifier)をつけることができる。1番目から2番目に遷移するSegueに2ndSegueという名前をつけると、下記のようなコードで、1番目にあるボタンをクリックすると2番目に飛ぶようにできる。

@IBAction func tap2nd(sender: UIButton) {
    self.performSegueWithIdentifier("2ndSegue", sender:nil)
}