প্রোগ্রামিয়ালি কনটেইনার ভিউ কীভাবে যুক্ত করবেন


107

একটি কনটেইনার ভিউ ইন্টারফেস এডিটর এর মাধ্যমে স্টোরিবোর্ডে সহজেই যুক্ত করা যায়। যুক্ত করা হলে, একটি ধারক ভিউ একটি স্থানধারক দর্শন, একটি এম্বেড সেগু এবং একটি (শিশু) দর্শন নিয়ন্ত্রণকারীর।

যাইহোক, আমি প্রোগ্রাম হিসাবে কনটেইনার ভিউ যুক্ত করার উপায় খুঁজে পাচ্ছি না। আসলে, আমি এমনকি নামের একটি শ্রেণিও খুঁজে পাচ্ছি না UIContainerView

কনটেইনার ভিউ ক্লাসের একটি নাম অবশ্যই একটি ভাল শুরু। সিগ সহ একটি সম্পূর্ণ গাইডের অনেক প্রশংসা হবে।

আমি ভিউ কন্ট্রোলার প্রোগ্রামিং গাইড সম্পর্কে সচেতন, তবে আমি এটিকে ইন্টারফেস বিল্ডার কনটেইনার দর্শকের জন্য যেভাবে করে তা হিসাবে বিবেচনা করি না। উদাহরণস্বরূপ, যখন সীমাবদ্ধতাগুলি যথাযথভাবে সেট করা থাকে, তখন (শিশু) ভিউ কনটেইনার ভিউতে আকার পরিবর্তনের সাথে অভিযোজিত হবে।


1
আপনি যখন বলছেন "যখন সীমাবদ্ধতাগুলি সঠিকভাবে সেট করা থাকে তখন (শিশু) দৃষ্টিভঙ্গি কনটেইনার ভিউতে আকার পরিবর্তনের সাথে অভিযোজিত হয়" (এর মাধ্যমে বোঝানো হয় যে আপনি যখন নিয়ামক নিয়ন্ত্রণ দেখেন তখন এটি সত্য নয়)? সীমাবদ্ধতাগুলি একই কাজ করে আপনি আইবিতে ধারক ভিউয়ের মাধ্যমে এটি করেছেন বা প্রোগ্রামারিকভাবে নিয়ামক সংযুক্তি দেখুন।
রব

1
সর্বাধিক গুরুত্বপূর্ণ বিষয় এম্বেড করা ViewControllerজীবনচক্র life ViewControllerইন্টারফেস বিল্ডার দ্বারা এম্বেড করা জীবনের চক্রটি স্বাভাবিক, তবে প্রোগ্রামযুক্তভাবে যুক্ত করা হয়েছে viewDidAppear, না হয়ও viewWillAppear(_:)নয় viewWillDisappear
ডনসং

2
@DawnSong - আপনি সঠিকভাবে দৃশ্য সংবরণ কল না, viewWillAppearএবং viewWillDisappearশিশু দৃশ্য নিয়ামক উপর, বলা হয় শুধু জরিমানা। যেখানে নেই সেখানে আপনার যদি উদাহরণ থাকে তবে আপনাকে স্পষ্ট করে দেওয়া উচিত, বা কেন তা নয় তা জিজ্ঞাসা করে নিজের প্রশ্ন পোস্ট করা উচিত।
রব

উত্তর:


228

একটি স্টোরিবোর্ড "ধারক দেখুন" কেবল একটি স্ট্যান্ডার্ড UIViewঅবজেক্ট। কোনও বিশেষ "ধারক দর্শন" প্রকার নেই। প্রকৃতপক্ষে, আপনি যদি ভিউয়ের স্তরক্রমের দিকে নজর দেন, আপনি দেখতে পাবেন যে "ধারক দর্শন" একটি মান UIView:

ধারক দেখুন

এই প্রোগ্রামটিমেটিকালিটি অর্জনের জন্য, আপনি "নিয়ন্ত্রক সংযুক্তি দেখুন" নিয়োগ করেন:

  • instantiateViewController(withIdentifier:)স্টোরিবোর্ড অবজেক্টে কল করে চাইল্ড ভিউ কন্ট্রোলার ইনস্ট্যান্ট করুন ।
  • addChildআপনার প্যারেন্ট ভিউ কন্ট্রোলারে কল করুন ।
  • viewআপনার ভিউ হায়ারার্কিতে ভিউ কন্ট্রোলার যুক্ত করুন addSubview(এবং frameউপযুক্ত হিসাবে বা সীমাবদ্ধগুলিও সেট করুন ) set
  • didMove(toParent:)অভিভাবক দর্শন নিয়ন্ত্রকের রেফারেন্সটি পাস করে চাইল্ড ভিউ কন্ট্রোলারে পদ্ধতিটি কল করুন ।

