質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

91.38%

  • Swift

    5020questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

  • iOS

    2950questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • Xcode

    2844questions

    Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

tableView上のUIViewを開放したい

解決済

回答 1

投稿 2017/11/22 09:38

  • 評価
  • クリップ 0
  • VIEW 48

midomurasaki

score 24

Xcode9, swift4でiOSアプリを開発しています。
構成としては、

  • FirstViewController(遷移元)
  • SecondViewController(遷移先, tableView)

があり、SecondViewControllertableViewcellをStoryboardで作成しています。
cell上には自分で作成したTestViewを乗せ、Tagを設定しcell生成時に取得しています。

このTestViewもStoryboardとXibで作成し、Timerを持っています。

import UIKit

class TestView: UIView {

    var timer: Timer?
    let timerInterval: TimeInterval = 60

    override init(frame: CGRect) {
        super.init(frame: frame)
        loadXib("TestView")
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        loadXib("TestView")
    }

    fileprivate func loadXib(_ nibName: String) {
        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: nibName, bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView
        addSubview(view)

        view.translatesAutoresizingMaskIntoConstraints = false
        let bindings = ["view": view]
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", options:NSLayoutFormatOptions(rawValue: 0), metrics:nil, views: bindings))
        addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", options:NSLayoutFormatOptions(rawValue: 0), metrics:nil, views: bindings))        
    }

    override func didAddSubview(_ subview: UIView) {
        super.didAddSubview(subview)
        setTimer()
    }

    override func removeFromSuperview() {
        // 呼ばれない
        timer?.invalidate()
    }

    func setTimer() {
        timer = Timer.scheduledTimer(timeInterval: timerInterval, target: self, selector: #selector(self.update), userInfo: nil, repeats: true)
        timer?.fire()
    }

    @objc func update(tm: Timer) {
        print("hello")
    }

}


cellの生成部分のコードは下記です。

extension SecondViewController: UITableViewDelegate, UITableViewDataSource {
    // 省略        
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "testCell", for: indexPath)
                if let testView: TestView = cell.viewWithTag(1) as? TestView {
                    print("add TestView")
                }
        return cell
    }
    // 省略    
}

発生している問題

cellも正しく追加され、60秒毎のtimerによる処理は問題無く行われるのですが、SecondViewControllerからFirstViewControllerへ戻った際にremoveFromSuperview()が呼ばれず、Timerが動き続けてしまいます。
cell上のviewを開放するにはどうしたらよいか、アドバイスいただけますでしょうか。

以上、よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

didAddSubview(_:)の動作を勘違いしています。
この関数は、自分にsubviewが追加されたときに呼ばれる関数です。(セル生成時に複数回呼ばれるのでタイマーも複数生成されてしまっている → 止めているのは最後のタイマーだけ)

add/removeされたときに処理を行うのであれば、

override func willMove(toSuperview newSuperview: UIView?) {
    if newSuperview != nil {
        //add時の処理
    } else {
        //remove時の処理
    }
}

処理内容によっては、init/deinitとか、didMoveToSuperview/removeFromSuperviewの方が良いかも知れません。

投稿 2017/11/22 10:30

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/22 11:16 編集

    ご回答ありがとうございます。

    breakpointを設定してもremoveFromSuperview()もご回答いただいたelse {...}の部分も呼ばれず、どこかで循環参照が起こっているのでは?と調べた結果、tableViewのcell生成用配列を取得するFirebaseのobserverが正しく開放されていないことが判明し、修正したところご回答いただきましたコードで正しく動作するようになりました。

    キャンセル

  • 2017/11/22 11:26

    「タイマーが止まってないからremoveFromSuperviewが呼ばれていない」と判断したのかと思ったら、本当に呼ばれてなかったんですね。まぁ解決して良かったです。

    ちなみに、もう関係無い話だとは思いますが、overrideしたremoveFromSuperviewの中で super.removeFromSuperview() が呼ばれてないです。

    キャンセル

  • 2017/11/22 11:32

    結局根本の原因は別のところにありましたがdidAddSubview(_:)の勘違いもあり、質問しなければまたハマっていたと思うので質問して良かったです。
    superの抜けについてもご指摘いただきありがとうございます。大変助かりました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

ただいまの回答率

91.38%

関連した質問

同じタグがついた質問を見る

  • Swift

    5020questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

  • iOS

    2950questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

  • Xcode

    2844questions

    Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。