hachinoBlog

hachinobuのエンジニアライフ

UITableViewCellが無い領域のUITableViewのSeparatorを消す方法

背景

TableViewCellが表示されている領域にはSeparatorあって良いんだけど、Cellが1個しかなくて後はUITableViewの領域で、その領域ではSeparatorを表示したくなかった

解決

TableViewのTableFooterViewに何か突っ込めばUITableView領域にSeparatorは出ない

tableView.tableFooterView = UIView(frame: CGRectZero)

SwiftTaskであるAPI処理の結果をもとにSuccessでTask.allを使う方法

背景

ある通信処理の成功結果をもとに複数通信をネストせずに書きたかった SwiftTaskを使うと簡単にできる

コード


private func generateTaskA() -> Task<Float, [String], NSError?> {

  return Task { (fulfill, reject) in
    //通信処理 成功時にはStringの配列が得られる
    fulfill(results)          
  }

}

private func generateTaskB(successA: String) -> Task<Float, Int, NSError?> {

  return Task<Float, DestinationPlaceProtocol, NSError?> { progress, fulfill, reject, configure in
      //通信処理 成功時にはInt型の値が得られる
      fulfill(result)
  }

}

func fetchData() {
  
  generateTaskA().success { [unowned self] results -> Task<(completedCount: Int, totalCount: Int), [Int], NSError?> in
    //TaskAの成功時 results(Stringの配列)をもとにTaskBを複数作成
    let taskListB = results.map(self.generateTaskB)
    return Task.all(taskListB)
  }.success { numberResults in
    //TaskBの配列のTaskが全て成功した場合にくる
   //TaskBの成功オブジェクトの配列なのでnumberResultsは[Int]
  }

}

1つ目のsuccessの返り値であるTaskはTaskBの配列の結果として何が得られるかを指定する(successの中で処理するTaskの結果)

Task.allの場合は

public class func all(tasks: [Task]) -> Task<BulkProgress, [Value], Error>

返り値が上記のようになるので合わせる (BulkProgressは(completedCount: Int, totalCount: Int)のエイリアス

Swiftのインスタンスから型メソッド(クラスメソッド)を呼び出す方法

背景

あるクラスのインスタンスから、そのクラスの型メソッド(クラスメソッド)を呼び出したいときにObjective-Cでは下記のようにやっていたけれどSwiftだとどう書くのか知りたかった

[[instance class] classMethod];

結論

dynamicTypeを使うことでインスタンスから型メソッド(クラスメソッド)へのアクセスが可能になる

class SampleClass() {

  //型メソッド(クラスメソッド)
  static func sampleClassMethod() {
    print("sampleClassMethod")
  }

}

//普通の型メソッド(クラスメソッド)の呼び出し方
SampleClass.sampleClassMethod()  //sampleClassMethod

//インスタンスからクラスメソッドへのアクセス
let sampleClass = SampleClass()
sampleClass.dynamicType.sampleClassMethod() //sampleClassMethod

dynamicTypeとはなんぞや?

dynamicTypeを使うとインスタンスのクラス自身(サブクラス化されていればサブクラス)が参照できるようです

let sampleClass = SampleClass()
print(sampleClass.dynamicType) //SampleClass

if sampleClass.dynamicType == SampleClass.self {
  print("true") //true
}

tableViewCell上にUISliderを載せてアニメーションしたらおかしくなる件

事象

カスタムのUITableViewCell上にUISliderをaddしてcellforRowで読み込まれると同時にSliderをアニメーションしてみたのだけれど、minimumTrackTintColorとmaximumTrackTintColor割合がおかしく、明らかに変なアニメーションをしだした。

解決方法

Sliderのアニメーションをdispatch_afterで遅らせてやるとうまくいった。

ちなみに[UIView animateKeyframesWithDuration...のdelay:をセットしてもダメだった。

下記サンプルではtag10にUISliderが紐付けられているものとする。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        UISlider *slider = (UISlider *)[cell viewWithTag:10];
        slider.value = 0;
        [UIView animateKeyframesWithDuration:1 delay:0 options:UIViewKeyframeAnimationOptionAllowUserInteraction animations:^{
            [slider setValue:0.8 animated:YES];
        } completion:^(BOOL finished) {
            
        }];
    });
    
    return cell;
}