দেখুন একটি ধারক দেখুন কন্ট্রোলার বাস্তবায়নকারী মধ্যে দেখুন কন্ট্রোলার গাইড প্রোগ্রামিং এবং অধ্যায় "একটি কনটেইনার দেখুন কন্ট্রোলার বাস্তবায়নকারী" UIViewController ক্লাস রেফারেন্স


উদাহরণস্বরূপ, সুইফ্ট ৪.২ এ এটি দেখতে দেখতে লাগবে:

override func viewDidLoad() {
    super.viewDidLoad()

    let controller = storyboard!.instantiateViewController(withIdentifier: "Second")
    addChild(controller)
    controller.view.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(controller.view)

    NSLayoutConstraint.activate([
        controller.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
        controller.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
        controller.view.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
        controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10)
    ])

    controller.didMove(toParent: self)
}

দ্রষ্টব্য, উপরে বর্ণমুখে আসলে একটি "ধারক ভিউ" যুক্ত করে না। আপনি যদি এটি করতে চান তবে আপনি এমন কিছু করতে চাই:

override func viewDidLoad() {
    super.viewDidLoad()

    // add container

    let containerView = UIView()
    containerView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(containerView)
    NSLayoutConstraint.activate([
        containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
        containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
        containerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
        containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
    ])

    // add child view controller view to container

    let controller = storyboard!.instantiateViewController(withIdentifier: "Second")
    addChild(controller)
    controller.view.translatesAutoresizingMaskIntoConstraints = false
    containerView.addSubview(controller.view)

    NSLayoutConstraint.activate([
        controller.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
        controller.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
        controller.view.topAnchor.constraint(equalTo: containerView.topAnchor),
        controller.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
    ])

    controller.didMove(toParent: self)
}

এই উত্তরোত্তর প্যাটার্নটি চূড়ান্তভাবে কার্যকর যদি কখনও কখনও পৃথক শিশু দেখার নিয়ন্ত্রকদের মধ্যে রূপান্তর হয় এবং আপনি কেবল নিশ্চিত করতে চান যে কোনও সন্তানের দৃষ্টিভঙ্গি একই স্থানে রয়েছে এবং পূর্ববর্তী সন্তানের দৃশ্যে (যেমন প্লেসমেন্টের জন্য সমস্ত অনন্য বাধাগুলি ধারক দৃশ্যের দ্বারা নির্ধারিত হয়, বরং প্রতিবার এই প্রতিবন্ধকতাগুলি পুনর্নির্মাণ করার চেয়ে)। তবে যদি কেবল সাধারণ দৃষ্টিকোণ পরিবেশন করা হয় তবে এই পৃথক ধারক দর্শনটির প্রয়োজন কম বাধ্যকারী।


উপরের উদাহরণগুলিতে আমি নিজের প্রতিবন্ধকতাগুলি সংজ্ঞায়িত translatesAutosizingMaskIntoConstraintsকরতে চলেছি false। আপনি যদি চান তবে আপনি অবশ্যই translatesAutosizingMaskIntoConstraintsহিসাবে ছেড়ে যেতে পারেন এবং আপনার যুক্ত দর্শনগুলির জন্য এবং trueউভয়ই সেট করতে পারেন।frameautosizingMask


এই উত্তরটির পূর্ববর্তী সংশোধনগুলি সুইফট 3 এবং সুইফট 2 উপস্থাপনের জন্য দেখুন।


আপনার উত্তরটি সম্পূর্ণ বলে আমি মনে করি না। সর্বাধিক গুরুত্বপূর্ণ বিষয় এম্বেড করা ViewControllerজীবনচক্র life ViewControllerইন্টারফেস বিল্ডার দ্বারা এম্বেড করা জীবনের চক্রটি স্বাভাবিক, তবে প্রোগ্রামযুক্তভাবে যুক্ত করা হয়েছে viewDidAppear, না হয়ও viewWillAppear(_:)নয় viewWillDisappear
ডনসং

আরেকটি অদ্ভুত জিনিস যে এমবেড করা ViewController's viewDidAppearতার বাবা-মার মধ্যে বলা হয় viewDidLoad, এর পরিবর্তে সময় তার বাবা-মারviewDidAppear
DawnSong

