如何在 Swift iOS 中使用闭包发回数据?

问题描述 投票:0回答:3

我正在按照本教程使用闭包发回数据。 https://betterprogramming.pub/5-ways-to-pass-data-between-view-controllers-18acb467f5ec

在本教程中,第 4 点是“闭包”。我有两个 VC,一个用于选择宠物(FormsVC),一个用于显示所选宠物(ProfileVC)。

下面是ProfileVC的代码:

// ProfileVC

// MARK: - Set Fav Pet Name
    
    func setPetName(pet: String) {
        lblFavouritePet.text = pet
    }

// MARK: - Button Select Your Fav Pet Event

    @IBAction func btnSelectYourFavPet_Event(_ sender: UIButton) {
        
        let vc = FormsVC()
        
        self.present(vc, animated: true)
    }

下面是FormsVC的代码:

// FormsVC

// MARK: - Variable Declaration
    
    var favoritePet = String()

// MARK: - viewDidLoad Method

    override func viewDidLoad() {
        super.viewDidLoad()

        setUpFormsVC()
        
    }

// MARK: - Set Up FormsVC
    
    func setUpFormsVC() {
       
        btnDog.titleLabel?.text = "Dog"
        btnCat.titleLabel?.text = "Cat"
        btnRabbit.titleLabel?.text = "Rabbit"
        btnBird.titleLabel?.text = "Bird"
        
    }

// MARK: - Button Selected Pet Event
    
    @IBAction func selectedPetEvent(_ sender: UIButton) {
        
        favoritePet = sender.titleLabel?.text ?? "Dog"
        
    }

// MARK: - Selected Pet Name
    
    func getFavoritePet() -> String {
        return favoritePet
    }

// MARK: - Button OK Event

    @IBAction func btnOk_Event(_ sender: UIButton) {
        
        let vc = ProfileVC()
        
        self.dismiss(animated: true, completion: {
            vc.setPetName(pet: self.getFavoritePet())
        })

// problem occurs when I dismiss FormsVC after selecting pet, the label displaying selected pet name (lblFavouritePet) throwing error of "Unexpectedly found nil while implicitly unwrapping an Optional value"
        
    }
}

当我在选择宠物后关闭 FormsVC 时出现问题,显示所选宠物名称 (lblFavouritePet) 的标签抛出错误“隐式展开可选值时意外发现 nil”。我不知道为什么它被发现为零,因为我已经分配了 favoritePet 的所选宠物的价值。抱歉这个愚蠢的问题,有人能帮我吗?

ios swift closures pass-data
3个回答
0
投票

正如@matt 提到的,您应该使用呈现视图控制器,而不是创建新实例。它在您使用的教程中说明:

if let vc = presentingViewController as? Profile...

你的应用程序崩溃了,因为你使用了故事板,而

lblFavoritePet
是一个
@IBOutlet
隐式解包可选,因此,你应该从故事板初始化它。但是你在不使用故事板的情况下初始化它,属性仍然存在
nil
.

所以,不要创建新实例,使用教程中说明的代码。

并遵循命名约定.


-1
投票

首先,你必须声明越近你想传递数据的地方。

// FormsVC
// MARK: - Variable Declaration
let completion: ((String)->Void)? = nil

// MARK: - Button OK Event
@IBAction func btnOk_Event(_ sender: UIButton) {
    
    completion?(self.getFavoritePet())
    self.dismiss(animated: true)
}

第二部分是你要写接收数据的代码

// ProfileVC
// MARK: - Button Select Your Fav Pet Event

@IBAction func btnSelectYourFavPet_Event(_ sender: UIButton) {
    
    let vc = FormsVC()
    vc.completion = { petName in
        self.setPetName(pet: petName)
    }

    self.present(vc, animated: true)
}

-1
投票
// MARK: - API call "LogIn"
    
    func apiCallLogIn() {
        
        if Utils.shareInstance.isInternetAvailable() {
            Utils.shareInstance.showLoader(vc: self)
            
            let param: Parameters = ["username": txtEmail.text ?? "", "password": txtPassword.text ?? ""]
            
            let url = AppConstant.API_URL.apiLogIn
            
            AF.request(url, method: .post, parameters: param).responseDecodable(of: UserModel.self) { response in
                
                switch response.result {
                    
                case .success(let user):
                    Utils.shareInstance.hideLoader()
                    
                    if response.value != nil {
                        print("user API Call --> \(user)")
                        
                        if user.result == true {
                            print("true --> \(String(describing: user.message))")
                            
                            self.userModel = user
                            
                            AppConstant.userDefaults.setLoggedIn(value: true)
                            AppConstant.userDefaults.setUserData(data: user)
                            
                            let homeVC = self.storyboard?.instantiateViewController(withIdentifier: AppConstant.ViewController.homeVC) as! HomeViewController
                            
                            self.navigationController?.pushViewController(homeVC, animated: true)
                        } else {
                            print("false --> \(String(describing: user.message))")
                            
                            Utils.shareInstance.displayMyAlertMessage(title: "Error", message: user.message ?? "", vc: self)
                        }
                        
                    } else {
                        Utils.shareInstance.displayMyAlertMessage(title: "Error", message: "No Data Found!", vc: self)
                    }
                    
                    
                case .failure(let error):
                    print("case 2: failure")
                    Utils.shareInstance.hideLoader()
                    Utils.shareInstance.displayMyAlertMessage(title: "Error", message: error.localizedDescription, vc: self)
                }
                
            }
            
            
            // this is also working
//            AF.request(url, method: .post, parameters: param).responseJSON { response in
//
//                switch response.result {
//
//                case .success(let JSON):
//                    Utils.shareInstance.hideLoader()
//
//                    let response = JSON as? [String:Any] ?? [:]
//
//                    print("response --> \(response)")
//
//                case .failure(let error):
//                    Utils.shareInstance.displayMyAlertMessage(title: "Error", message: error.localizedDescription, vc: self)
//                }
//
//            }
            
        } else {
            Utils.shareInstance.displayMyAlertMessage(title: "No Internet", message: "Connection Failed! Check Your Internet Connection!", vc: self)
        }
        
    }
