Swift-如何在自定义单元格中从堆栈视图中删除白色背景?

时间:2018-07-23 03:26:33

标签: ios swift uistackview

我已经完成FoodTracker app的练习,可以在Apple网站上进行练习。这些星星是通过程序制作的,以显示在视图上。星星处于堆栈视图中。当我在模拟器中选择单元格时,单元格突出显示,星星的背景是白色正方形。我进入草图并重新绘制了星星,使它们没有背景,但星星仍是白色方块。我无法获得高亮显示的单元格的图片,但是我添加了一张图片,以便您知道我在说什么。

有什么想法吗?让我知道您是否需要更多信息。

Sample of cell

这是自定义单元格的代码。

import UIKit

@IBDesignable class RatingControl: UIStackView {

//MARK: Properties
private var ratingButtons = [UIButton]()

var rating = 0 {
    didSet {
        updateButtonSelectionStates()
    }
}

@IBInspectable var starSize: CGSize = CGSize(width: 44.0, height: 44.0) {
    didSet {
        setupButtons()
    }
}

@IBInspectable var starCount: Int = 5 {
    didSet{
        setupButtons()
    }
}

//MARK: Initializers
override init(frame: CGRect) {
    super.init(frame: frame)
    setupButtons()
}

required init(coder: NSCoder) {
    super.init(coder: coder)
    setupButtons()
}

//MARK: Button Action
@objc func ratingButtonTapped(button: UIButton) {
    guard let index = ratingButtons.index(of: button) else {
        fatalError("The button, \(button), is not in the ratingButtons array: \(ratingButtons)")
    }

    // Calculate the rating of the selected button
    let selectedRating = index + 1

    if selectedRating == rating {
        // If the selected star represents the current rating, reset the rating to 0.
        rating = 0
    } else {
        // Otherwise set the rating to the selected star
        rating = selectedRating
    }
}

private func setupButtons() {

    // Clear any existing buttons
    for button in ratingButtons {
        removeArrangedSubview(button)
        button.removeFromSuperview()
    }
    ratingButtons.removeAll()

    // Load Button Images
    let bundle = Bundle(for: type(of: self))
    let filledStar = UIImage(named: "filledStar", in: bundle, compatibleWith: self.traitCollection)
    let emptyStar = UIImage(named:"emptyStar", in: bundle, compatibleWith: self.traitCollection)
    let highlightedStar = UIImage(named:"highlightedStar", in: bundle, compatibleWith: self.traitCollection)

    for index in 0..<starCount {
        // Create the button
        let button = UIButton()

        // Set the button images
        button.setImage(emptyStar, for: .normal)
        button.setImage(filledStar, for: .selected)
        button.setImage(highlightedStar, for: .highlighted)
        button.setImage(highlightedStar, for: [.highlighted, .selected])

        // Add constraints
        button.translatesAutoresizingMaskIntoConstraints = false
        button.heightAnchor.constraint(equalToConstant: starSize.height).isActive = true
        button.widthAnchor.constraint(equalToConstant: starSize.width).isActive = true

        // Set the accessibility label
        button.accessibilityLabel = "Set \(index + 1) star rating"

        // Setup the button action
        button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(button:)), for: .touchUpInside)

        // Add the button to the stack
        addArrangedSubview(button)

        // Add the new button to the rating button array
        ratingButtons.append(button)
    }

    updateButtonSelectionStates()
}

private func updateButtonSelectionStates() {
    for (index, button) in ratingButtons.enumerated() {
        // If the index of a button is less than the rating, that button should be selected.
        button.isSelected = index < rating

        // Set the hint string for the currently selected star
        let hintString: String?
        if rating == index + 1 {
            hintString = "Tap to reset the rating to zero."
        } else {
            hintString = nil
        }

        // Calculate the value string
        let valueString: String
        switch (rating) {
        case 0:
            valueString = "No rating set."
        case 1:
            valueString = "1 star set."
        default:
            valueString = "\(rating) stars set."
        }

        // Assign the hint string and value string
        button.accessibilityHint = hintString
        button.accessibilityValue = valueString
    }
}

}

这是TableViewController的代码

import UIKit
import os.log