@ ডনসং - "তবে প্রোগ্রাম যুক্তভাবে যুক্ত করা একটিতে viewDidAppear[তবে] দু'টিও viewWillAppear(_:)নেই viewWillDisappear"। willপ্রদর্শিত পদ্ধতি উভয় পরিস্থিতিতে সঠিকভাবে বলা হয়। didMove(toParentViewController:_)প্রোগ্রাম থেকে এটি করার সময় একজনকে অবশ্যই ডাকতে হবে , অন্যথায় তারা তা করবে না। উপস্থিতি সময় সম্পর্কে। পদ্ধতি, তারা উভয় উপায়ে একই ক্রম বলা হয়। কি আলাদা হয়, এর সময়সীমা viewDidLoad, কারণ এম্বেড সহ, এটি আগে লোড করা হয়েছিল parent.viewDidLoad, তবে প্রোগ্রামেটিক সহ, যেমনটি আমরা প্রত্যাশা করি, এটি ঘটে যায় parent.viewLoadLoad
রব

2
আমি কাজ না করার প্রতিবন্ধকতায় আটকে ছিলাম; আমি অনুপস্থিত ছিল দেখা যাচ্ছে translatesAutoresizingMaskIntoConstraints = false। আমি জানি না কেন এটির প্রয়োজন বা কেন এটি জিনিসগুলিকে কাজ করে তোলে, তবে আপনার উত্তরে এটি অন্তর্ভুক্ত করার জন্য আপনাকে ধন্যবাদ।
হেসেন

1
@ রব এ ডেভেলপার.এপল. com/library/archive/featuredarticles/… তালিকাভুক্ত 5-1-তে, অবজেক্টিভ-সি কোডের একটি লাইন রয়েছে যা বলে, "কন্টেন্ট.ভিউ.ফ্রেম = [স্ব ফ্রেমফোর্ডকন্ট্যান্ট্রোলার]"। " সেই কোডটিতে "ফ্রেমফোরকন্টেন্টট্রোলটার" কী? এটা কি ধারক দেখার ফ্রেম?
ড্যানিয়েল ব্রোভার

24

সুইফট 3 এ @ রবের উত্তর:

    // add container

    let containerView = UIView()
    containerView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(containerView)
    NSLayoutConstraint.activate([
        containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
        containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10),
        containerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
        containerView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10),
        ])

    // add child view controller view to container

    let controller = storyboard!.instantiateViewController(withIdentifier: "Second")
    addChildViewController(controller)
    controller.view.translatesAutoresizingMaskIntoConstraints = false
    containerView.addSubview(controller.view)

    NSLayoutConstraint.activate([
        controller.view.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
        controller.view.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
        controller.view.topAnchor.constraint(equalTo: containerView.topAnchor),
        controller.view.bottomAnchor.constraint(equalTo: containerView.bottomAnchor)
        ])

    controller.didMove(toParentViewController: self)

13

বিস্তারিত

  • এক্সকোড 10.2 (10E125), সুইফট 5

সমাধান

import UIKit

class WeakObject {
    weak var object: AnyObject?
    init(object: AnyObject) { self.object = object}
}

class EmbedController {

    private weak var rootViewController: UIViewController?
    private var controllers = [WeakObject]()
    init (rootViewController: UIViewController) { self.rootViewController = rootViewController }

    func append(viewController: UIViewController) {
        guard let rootViewController = rootViewController else { return }
        controllers.append(WeakObject(object: viewController))
        rootViewController.addChild(viewController)
        rootViewController.view.addSubview(viewController.view)
    }

    deinit {
        if rootViewController == nil || controllers.isEmpty { return }
        for controller in controllers {
            if let controller = controller.object {
                controller.view.removeFromSuperview()
                controller.removeFromParent()
            }
        }
        controllers.removeAll()
    }
}

ব্যবহার

class SampleViewController: UIViewController {
    private var embedController: EmbedController?

    override func viewDidLoad() {
        super.viewDidLoad()
        embedController = EmbedController(rootViewController: self)

        let newViewController = ViewControllerWithButton()
        newViewController.view.frame = CGRect(origin: CGPoint(x: 50, y: 150), size: CGSize(width: 200, height: 80))
        newViewController.view.backgroundColor = .lightGray
        embedController?.append(viewController: newViewController)
    }
}

সম্পূর্ণ নমুনা

ViewController

import UIKit

class ViewController: UIViewController {

    private var embedController: EmbedController?
    private var button: UIButton?
    private let addEmbedButtonTitle = "Add embed"

    override func viewDidLoad() {
        super.viewDidLoad()

        button = UIButton(frame: CGRect(x: 50, y: 50, width: 150, height: 20))
        button?.setTitle(addEmbedButtonTitle, for: .normal)
        button?.setTitleColor(.black, for: .normal)
        button?.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        view.addSubview(button!)

        print("viewDidLoad")
        printChildViewControllesInfo()
    }