うまくいかない時は遅延処理させると解決するのは結構あるけど、 delayじゃなくdispatch_after使わないとダメだったとは。。

かなりの時間を使ってしまったので久しぶりにメモ。

Android Studio0.8.14にしたらエラーしまくった

背景

Android Studioのverを0.8.14にしたらエラーになったので解決方法をメモ

解決方法

まず、アップデートしているとAndroid Studio配下にsdkフォルダを配置するなと怒られるので、sdkの場所をmvコマンドで適当な場所に移す。

sdkを移した後にリトライすれば0.8.14に上がる。

Android Studioを起動した際にsdkがないと言われるので、sdkを移動したパスを設定する。

その後に、新規プロジェクト作成したら今度はAndroid APIの最新である21を使うにはjdk1.7を使えというエラー。

OracleからJava SE Development Kit 7u71をダウンロードしてjdkをjdk1.7.0_71.jdk/Contents/Homeを使うように設定してようやく動いた。

Android Studioはまだbeta版だからいろいろと仕様が変わるのですね。

ありえん。。

追記 新規でAndroid Studio0.8.14をインストールする場合はここが大変参考になります。

NSLayoutConstraintを使ってAutoLayoutをコードで書く

背景

AutoLayoutをxib上で使っているのだけれどxibでは表現できるAutoLayoutに限界があると思いコードで書く方法を調べたのでメモ。

サンプル

1.self.viewから左20,上20のマージンをとった位置にwidth100,height100の赤いViewを配置

    UIView *redView = [[UIView alloc] init];
    redView.backgroundColor = [UIColor redColor];
    redView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:redView];
    NSLayoutConstraint *redLeftConstraint = [NSLayoutConstraint constraintWithItem:redView
                                                                         attribute:NSLayoutAttributeLeft
                                                                         relatedBy:NSLayoutRelationEqual
                                                                            toItem:self.view
                                                                         attribute:NSLayoutAttributeLeft
                                                                        multiplier:1
                                                                          constant:20];
    
    NSLayoutConstraint *redTopConstraint = [NSLayoutConstraint constraintWithItem:redView
                                                                     attribute:NSLayoutAttributeTop
                                                                     relatedBy:NSLayoutRelationEqual
                                                                        toItem:self.topLayoutGuide
                                                                     attribute:NSLayoutAttributeTop
                                                                    multiplier:1
                                                                      constant:20];
    
    NSLayoutConstraint *redWidthConstraint = [NSLayoutConstraint constraintWithItem:redView
                                                                       attribute:NSLayoutAttributeWidth
                                                                       relatedBy:NSLayoutRelationEqual
                                                                          toItem:nil
                                                                       attribute:NSLayoutAttributeNotAnAttribute
                                                                      multiplier:1
                                                                        constant:100];
    
    NSLayoutConstraint *redHeightConstraint = [NSLayoutConstraint constraintWithItem:redView
                                                                        attribute:NSLayoutAttributeHeight
                                                                        relatedBy:NSLayoutRelationEqual
                                                                           toItem:nil
                                                                        attribute:NSLayoutAttributeNotAnAttribute
                                                                       multiplier:1
                                                                         constant:100];
    
    [self.view addConstraints:@[redLeftConstraint, redTopConstraint, redWidthConstraint, redHeightConstraint]];

f:id:hachinobu:20141103191352p:plain

f:id:hachinobu:20141103191351p:plain