// MARK: - Objc Method for Search
    
    @objc func searchTextField(_ sender: UITextField) {
        
        self.arrSearchItems.removeAll()
        
        if txtEnterSearchText.text?.trimmingCharacters(in: .whitespaces).count != 0 {
            
            isSearching = true
            
            for item in arrItems {
                if let search = txtEnterSearchText.text {
                    let range1 = item.title?.lowercased().range(of: search, options: .caseInsensitive, range: nil, locale: nil)
                    let range2 = item.category?.lowercased().range(of: search, options: .caseInsensitive, range: nil, locale: nil)
                    let range3 = item.price?.lowercased().range(of: search, options: .caseInsensitive, range: nil, locale: nil)
                    
                    if range1 != nil || range2 != nil || range3 != nil {
                        self.arrSearchItems.append(item)
                    }
                }
            }
            print("search array --> \(arrSearchItems.count)")
            
        } else {
            isSearching = false
            print("Without Search")
        }
        
        self.tableVwHome.reloadData()
        
    }

// MARK: - API Call "Category"
    
    func apiCallCategory() {
        
        if Utils.shareInstance.isInternetAvailable() {
            Utils.shareInstance.showLoader(vc: self)
            
            let url = AppConstant.API_URL.apiCategory
            
            AF.request(url, method: .post).responseDecodable(of: CategoriesModel.self) { response in
                
                switch response.result {
                    
                case .success(let categoryModel):
                    Utils.shareInstance.hideLoader()
                    
                    if response.value != nil {
                        print("categoryModel API Call --> \(categoryModel)")
                        
                        self.arrCategories = categoryModel.categories ?? [Category]()
                        
//                        guard let category = categoryModel.categories else { return }
//                        self.arrCategories.append(contentsOf: category) // this will append data every time view gets reload // query
                        
                        print("arrCategories --> \(self.arrCategories)")
                        
                        DispatchQueue.main.async {
                            self.tableVwHome.reloadData()
                        }
                        
                    } else {                        
                        Utils.shareInstance.displayMyAlertMessage(title: "Error", message: "No Data Found!", vc: self)
                    }
                    
                case .failure(let error):
                    print("case 2: failure")
                    Utils.shareInstance.hideLoader()
                    Utils.shareInstance.displayMyAlertMessage(title: "Error", message: error.localizedDescription, vc: self)
                }
            }
            
        } else {
            Utils.shareInstance.displayMyAlertMessage(title: "No Internet", message: "Connection Failed! Check Your Internet Connection!", vc: self)
        }
        
    }
// MARK: - Segment Control Event
    
    @IBAction func segmentControl_Event(_ sender: Any) {
        
        if segmentControl.selectedSegmentIndex == 0 {
            
            topConstraintxtSearch.constant = 0
            topConstraintShowOnly.constant = 0
            heightConstrainttxtSearch.constant = 0
            heightConstraintShowOnly.constant = 0
            vWShowOnly.layoutIfNeeded()
            txtEnterSearchText.layoutIfNeeded()
            
            vWShowOnly.isHidden = true
            txtEnterSearchText.isHidden = true
            apiCallCategory()
            
        } else {
            
            topConstraintxtSearch.constant = 20
            topConstraintShowOnly.constant = 20
            heightConstrainttxtSearch.constant = 50
            heightConstraintShowOnly.constant = 35
            vWShowOnly.layoutIfNeeded()
            txtEnterSearchText.layoutIfNeeded()
            
            vWShowOnly.isHidden = false
            txtEnterSearchText.isHidden = false
            
            if isShowBtnSelected {
                
                btnShowStockItems.setImage(UIImage(named: "dotedCircle"), for: .normal)

                let filteredArray = arrItems.filter({ $0.inStock == true })
                print("In Stock Items --> \(filteredArray.count)")

                self.arrItems = filteredArray
                self.tableVwHome.reloadData()

                isShowBtnSelected = true
                
            } else {
                btnShowStockItems.setImage(UIImage(named: "circle"), for: .normal)

                getItemsData()
                print("mainArray count --> \(arrItems.count)")

                isShowBtnSelected = false
            }
        }
    }