class MealTableViewController: UITableViewController {

//MARK: Properties

var meals = [Meal]()

override func viewDidLoad() {
    super.viewDidLoad()

    // Use the edit button item provided by the table view controller.
    navigationItem.leftBarButtonItem = editButtonItem

    // Load any saved meals, otherwise load sample data.
    if let savedMeals = loadMeals() {
        meals += savedMeals

        /*
         If loadMeals() successfully returns an array of Meal objects, this condition is true and the if statement gets executed. If loadMeals() returns nil, there were no meals to load and the if statement doesn’t get executed. This code adds any meals that were successfully loaded to the meals array.
         */
    } else {
        // Load the sample data.
        loadSampleMeals()
        //This code adds any meals that were loaded to the meals array.
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return meals.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    // Table view cells are reused and should be dequeued using a cell identifier.
    let cellIdentifier = "MealTableViewCell"

    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? MealTableViewCell  else {
        fatalError("The dequeued cell is not an instance of MealTableViewCell.")
    }

    // Fetches the appropriate meal for the data source layout.
    let meal = meals[indexPath.row]

    cell.nameLabel.text = meal.name
    cell.photoImageView.image = meal.photo
    cell.ratingControl.rating = meal.rating


    return cell
}

// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    // Return false if you do not want the specified item to be editable.
    return true
}

// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    if editingStyle == .delete {

        // Delete the row from the data source
        meals.remove(at: indexPath.row)

        saveMeals()

        tableView.deleteRows(at: [indexPath], with: .fade)
    } else if editingStyle == .insert {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }    
}

/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {

}
*/

/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
    // Return false if you do not want the item to be re-orderable.
    return true
}
*/

  // MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)
    switch(segue.identifier ?? "") {

    case "AddItem":
        os_log("Adding a new meal.", log: OSLog.default, type: .debug)

    case "ShowDetail":
        guard let mealDetailViewController = segue.destination as? MealViewController else {
            fatalError("Unexpected destination: \(segue.destination)")
        }

        guard let selectedMealCell = sender as? MealTableViewCell else {
            fatalError("Unexpected sender: \(sender)")
        }

        guard let indexPath = tableView.indexPath(for: selectedMealCell) else {
            fatalError("The selected cell is not being displayed by the table")
        }

        let selectedMeal = meals[indexPath.row]
        mealDetailViewController.meal = selectedMeal

    default:
        fatalError("Unexpected Segue Identifier; \(segue.identifier)")
    }
}

//MARK: Actions

@IBAction func unwindToMealList(sender: UIStoryboardSegue) {
    if let sourceViewController = sender.source as? MealViewController, let meal = sourceViewController.meal {

        if let selectedIndexPath = tableView.indexPathForSelectedRow {

            // Update an existing meal.
            meals[selectedIndexPath.row] = meal
            tableView.reloadRows(at: [selectedIndexPath], with: .none)
        } else {
            // Add a new meal.
            let newIndexPath = IndexPath(row: meals.count, section: 0)

            meals.append(meal)
            tableView.insertRows(at: [newIndexPath], with: .automatic)
        }

        // Save the meals.
        saveMeals()
    }
}

//MARK: Private Methods

private func loadSampleMeals() {
    let photo1 = UIImage(named: "meal1")
    let photo2 = UIImage(named: "meal2")
    let photo3 = UIImage(named: "meal3")

    guard let meal1 = Meal(name: "Caprese Salad", photo: photo1, rating: 4) else {
        fatalError("Unable to instantiate meal1")
    }

    guard let meal2 = Meal(name: "Chicken and Potatoes", photo: photo2, rating: 5) else {
        fatalError("Unable to instantiate meal2")
    }

    guard let meal3 = Meal(name: "Pasta with Meatballs", photo: photo3, rating: 3) else {
        fatalError("Unable to instantiate meal2")
    }

    meals += [meal1, meal2, meal3]
}

/// Save Meals
private func saveMeals() {

    let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(meals, toFile: Meal.ArchiveURL.path)

    if isSuccessfulSave {
        os_log("Meals successfully saved.", log: OSLog.default, type: .debug)
    } else {
        os_log("Failed to save meals...", log: OSLog.default, type: .error)
    }
}

/// Load Meals
private func loadMeals() -> [Meal]? {

    return NSKeyedUnarchiver.unarchiveObject(withFile: Meal.ArchiveURL.path) as? [Meal]
 }

}

1 个答案:

答案 0 :(得分:0)

摘自UIStackView上的Apple文档:

  

UIStackView是UIView的非呈现子类;这就对了   不提供自己的任何用户界面。相反,它只是   管理其排列视图的位置和大小。结果是,   一些属性(例如backgroundColor)对堆栈没有影响   视图。

您看不到任何背景颜色变化的原因是您的stackView从未得到渲染。

您可以删除白色背景的方法是将MealTableViewCell的基本视图的背景色更改为透明色。