2.self.viewのbottom部分に幅=端末幅,高さ40の青いView配置

    UIView *blueView = [[UIView alloc] init];
    blueView.backgroundColor = [UIColor blueColor];
    blueView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:blueView];
    NSLayoutConstraint *blueLeftConstraint = [NSLayoutConstraint constraintWithItem:blueView
                                                                             attribute:NSLayoutAttributeLeft
                                                                             relatedBy:NSLayoutRelationEqual
                                                                                toItem:self.view
                                                                             attribute:NSLayoutAttributeLeft
                                                                            multiplier:1
                                                                              constant:0];
    NSLayoutConstraint *blueRightConstraint = [NSLayoutConstraint constraintWithItem:blueView
                                                                              attribute:NSLayoutAttributeRight
                                                                              relatedBy:NSLayoutRelationEqual
                                                                                 toItem:self.view
                                                                              attribute:NSLayoutAttributeRight
                                                                             multiplier:1
                                                                               constant:0];
    NSLayoutConstraint *blueHeightConstraint = [NSLayoutConstraint constraintWithItem:blueView
                                                                            attribute:NSLayoutAttributeHeight
                                                                            relatedBy:NSLayoutRelationEqual
                                                                               toItem:nil
                                                                            attribute:NSLayoutAttributeNotAnAttribute
                                                                           multiplier:1
                                                                             constant:40];
    NSLayoutConstraint *blueTopConstraint = [NSLayoutConstraint constraintWithItem:blueView
                                                                        attribute:NSLayoutAttributeTop
                                                                        relatedBy:NSLayoutRelationEqual
                                                                           toItem:self.view
                                                                        attribute:NSLayoutAttributeBottom
                                                                        multiplier:1
                                                                          constant:-blueHeightConstraint.constant];
    [self.view addConstraints:@[blueLeftConstraint, blueRightConstraint, blueTopConstraint, blueHeightConstraint]];

f:id:hachinobu:20141103202832p:plain

f:id:hachinobu:20141103202833p:plain

3.self.viewのcenter座標に幅=self.view.frame.width/2 高さ=self.view.frame.height/2 の黄色Viewを配置

    UIView *yellowView = [[UIView alloc] init];
    yellowView.backgroundColor = [UIColor yellowColor];
    yellowView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:yellowView];
    NSLayoutConstraint *yellowCenterXConstraint = [NSLayoutConstraint constraintWithItem:yellowView
                                                                            attribute:NSLayoutAttributeCenterX
                                                                            relatedBy:NSLayoutRelationEqual
                                                                               toItem:self.view
                                                                            attribute:NSLayoutAttributeCenterX
                                                                              multiplier:1
                                                                                constant:0];
    NSLayoutConstraint *yellowCenterYConstraint = [NSLayoutConstraint constraintWithItem:yellowView
                                                                               attribute:NSLayoutAttributeCenterY
                                                                               relatedBy:NSLayoutRelationEqual
                                                                                  toItem:self.view
                                                                               attribute:NSLayoutAttributeCenterY
                                                                              multiplier:1
                                                                                constant:0];
    NSLayoutConstraint *yellowWidthConstraint = [NSLayoutConstraint constraintWithItem:yellowView
                                                                             attribute:NSLayoutAttributeWidth
                                                                             relatedBy:NSLayoutRelationEqual
                                                                                toItem:self.view
                                                                             attribute:NSLayoutAttributeWidth
                                                                            multiplier:.5
                                                                              constant:0];
    NSLayoutConstraint *yellowHeightConstraint = [NSLayoutConstraint constraintWithItem:yellowView
                                                                              attribute:NSLayoutAttributeHeight
                                                                              relatedBy:NSLayoutRelationEqual
                                                                                 toItem:self.view
                                                                              attribute:NSLayoutAttributeHeight
                                                                             multiplier:.5
                                                                               constant:0];
    [self.view addConstraints:@[yellowCenterXConstraint, yellowCenterYConstraint, yellowWidthConstraint, yellowHeightConstraint]];

f:id:hachinobu:20141103203343p:plain

f:id:hachinobu:20141103203344p:plain

共通する部分としてViewのtranslatesAutoresizingMaskIntoConstraintsプロパティは必ずNOにすること。

これをしないとAutolayoutが適用されない。

Viewを作成する際にinitWithFrame:で幅と高さを指定しても意味はないのでNSLayoutConstraintのメソッドで幅と高さの制約を作る必要がある。

組み合わせや使い方によっては柔軟なものができそうであるが、それにしてもNSLayoutConstraintはマゾい

なので今度Masonryを試してみようと思う。

iOS7とiOS8でUIScreenのboundsのサイズが違うので対応する

背景

iOS8からUIScreenのboundsを取得すると端末の向きに応じてwidthとheightの値が変更されるようになりLandscape対応でちょっと困ったのでメモ

iOS7とiOS8で差分を埋める

+ (CGSize)mainScreenSize 
{
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    if ((NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) &&  UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
        return CGSizeMake(screenSize.height, screenSize.width);
    }
    return screenSize;
}

これでiOS7でもiOS8と同様のサイズを返すです。