// MARK: - Button Show Only Stock Items Event
    
    @IBAction func btnShowStockItems_Event(_ sender: UIButton) {
        
        if isShowBtnSelected {
            btnShowStockItems.setImage(UIImage(named: "circle"), for: .normal)
            
            getItemsData()
            print("mainArray count --> \(arrItems.count)")
            
            isShowBtnSelected = false
        } else {
            btnShowStockItems.setImage(UIImage(named: "dotedCircle"), for: .normal)
            
            let filteredArray = arrItems.filter({ $0.inStock == true })
            print("In Stock Items --> \(filteredArray.count)")

            self.arrItems = filteredArray
            self.tableVwHome.reloadData()
                        
            isShowBtnSelected = true
        }
    }
// MARK: - TextField Delegate

extension HomeViewController: UITextFieldDelegate{
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        
        txtEnterSearchText.resignFirstResponder()
        return true
    }
}
// MARK: - Save Items
    
    func saveItems() {
        
        item = ItemsModel(imgItem: selectedImgString, title: txtEnterTitle.text, category: dropDown.text, price: txtEnterPrice.text, inStock: isShowBtnSelected ? true : false)
        
        let arrayItems = AppConstant.userDefaults.retrieveItemsData()
        self.arrItems.append(contentsOf: arrayItems)
        
        self.arrItems.append(item)
        print("Array Items --> \(arrItems)")
                
        Toast.show(message: "Item saved successfully", controller: self)
        
        AppConstant.userDefaults.setItemsData(data: self.arrItems)
        
    }
// MARK: - Button Save Event
    
    @IBAction func btnSave_Event(_ sender: UIButton) {
        
        if checkTextFieldsEmpty() == false {
            print("All TextFields are filled")
            
            saveItems()
            
            self.navigationController?.popViewController(animated: true)
            
            selectedImgString = nil
            txtEnterTitle.text = ""
            txtEnterPrice.text = ""
            dropDown.text = ""
            btnInStock.setImage(UIImage(named: "circle"), for: .normal)
            
        } else {
            print("TextFields are empty")
        }
    }
// MARK: - ImagePickerDelegate

extension AddItemViewController: ImagePickerDelegate {
    
    func didSelect(image: UIImage?) {
        
        let selectedImg = image?.resizeImageWithCGSize(newSize: CGSize(width: 80, height: 80))
        
        self.selectedImgString = selectedImg?.jpegData(compressionQuality: 1)?.base64EncodedString()
//        print("selectedImgString --> \(selectedImgString)")
    }
}
import Foundation
import UIKit
import Reachability

class Utils: NSObject {
    
    static var shareInstance = Utils()
    var activityIndicator = UIActivityIndicatorView(style: .large)
    
    // MARK: - Display Alert for Empty TextFields
    
    func displayMyAlertMessage(title: String, message: String, vc: UIViewController) {
        
        let myAlert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        
        myAlert.addAction(okAction)
        vc.present(myAlert, animated: true, completion: nil)
    }
    
    // MARK: - Show Loader
    
    func showLoader(vc: UIViewController) {
        
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        vc.view.addSubview(activityIndicator)
        activityIndicator.centerXAnchor.constraint(equalTo: vc.view.centerXAnchor).isActive = true
        activityIndicator.centerYAnchor.constraint(equalTo: vc.view.centerYAnchor).isActive = true
        
        activityIndicator.startAnimating()
    }
    
    // MARK: - Hide Loader
    
    func hideLoader() {
        activityIndicator.stopAnimating()
    }
    
    // MARK: - Check Internet Availability
    
    func isInternetAvailable() -> Bool {
        
        let reachability = try! Reachability()
        
        if reachability.connection == .wifi || reachability.connection == .cellular {
            print("Internet is available")
            return true
        } else {
            print("Connection Failed! Check Your Internet Connection!")
            return false
        }
    }
    
}

// MARK: - Configure Cell
    
    func ConfigureCell(object: ItemsModel) {
        
        self.selectedBackgroundView = UIView()
        self.selectedBackgroundView?.backgroundColor = .systemBackground
        
        self.lblPrice.text = "\("$") \(object.price ?? "")"
        
        let imgStr = object.imgItem
        
        if let decodedData = Data(base64Encoded: imgStr ?? "", options: .ignoreUnknownCharacters) {
            self.imgItem.image = UIImage(data: decodedData)
        }
        
        self.lblTitle.text = object.title
        self.lblCategory.text = object.category
        
        let inStock = object.inStock
        self.lblInStock.text = inStock ?? false ? "(In stock)" : "(Out of stock)"
        
    }

© www.soinside.com 2019 - 2024. All rights reserved.