    func addChildViewControllers() {

        var newViewController = ViewControllerWithButton()
        newViewController.view.frame = CGRect(origin: CGPoint(x: 50, y: 150), size: CGSize(width: 200, height: 80))
        newViewController.view.backgroundColor = .lightGray
        embedController?.append(viewController: newViewController)

        newViewController = ViewControllerWithButton()
        newViewController.view.frame = CGRect(origin: CGPoint(x: 50, y: 250), size: CGSize(width: 200, height: 80))
        newViewController.view.backgroundColor = .blue
        embedController?.append(viewController: newViewController)

        print("\nChildViewControllers added")
        printChildViewControllesInfo()
    }

    @objc func buttonTapped() {

        if embedController == nil {
            embedController = EmbedController(rootViewController: self)
            button?.setTitle("Remove embed", for: .normal)
            addChildViewControllers()
        } else {
            embedController = nil
            print("\nChildViewControllers removed")
            printChildViewControllesInfo()
            button?.setTitle(addEmbedButtonTitle, for: .normal)
        }
    }

    func printChildViewControllesInfo() {
        print("view.subviews.count: \(view.subviews.count)")
        print("childViewControllers.count: \(childViewControllers.count)")
    }
}

ViewControllerWithButton

import UIKit

class ViewControllerWithButton:UIViewController {

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

    private func addButon() {
        let buttonWidth: CGFloat = 150
        let buttonHeight: CGFloat = 20
        let frame = CGRect(x: (view.frame.width-buttonWidth)/2, y: (view.frame.height-buttonHeight)/2, width: buttonWidth, height: buttonHeight)
        let button = UIButton(frame: frame)
        button.setTitle("Button", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        view.addSubview(button)
    }

    override func viewWillLayoutSubviews() {
        addButon()
    }

    @objc func buttonTapped() {
        print("Button tapped in \(self)")
    }
}

ফলাফল

এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন এখানে চিত্র বর্ণনা লিখুন


1
আমি যোগ করতে এই কোড ব্যবহার করেছেন tableViewControllerএকটি viewControllerকিন্তু সাবেক শিরোনাম সেট করতে পারিনি। এটা সম্ভব কিনা তা আমি জানি না। আমি এই প্রশ্ন পোস্ট করেছি । আপনার যদি এটি তাকান তবে এটি আপনার সুন্দর of
Mahan

12

সুইফট 5 এ আমার কোডটি এখানে।

class ViewEmbedder {
class func embed(
    parent:UIViewController,
    container:UIView,
    child:UIViewController,
    previous:UIViewController?){

    if let previous = previous {
        removeFromParent(vc: previous)
    }
    child.willMove(toParent: parent)
    parent.addChild(child)
    container.addSubview(child.view)
    child.didMove(toParent: parent)
    let w = container.frame.size.width;
    let h = container.frame.size.height;
    child.view.frame = CGRect(x: 0, y: 0, width: w, height: h)
}

class func removeFromParent(vc:UIViewController){
    vc.willMove(toParent: nil)
    vc.view.removeFromSuperview()
    vc.removeFromParent()
}

class func embed(withIdentifier id:String, parent:UIViewController, container:UIView, completion:((UIViewController)->Void)? = nil){
    let vc = parent.storyboard!.instantiateViewController(withIdentifier: id)
    embed(
        parent: parent,
        container: container,
        child: vc,
        previous: parent.children.first
    )
    completion?(vc)
}

}

ব্যবহার

@IBOutlet weak var container:UIView!

ViewEmbedder.embed(
    withIdentifier: "MyVC", // Storyboard ID
    parent: self,
    container: self.container){ vc in
    // do things when embed complete
}

নন-স্টোরিবোর্ড ভিউ কন্ট্রোলারের সাথে অন্য এম্বেড ফাংশনটি ব্যবহার করুন।


2
দুর্দান্ত ক্লাস, তবে আমি একই মাস্টার ভিউ কন্ট্রোলারের মধ্যে 2 টি ভিউ কন্ট্রোলারকে এম্বেড করার জন্য নিজেকে প্রয়োজনীয় বলে মনে করি, যা আপনার removeFromParentকল প্রতিরোধ করে, আপনি কীভাবে আপনার ক্লাসটিকে এই অনুমতি দেওয়ার জন্য সংশোধন করবেন?
গ্যারিসাবো

উজ্জ্বল :) আপনাকে ধন্যবাদ
বিদ্রোহী

এটি দুর্দান্ত উদাহরণ, তবে আমি কীভাবে এতে কিছু রূপান্তর অ্যানিমেশন যুক্ত করতে পারি (এম্বেডিং, চাইল্ড ভিউ নিয়ন্ত্রণকারীদের প্রতিস্থাপন)?
মিশা জিওব্রো
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.