UIKit - tintColorについて調べた

本を読んでいたら、tintColorについて発見があったのでメモ。
単にUIの色を変えるという認識でした。

親のビューのtintColorを変更すると、子のビューのtintColorが変更される

let parentView = UIView()
let stepper = UIStepper()
stepper.tintColor = UIColor.yellowColor() // 黄色を設定
parentView.addSubview(stepper)

parentView.tintColor = UIColor.blueColor() // 子供すべてのtintColorが青色に変更される

さらに、tintColorにnilを代入すると、親のtintColorがあてがわれる。

let parentView = UIView()
parentView.tintColor = UIColor.redColor()

let stepper = UIStepper()
stepper.tintColor = UIColor.greenColor()

stepper.tintColor = nil //親の赤色がtintColorに設定される

tintColorを変更すると、変更したビューではtintColorDidChangeがコールされる

さらにtintColorを変更するとコールされるメソッドもあった。

let parentView = MyView()
parentView.tintColor = UIColor.redColor()

class MyView: UIView {
    override func tintColorDidChange() {
        print(tintColorを赤色に変更した)
    }
}

まだまだ知らないことがたくさんありますね〜。

絶賛ブランクを埋めるために、以下の本を読んでUIKitについて学び直してます。
小手先のテクニックではなくて、UIKitについてしっかり書かれているので学びがとても多いです。

CoreGraphics - CGRect, CGPointが短形内にあるかどうかを調べる

短形A、ポイントAが短形Bの範囲内にあるかどうか、
調べるには以下のメソッドを使うようだ。

// 短形を調べる場合
CGRectContainsRect(rect1: CGRect, _ rect2: CGRect) -> Bool

// ポイントを調べる場合
CGRectContainsPoint(rect: CGRect, _ point: CGPoint) -> Bool

知らなんだ。

Swift - 文字列の計算式を計算する

"3+5/2"のような文字列の計算式の答えを求めるには、以下のように書きます。

let expression = NSExpression(format: $計算式文字列$)
let result = expression.expressionValueWithObject(nil, context: nil) as? NSNumber

resultには、正確な計算式であれば答えとなるNSNumberが、計算式でなければnilが返ってきます。
Javascriptのevalのようなものですね。

"5/3"が計算式だとすると数字は全てIntなので、小数点の答えは出ません。
小数点まで求めるのであれば、計算式内の数字を正規表現かなにかで、
”X.0”としてやればDouble型になるので、小数点の答えも出るのではないかと思います。

参考:

spin.atomicobject.com

UIKit - TableViewCellの高さについて

しばしブランクが合ったので、UITableViewCellの高さの設定が以前とちがっていました。
忘れないようにメモる。

UITableViewCellの高さが一定ならば、UITableViewのrowHeightプロパティに入れるだけで良い。

let tableView = UITableView()
tableView.rowHeight = 80 // 高さが一定値

UITableViewDelegateのheightForRowAtIndexPathに入れると、
セルの数だけ呼び出されるので無駄が多いとのこと。

qiita.com

UIKit - オリジナル画面遷移を作ってみる(1)

とっても今更だが、オリジナルの画面遷移ができる方法をしっておきたかったので、
テストがてら簡単なものをつくって自分なりにまとめる。

今回はモーダルで開くときの画面遷移をオリジナルにしたが、
同様のやり方でUINavigationControllerのPush/PopやUITabControllerの切り替えにも対応できる。

必要なもの

・MainViewController(遷移元のViewController)
・ModalViewController(遷移先のViewController)
・AnimationController(遷移のアニメーション処理を受け持つクラス。UIViewControllerAnimatedTransitioningを実装)

遷移元と遷移先のViewControllerは当たり前として、
UIViewControllerAnimatedTransitioningプロトコルを実装したクラス一つ作り、 遷移元にUIViewControllerTransitioningDelegateを実測してやれば、
簡単にカスタム遷移を実現することができます。UIKit便利ですね。

アニメーションコントローラーの作成

遷移アニメーション処理を受け持つクラスを作成します。
ここでは、遷移元ビューコントローラーでpresentationViewControllerした時に、
登場時(presenting==true)のときには、右から新たなビューコントローラーが出てくるように、
退去時(presenting==false)のときには、右に表示ビューコントローラーが消えていくようにアニメーションさせています。

class SlideAnimationController: NSObject, UIViewControllerAnimatedTransitioning{
    
    let presenting: Bool
    
    init(presenting: Bool) {
        self.presenting = presenting
        super.init()
    }
    
    // アニメーションにかける時間
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return NSTimeInterval(0.6)
    }
    
    // アニメーション
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        
        guard let containerView = transitionContext.containerView(),
            fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
            toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
            else {
            return
        }
        
        let fromView = fromVC.view
        let toView = toVC.view
        
        let inframe = transitionContext.initialFrameForViewController(fromVC)
        let outframe = CGRectOffset(inframe, CGRectGetWidth(inframe), 0)
        
        if presenting {
            
            containerView.addSubview(toView)

            toView.frame = outframe
            
            UIView.animateWithDuration(self.transitionDuration(transitionContext), animations: { 
                toView.frame = inframe
                }, completion: { (finished) in
                    transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
            })
        }
        else {
            
            containerView.insertSubview(toView, belowSubview: fromView)
            
            toView.frame = inframe
            
            UIView.animateWithDuration(self.transitionDuration(transitionContext), animations: { 
                fromView.frame = outframe
                }, completion: { (finished) in
                    transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
            })
        }
    }
}

遷移元ビューコントローラーにUIViewControllerTransitioningDelegateを実装します

// MainViewController

func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return SlideAnimationController(presenting: true)
    }
    
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return SlideAnimationController(presenting: false)
}

あとは遷移するだけ

let modalVC = ModalViewController()
modalVC.transitioningDelegate = self
modalVC.modalPresentationStyle = .FullScreen // .FullScreen or .Customを設定する
presentViewController(modalVC, animated: true, completion: nil)

案外簡単にできたので、次はユーザー操作に合わせて画面遷移させてみたいと思います。 コードはこちら↓

github.com

UIKit - セルの選択状態を無色にしたい

忘れていたのでメモがわり。
selectionStyleを.Noneに設定すればよし。

MyCell.swift

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        self.selectionStyle = .None
}

Swift - 自作クラスをEquatableプロトコルに準拠させる

自作クラスである、以下の様なMemberクラスがあったとします。

class Member {
   let id: String
   let name: String

   init(_ id:String, _ name: String) {
     self.id = id
     self.name = name
   }

}

同一人物かどうかを判断するために、
以下のように「==」や「!=」での比較を行いたいと思いました。

let memberA = Member("01", "Taro")
let memberB = Member("01", "Masami")

if memberA == memberB {
    print("同じ人")
}
else {
    print("違う人")
}

等値演算子に利用するには、
自作のMemberクラスをEqutableプロトコルに準拠させなければなりません。
ということで、以下のように書くと上記の書き方が叶います。
こういうのを「演算子オーバーロード」と言うらしい。

func == (left : Member, right : Member) -> Bool {
   return left.id == right.id
}
extension Member: Equatable {}


class Member {
   let id: String
   let name: String

   init(_ id:String, _ name: String) {
     self.id = id
     self.name = name
   }
}

この例ではイマイチですが、リンク先のように日付先などを比べるにあたって、
コードが見づらくなる時などには良いかもしれませんね。

参考: http://dev.classmethod.jp/smartphone/strideablensdate/