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

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

ただいまの
回答率

90.47%

  • Swift

    7463questions

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

  • Xcode

    4207questions

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

ストーリーボードを使わずにコードのみで値を渡す方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 237
退会済みユーザー

退会済みユーザー

FirstViewController(UIViewController)から
SecondViewController(UICollectionViewCell)へ値を渡す方法をコードのみでの実現したいと思っています。

一連の動作をする4ファイルのコードを記載しました。
ご確認いただければ幸いです よろしくお願いします。

import UIKit

class PresentationController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

private var Button: UIBarButtonItem!
    private let cellId = "cellId"

    override func viewDidLoad() {
        super.viewDidLoad()
        set()
    }

    func set() {

        collectionView?.delegate = self
        collectionView?.dataSource = self

        collectionView?.register(SecondViewController.self, forCellWithReuseIdentifier: "cellId")

    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 1
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        switch indexPath.item { 
        default:
            return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! SecondViewController

        }
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        switch indexPath.item {           
        default:
            return CGSize(width: view.frame.width, height: 300)
        }      
    }

    //ここへ
    @objc func openButton(_ sender: UIButton) {
        print("変更するモーダルビュー 開いたよ")
        //モーダルの動き .custom
        let modalViewController = FirstViewController()
        modalViewController.modalPresentationStyle = .custom
        modalViewController.transitioningDelegate = self
        present(modalViewController, animated: true, completion: nil)
    }

    //
    override func viewWillAppear(_ animated: Bool) {
        collectionView?.reloadData()
    }    
}
import UIKit

class CustomPresentationController: UIPresentationController {
    // 呼び出し元のView Controller の上に重ねるビュー(ブラック)
    var overlayView = UIView()

    override func presentationTransitionWillBegin() {
        guard let containerView = containerView else {
            return
        }

        overlayView.frame = containerView.bounds
        overlayView.backgroundColor = .black
        overlayView.alpha = 0.0
        containerView.insertSubview(overlayView, at: 0)

        presentedViewController.transitionCoordinator?.animate(alongsideTransition: {[weak self] context in
            self?.overlayView.alpha = 0.7
            }, completion:nil)
    }

    override func dismissalTransitionWillBegin() {
        presentedViewController.transitionCoordinator?.animate(alongsideTransition: {[weak self] context in
            self?.overlayView.alpha = 0.0
            }, completion:nil)
    }

    override func dismissalTransitionDidEnd(_ completed: Bool) {
        if completed {
            overlayView.removeFromSuperview()
        }
    }

    let margin = (x: CGFloat(30), y: CGFloat(220.0))

    override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize {
        return CGSize(width: parentSize.width - margin.x, height: parentSize.height - margin.y)
    }

    override var frameOfPresentedViewInContainerView: CGRect {
        var presentedViewFrame = CGRect()
        let containerBounds = containerView!.bounds
        let childContentSize = size(forChildContentContainer: presentedViewController, withParentContainerSize: containerBounds.size)
        presentedViewFrame.size = childContentSize
        presentedViewFrame.origin.x = margin.x / 2.0
        presentedViewFrame.origin.y = margin.y / 2.0

        return presentedViewFrame
    }

    override func containerViewWillLayoutSubviews() {
        overlayView.frame = containerView!.bounds //bounds 境界内
        presentedView?.frame = frameOfPresentedViewInContainerView
        presentedView?.layer.cornerRadius = 10
        presentedView?.clipsToBounds = true
    }
    override func containerViewDidLayoutSubviews() {
    }
}
import UIKit

class FirstViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        set()
    }

    let userName: UITextField = {
        let un = UITextField()
        un.backgroundColor = .gray
        un.text = ""
        un.frame = CGRect(x: 100, y: 120, width: 200, height: 40)
        un.returnKeyType = .done
        return un
    }()

    let saveButton: UIButton = {
        let c = UIButton()
        c.frame = CGRect(x: 200, y: 0, width: 100, height: 100)
        c.setTitle("保存", for: .normal)
        c.backgroundColor = .gray
        return c
    }()

    let cancelbutton: UIButton = {
        let b = UIButton()
        b.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
        b.setTitle("キャンセル", for: .normal)
        b.backgroundColor = .green
        return b
    }()

    func set() {
        //初期化時 カラにする
        userName.text = ""
        view.addSubview(userName)
        userName.delegate = self

        view.addSubview(saveButton)
        //初期化時は タップ無効化
        saveButton.isEnabled = false
        //ターゲット このボタンをダイレクトにタップした時の判定は⬇️の パターン2にあるよ
        saveButton.addTarget(self, action: #selector(save2(_:)), for: .touchUpInside)

        view.addSubview(cancelbutton)
        cancelbutton.addTarget(self, action: #selector(close(_:)), for: .touchUpInside)
    }


    //パターン🏀
    //"return"をタップ キーボード閉じる
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {

        //"return"でtextFieldが閉じる(userName, place, siteなど)
        textField.resignFirstResponder()

        //TextFieldに入力された値に反応して、その値を習得。
        NotificationCenter.default.addObserver(self, selector: #selector(FirstViewController.changeNotifyTextField(sender:)), name: NSNotification.Name.UITextFieldTextDidChange, object: nil)

        return true
    }

    //senderの中に 変更があったUITextFieldが入ってくる感じ
    @objc public func changeNotifyTextField (sender: NSNotification) {

        guard let textView = sender.object as? UITextField else { return }

        //文字が1以上で有効に
        saveButton.isEnabled = (userName.text?.count)! > 0

        //タップ処理を登録
        saveButton.addTarget(self, action: #selector(save3(_:)), for: .touchUpInside)
    }

    //🏀

    //パターン🍯
    //saveButtonをダイレクトにタップ
    @objc func save2(_ sender: UITapGestureRecognizer) {

        NotificationCenter.default.addObserver(self, selector: #selector(FirstViewController.changeNotifyTextField2(sender:)), name: NSNotification.Name.UITextFieldTextDidChange, object: nil)
    }


    //senderの中に 変更があったUITextFieldが入ってくる感じ
    @objc public func changeNotifyTextField2 (sender: NSNotification) {

        guard let textView = sender.object as? UITextField else {
            return
        }
        //文字が1以上で有効に
        saveButton.isEnabled = (userName.text?.count)! > 0
        //タップ処理を登録
        saveButton.addTarget(self, action: #selector(save3(_:)), for: .touchUpInside)
    }


    @objc func save3(_ sender: UITapGestureRecognizer) {

        //値を渡したい
        let controller = presentingViewController as! SecondViewController

        controller.textFromModal = userName.text!

        self.dismiss(animated: true, completion: nil)

        print("タップ キーボードとモーダルビュー 閉じたよ")

        saveButton.endEditing(true)

    }
    //🍯
   @objc func close(_ sender: UITapGestureRecognizer) {
        print("保存キャンセル モーダルビュー 閉じたよ")
        self.dismiss(animated: true, completion: nil)
    }

}
import UIKit

class SecondViewController: UICollectionViewCell {

    //モーダルビューから受け取るテキスト
    var textFromModal = "" {
        didSet {
            // 値が更新されたら、ラベルの内容も更新する
            updateLabel(text: textFromModal)
        }
    }

    //ラベルの内容を更新する処理
    func updateLabel(text: String) {
        nameLabel.text = ""
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .gray
        set()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    let button: UIButton = {
        let b = UIButton()
        b.setTitle("モーダル", for: .normal)
        b.backgroundColor = .blue
        b.translatesAutoresizingMaskIntoConstraints = false
        b.addTarget(self, action: #selector(PresentationController.openButton(_:)), for: .touchUpInside)
        b.frame = CGRect(x: 20, y: 50, width: 100, height: 100)
        return b
    }()


    let nameLabel: UILabel = {
        let l = UILabel()
        l.backgroundColor = .white
        l.translatesAutoresizingMaskIntoConstraints = false
        l.frame = CGRect(x: 20, y: 200, width: 200, height: 50)

        return l
    }()

    func set() {
        addSubview(button)
        addSubview(nameLabel)
    }    
}

//UIPresentationControllerで返す クラス名(CustomPresentationController)
extension PresentationController: UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return CustomPresentationController(presentedViewController: presented, presenting: presenting)
    }

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

+2

SecondViewController().HogeStrng とすると、SecondViewControllerのインスタンスが作成されてそれのHogeStrngに値を設定した後、作成されたSecond~はどこの変数からも保持されないため不要となり即座に破棄されます。次の行も同じで意味がありません。
Second~にはどのように遷移してるんでしょうか?
First~がdismissを読んでるならSecond~からFirst~を表示して戻っているんでしょうか?
それとも新しく別のモーダルを表示をするつもりなのでしょうか?

前者ならpresentingViewControllerでSecond~のインスタンスを取得し設定します。
後者なら、presentViewController等に渡しているSecond~のインスタンスに値を設定します。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/16 15:13 編集

    ありがとうございます。
    説明がわかりづらく申し訳ありません。
    前者だと思います。

    First〜にあるボタンをタップすると
    First〜はSecond〜の上にモーダルで覆いかぶさります。

    その後 textfieldに適当に入力しボタンをタップすると、First〜は閉じてSecond〜が表われます。画面が戻るという形です。
    そこにLabelがあります。

    キャンセル

  • 2018/06/16 21:55 編集

    https://picolica.com/2017/04/04/swift3-get-from-modalview/
    こちらを参考にして、FirstとSecondどちらもUIViewControllerの場合ならlabelに表示できたのですが

    実現したい Firstの UIViewController内でtextfieldに入力し、SecondのUICollectionViewCell内のlabelに表示ができません...

    キャンセル

checkベストアンサー

+1

その場合はFirstのpresentingViewControllerがSecondViewControllerなので、キャストすれば呼び出し元のSecond~が手に入ります。
そいつのHogeStrngにテキストを設定し、FirstをdismissするとSecondに戻るので、Second~のviewWillAppearあたりでCollectionViewをリセット、collectionView(_ collectionView:, cellForItemAt indexPath:)でHogeStrngをラベルに代入という流れになるかと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/17 13:08

    何度も噛み砕いた内容の回答ありがとうございます。

    >>HogeStrngにテキストを設定し
    これは Second〜内で var HogeStrng:String = "" という書き方にしていますが、これで問題ないでしょうか?

    キャンセル

  • 2018/06/17 16:48

    それで問題ないです。
    初期値は空文字なのでなのも出ず、Firstから戻ったら何か入っているので表示されるようになるかと。

    キャンセル

  • 2018/06/17 17:01

    そちらは分かりました。

    >>Second~のviewWillAppearあたりでCollectionViewをリセット、

    こちらのリセットという意味が調べていますが分かりません。
    SecondViewController(uicollectionViewCell)をregisterしているのは別のUICollectionViewControllerなのですが、この中にviewWillAppearを書きますか?

    キャンセル

  • 2018/06/17 18:39

    > SecondViewController(uicollectionViewCell)をregisterしているのは別のUICollectionViewController

    ちょっとこれが何のことかわかりませんが、、、

    単にCollectionViewのセルに値を再設定するにはセルを全部作成し直すのが手っ取り早いというだけです。CollectionView.reloadData()ですね。
    viewWillAppearでreloadDataを呼べばSecond~が表示される直前にセルが全部作成し直されるので、その時に呼ばれる
    collectionView(_ collectionView:, cellForItemAt indexPath:)でセルのラベルのテキストにHogeStrngを設定すればいいです。

    キャンセル

  • 2018/06/17 20:41

    他にUICollectionViewControllerファイルがあり、そこに今回のセル含めUICollectionViewCellを4つそれぞれ
    collectionView?.register(SecondViewController.self, forCellWithReuseIdentifier: "cellId")という形でregisterしております。

    ですのでcellForItemAt...内はswitchで分け、
    return collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! SecondViewController としていますので、
    ここでHogeStrngを設定する方法が分かりません...

    キャンセル

  • 2018/06/17 20:44 編集

    また、First〜のタップしモーダルを閉じる時に下記のコードを発火させデータを渡したいのですが、


    let controller = presentingViewController as! SecondViewController

    SecondViewController(). HogeStrng = textField.text!
    controller.label = SecondViewController(). HogeStrng

    タップするとエラー
    Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

    Cast from 'UIViewController?' to unrelated type 'SecondViewController' always fails
    と出てしまいます。

    見辛く、また何度も申し訳ありません。

    キャンセル

  • 2018/06/17 20:49

    override func viewWillAppear(_ animated: Bool) {
    collectionView?.reloadData()
    }
    はUICollectionViewController内に書きました。

    キャンセル

  • 2018/06/17 22:38

    UICollectionViewControllerというクラスがSecondの中にあってそいつがUICollectionViewDataSourceを実装しているんですね?それだったらviewWillAppearのreloadDataの前にUICollectionViewControllerにHogeStrngを渡すようにしないといけません。

    FirstはちゃんとSecondのpresentViewControllerを使って表示してますか?
    let controllerの行でブレークして presentingViewController がどの型か調べてみてください。
    あと最初に言った通り2,3行目のは意味がありません。

    controller.HogeStrng = textField.text!

    になるはずです。

    キャンセル

  • 2018/06/18 01:14 編集

    toki_td様
    本文に一連の動作をする4つのファイルのコードを追記しました。
    実現したいです 見ていただければ幸いです。どうぞよろしくお願いします。

    ブレイクポイントをつけてデバックコンソールを見ましたが、確認できませんでした。
    おっしゃる意味は何となくわかるのですが、まだ知識が追いついていません、申し訳ないです。

    キャンセル

  • 2018/06/18 17:36

    一連の動作をする4つのファイルのコードの不備のある部分をご指摘頂ければ助かります。

    キャンセル

  • 2018/06/18 23:13

    PresentationControllerのpresent(_:animated:completion:)を呼んでいるんですから
    Firstから見てpresentingViewControllerはPresentationControllerです。

    let controller = ~

    の位置でブレークした状態でデバッグコンソールで

    > po self.presentingViewController

    としたら多分PresentationControllerになっていると思います。

    この場合、SecondではなくPresentationControllerにHogeStrngを覚えさせてください。

    そして PresentationControllerのcollectionView(_ collectionView:, cellForItemAt:) でセル(Second~)を返していますが、
    この時にSecondViewControllerにHogeStrngを渡します。

    キャンセル

  • 2018/06/19 12:21

    >>この場合、
    ここからが分かりません。

    HogeStrngは本文コードだとtextFromModalの事です。分かりづらくすみません。
    下記のコードをPresentationControllerのviewDidLoad内に書きました。
    これを覚えさせるという事でしょうか?


    //モーダルビューから受け取るテキスト
    var textFromModal = "" {
    didSet {
    // 値が更新されたら、ラベルの内容も更新する
    updateLabel(text: textFromModal)
    }
    }



    //ラベルの内容を更新する処理
    func updateLabel(text: String) {
    SecondViewController().nameLabel.text = ""
    }

    キャンセル

  • 2018/06/19 14:18

    FirstからSecondへ戻る際の操作まで、私でも分かるように何度も丁寧に教えていただいき、本当にありがとうございます。
    toki_td様の回答をベストアンサーとさせていただきます。

    まだ実現自体はできていないので、他の質問として回答を求めたいと思います。
    本当に助かりました。もし宜しければ見ていただきたいです。

    キャンセル

関連した質問

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

  • Swift

    7463questions

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

  • Xcode

    4207questions

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