iOS开发:读取Plist数组元素并传值至ViewController展示菜品信息
Hey there! Let's fix this step by step to get your restaurant menu data showing up correctly in the RestaurantViewController when a collection view cell is tapped.
First, Let's Clean Up Data Handling with Models
Instead of relying on NSMutableArray (which is Objective-C-era code), let's define Swift structs to model your data. This makes it way easier to work with and avoids messy dictionary access.
// Define a model for individual menu items struct MenuItem { let name: String let price: String // Use Double instead if your plist stores numeric prices } // Define a model for restaurants struct Restaurant { let name: String let menuItems: [MenuItem] }
Next, Load and Parse the Plist Properly
Load your plist once (not every time a cell is tapped!) and convert it into our [Restaurant] array. Add this method to your collection view controller:
private func loadRestaurants() -> [Restaurant] { // Get the plist path safely guard let plistPath = Bundle.main.path(forResource: "ResturantFile", ofType: "plist"), // Load raw data as an array of dictionaries let rawRestaurantData = NSArray(contentsOfFile: plistPath) as? [[String: Any]] else { print("Failed to load or parse the RestaurantFile.plist") return [] } var restaurants = [Restaurant]() for rawRestaurant in rawRestaurantData { // Extract the restaurant name (the only key in each dictionary) guard let restaurantName = rawRestaurant.keys.first, // Extract the menu items array for this restaurant let rawMenuItems = rawRestaurant[restaurantName] as? [[String: String]] else { continue // Skip invalid entries } // Convert raw menu items to our MenuItem model let menuItems = rawMenuItems.compactMap { itemDict in guard let itemName = itemDict["itemName"], let itemPrice = itemDict["ItemPrice"] else { return nil // Skip items missing name/price } return MenuItem(name: itemName, price: itemPrice) } restaurants.append(Restaurant(name: restaurantName, menuItems: menuItems)) } return restaurants }
Then load this array in viewDidLoad so it's ready to use:
var restaurants: [Restaurant] = [] var restaurantImageArray: [UIImage] = [] // Your existing image array override func viewDidLoad() { super.viewDidLoad() restaurants = loadRestaurants() // Other setup code for your collection view }
Fix the didSelectItemAt Method
Your original code had a broken sorting line and was reloading the plist every tap. Let's replace it with logic that grabs the selected restaurant's data and passes it to RestaurantViewController:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { // Get the selected restaurant from our preloaded array let selectedRestaurant = restaurants[indexPath.row] let selectedImage = restaurantImageArray[indexPath.row] // Option 1: Use your existing closure to pass data blockClosure?(selectedImage, selectedRestaurant.name, selectedRestaurant.menuItems) // Option 2: If you're using navigation to push the VC directly (instead of closure) let restaurantVC = RestaurantViewController() restaurantVC.restaurantImage = selectedImage restaurantVC.restaurantName = selectedRestaurant.name restaurantVC.menuItems = selectedRestaurant.menuItems navigationController?.pushViewController(restaurantVC, animated: true) }
Set Up RestaurantViewController to Display the Data
Update your RestaurantViewController to accept the data and populate the table view:
class RestaurantViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { // Properties to receive data var restaurantImage: UIImage? var restaurantName: String? var menuItems: [MenuItem] = [] @IBOutlet weak var tableView: UITableView! @IBOutlet weak var restaurantImageView: UIImageView! @IBOutlet weak var restaurantNameLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // Configure table view tableView.dataSource = self tableView.delegate = self // Set up UI with received data restaurantImageView.image = restaurantImage restaurantNameLabel.text = restaurantName title = restaurantName } // MARK: - UITableView Data Source func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return menuItems.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "MenuItemCell", for: indexPath) let item = menuItems[indexPath.row] // Configure cell labels (adjust based on your cell's structure) cell.textLabel?.text = item.name cell.detailTextLabel?.text = item.price return cell } }
Key Fixes from Your Original Code
- No more repeated plist loads: We load the data once in
viewDidLoadinstead of every time a cell is tapped. - Broken sorting logic removed: Your original
sorted(by: {$0 == "ItemName"})was incorrect because it tried to compare array elements to a string instead of sorting menu items properly (our model handles this cleanly). - Type-safe data passing: Using structs ensures we're passing valid, typed data instead of ambiguous
Anyobjects.
内容的提问来源于stack exchange,提问作者Gurjit Singh

