Start Developing iOS Apps (Swift)->實現自定義控件(二)

添加對Interface Builder的支持

如果你在Interface Builder中看rating控件,你會發現它就是個大的、空的矩形。更糟糕的是,如果你選擇rating控件,它的邊框將變成紅色,這表明rating 控件的布局有問題。事實上,還有另外兩個表明可能有問題的跡象。在右側的Activity viewer(活動查看器)有一個黃色警告三角。在View Controller場景旁邊的大綱視圖還有一個紅色的錯誤圖標。

image: ../Art/ICC_errorsandwarnings_2x.png

如果你點擊這些圖標,Xcode會顯示關于這兩個錯誤和警告的更多信息。

image: ../Art/ICC_ambiguouslayoutwarning_2x.png
image: ../Art/ICC_ambiguouslayoutwarning_2x.png

image: ../Art/ICC_missingconstrainterror_2x.png

這兩種情況,根本的原因是一樣的。Interface Builder 不知道任何關于rating控件的內容。為了修復它,你需要使用@IBDesignabel來定義控件。它讓Interface Builder實例化你的控件的一個副本,并直接將其繪制到畫布中。另外,現在Interface Builder具有一個活動的控件副本,它的布局引擎能夠正確的定位和設置控件的大小。

把控件聲明為@IBDesignable

  1. 在RatingControl.swift,找到類聲明:
    class RatingControl: UIStackView {
  1. 在它前面加上 @IBDesignable。
@IBDesignable class RatingControl: UIStackView {
  1. 按下Command-B來構建項目(或者選擇 Product > Build)。
  2. 打開Main.storyboard。當構建完成,storyboard將顯示rating控件的實時視圖。


    image: ../Art/ICC_designableliveview_2x.png

    注意,現在畫布正確的設置了RatingControl視圖的尺寸和位置。而警告和錯誤也已經消失。

Interface Builder能夠做很多事,不僅僅是顯示你的自定義視圖。你能夠指定一些屬性可以在Attributes Inspector中被設置。添加@IBInspectable屬性到所需的屬性。Interface Builder支持基本類型(以及相應的可選項)的檢查,包括:布爾值、數字、字符串,以及CGRect、CGSize、CDPoint和UIColor。

添加可檢查屬性

  1. 在RatingControl.swift中,在//MARK: Properties 部分的下面添加如下屬性:
@IBInspectable var starSize: CGSize = CGSize(width: 44.0, height: 44.0)
        @IBInspectable var starCount: Int = 5

這幾行代碼定義了按鈕的尺寸,并定義了你的控件有多少個按鈕。

  1. 現在你需要使用這些值。定位到setupButtons()方法,做如下改變:
  2. 在for-in聲明,把數字5改為startCount。
  3. 在 button.heightAnchor.constraint()方法調用,把數字44.0改為starSize.height。
  4. 在 button.widthAnchor.constraint()方法調用,把數字44.0改為starSize.width。現在方法應該如下所示:
private func setupButtons() {
            
            for _ in 0..<starCount {
                // Create the button
                let button = UIButton()
                button.backgroundColor = UIColor.red
                
                // Add constraints
                button.translatesAutoresizingMaskIntoConstraints = false
                button.heightAnchor.constraint(equalToConstant: starSize.height).isActive = true
                button.widthAnchor.constraint(equalToConstant: starSize.width).isActive = true
                
                // 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)
            }
        }

如果你切換到 Main.storyboard并選擇RatingControl,你將看到Star Size 和Star Count 已設置到了Attributes inspector中。虛線表示控件當前正在使用默認的值(44.0點和5星)。但是現在改變這些值還不會改變控件。


image: ../Art/ICC_inspectableattributes_2x.png
  1. 要更新控件,你需要在每次這些屬性改變的時候重新設置控件的按鈕。為了實現它,給每個屬性添加一個屬性觀察器(property observer)。屬性觀察器在屬性值每次被設置時調用,并且可以在值改變之前或之后立刻執行。
@IBInspectable var starSize: CGSize = CGSize(width: 44.0, height: 44.0) {
            didSet {
                setupButtons()
            }
        }
         
        @IBInspectable var starCount: Int = 5 {
            didSet {
                setupButtons()
            }
        }

這里,你為starSize和starCount屬性定義了屬性觀察器。具體來說,didSet屬性觀察器會在屬性值被設置之后立刻被調用。你的實現是調用 setupButtons()方法。這個方法使用更新的尺寸和數量添加新的按鈕;但是,這個實現沒有擺脫舊的按鈕。

  1. 為了清除舊的按鈕,在setupButtons() 方法的開始位置添加如下代碼:
// clear any existing buttons
        for button in ratingButtons {
            removeArrangedSubview(button)
            button.removeFromSuperview()
        }
        ratingButtons.removeAll()

這段代碼遍歷所有的rating控件的按鈕。首先,它從stack view管理的視圖列表中刪除按鈕。這告訴stack view它不用再計算這個按鈕的尺寸和位置——但按鈕仍然是stack view的子視圖。接下來,代碼把按鈕從stack view中完全刪除。最后,當所有的按鈕都被刪除后,代碼清空ratingButtons數組。
現在setupButtons()方法看上去是這樣的。

private func setupButtons() {
            
            // clear any existing buttons
            for button in ratingButtons {
                removeArrangedSubview(button)
                button.removeFromSuperview()
            }
            ratingButtons.removeAll()
            
            for _ in 0..<starCount {
                // Create the button
                let button = UIButton()
                button.backgroundColor = UIColor.red
                
                // Add constraints
                button.translatesAutoresizingMaskIntoConstraints = false
                button.heightAnchor.constraint(equalToConstant: starSize.height).isActive = true
                button.widthAnchor.constraint(equalToConstant: starSize.width).isActive = true
                
                // 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)
            }
        }

注意
從性能角度上看,刪除并替換所有按鈕并不是一個好主意。但是,didSet觀察器只能在設計的時候被Interface Builder調用。當應用運行時,setupButtons()只被調用一次,在控件第一次從storyboard被加載的時候。因此,沒有必要創建更復雜的解決方案來更新現有的按鈕。

檢查點:打開Main.storyboard并選擇RatingControl對象。嘗試改變Start Size和StarCount屬性。畫布中的控件會發生改變以匹配新的設置。運行應用,你將在模擬器中看到這些改變。

image: ../Art/ICC_modifyinginspectableproperties_2x.png

記住,當你測試完了之后,把值改回默認的。

進一步探索
更多關于使用自定義視圖的信息,見Xcode help中的Lay out user interfaces > Add objects and media > Render custom views。

添加星星圖片到按鈕

接下來,你將添加空的、填充的、以及高亮的星星圖片到按鈕。


image: ../Art/ICC_emptyStar_2x.png

image: ../Art/ICC_filledStar_2x.png

image: ../Art/ICC_highlightedStar_2x.png

你可以在課后的下載文件中找到Images文件,從里面找到這些圖片,或者使用你自己的圖片。(確保圖片的名字和你在稍后代碼中圖片的名字保持一致。)

添加圖片到你的項目

  1. 在project navigator中,選擇Assets.xcassets來查看資源目錄(asset catalog)。
    回想一下,資源目錄是為應用存儲和組織圖片資源的地方。
  2. 在左下角,點擊加號(+)并從彈出菜單選擇New Folder。


    image: ../Art/ICC_assetcatalog_addfolder_2x.png
    image: ../Art/ICC_assetcatalog_addfolder_2x.png
  3. 雙擊文件名稱,重命名為Rating Images。
  4. 選中這個文件,在右下角,點擊加號按鈕并在彈出菜單中選擇New Image Set。
    一個圖片集合(image set)代表一個圖像資源,但是能夠包含圖像的不同版本,這些版本是用來在不同屏幕分辨率上顯示的。
  5. 雙擊image set的名字,重命名為emptyStar。
  6. 在電腦中,選擇你想添加的空心星星圖片。
  7. 拖拽這個圖片放到image set的2x插槽內。


    image: ../Art/ICC_emptystar_drag_2x.png

    2x是本課你選中的iPhone 7模擬器的顯示分辨率。

  8. 選中這個文件,在右下角,點擊加號按鈕并在彈出菜單中選擇New Image Set。
  9. 雙擊image set的名字,重命名為filledStar。
  10. 在電腦上,選擇你想要添加的填充星星圖片。
  11. 拖拽這個圖片放到image set的2x插槽內。


    image: ../Art/ICC_filledstar_drag_2x.png
  12. 選中這個文件,在右下角,點擊加號按鈕并在彈出菜單中選擇New Image Set。
  13. 雙擊image set的名字,重命名為highlightedStar。
  14. 在電腦上,選擇你想要添加的填充星星圖片。
  15. 拖拽這個圖片放到image set的2x插槽內。


    image: ../Art/ICC_highlightedstar_drag_2x.png

你的資源目錄看上去是這樣的。


image: ../Art/ICC_assetcatalog_final_2x.png

接下來,編寫代碼來在相應的時候為按鈕設置合適的圖片。

為按鈕設置星星圖片

  1. 在RatingControl.swift導航到setupButtons()方法,并且在創建按鈕的for-in循環的上面添加如下代碼:
// 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)

這些行從資源目錄加載星星圖片。注意資源目錄是在應用的主束(bundle)里。這意味著應用可以使用 UIImage(named:)方法加載圖片。但是,因為控件是@IBDesignable,所以代碼也需要運行在Interface Builder中。要讓圖片在Interface Builder中正確的加載,你必須明確指定目錄的束。這樣就確保系統能找到并加載圖片。

  1. 找到設置背景顏色的代碼行,并用下面的代碼進行替換。
// 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])

按鈕有五種不同狀態:normal(一般)、高亮(highlighted)、聚焦(focused)、選中(selected)、和禁用(disabled)。默認時,按鈕根據它的狀態來修改自身的顯示,例如,一個禁用的按鈕呈現灰色。一個按鈕可以在同時呈現多種狀態,例如按鈕即是禁用又是高亮。
按鈕總是從normal狀態開始(不是高亮、選中、聚焦、或者禁用)。無論何時用戶點擊時,按鈕是高亮。你也能用代碼設置按鈕是選中還是禁用。聚焦狀態使用在基于焦點的界面,例如Apple TV。在上面的代碼中,你告訴按鈕normal狀態下,使用空心星星圖片。這時按鈕默認的圖片。每當一個狀態或混合狀態沒有它們自己的圖片時,系統就會使用這個圖片(可能具有附加效果)。
接下來,上面的代碼為選中狀態設置了填充圖片。如果你用編碼的方式將按鈕設置為選中,它將從空心星星變為已填充星星。最后,為高亮狀態以及高亮和選中混合狀態都設置高亮圖片。當用戶點擊按鈕的時候,無論是否選中,系統都會顯示高亮按鈕圖片。

你的setupButtons()方法看上去是這樣了:

        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 _ 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
                
                // 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)
            }
        }

檢查點:運行應用。你將看到星星替代了紅色按鈕。點擊這里的任何按鈕讓然會調用ratingButtonTapped(_:)并且在控制臺上打印消息。當你點擊按鈕的時候甚至能看到高亮星星,但是你的按鈕還沒有變為填充圖片。你要接著修改。

image: ../Art/ICC_sim_emptystars_2x.png
image: ../Art/ICC_sim_emptystars_2x.png

實現按鈕動作

用戶需要能夠通過點擊星星來選擇評分,所以你要使用真正的代碼來代替ratingButtonTapped(_:) 方法中調試用的代碼。

實現評分動作

  1. 在RatingControl.swift中,找到ratingButtonTapped(button:)方法。
func ratingButtonTapped(button: UIButton) {
            print("Button pressed ??")
        }
  1. 用下面的代碼替換print語句:
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
            }
        }

在上面的代碼中,indexOf(:)方法嘗試在按鈕數組中找這個按鈕,并在找到后返回它在數組中的索引值。這個方法返回的是可選類型Int,因為你查找的對象在集合中可能不存在。但是,因為觸發該動作的唯一按鈕集是你創建的,如果indexOf(:)方法不能找到一個匹配的按鈕,那么代碼就有了嚴重的錯誤。拋出錯誤、終止應用,并在控制臺上打印有用的錯誤信息,幫助你在設計和測試應用時找到并修復錯誤。
一旦你有按鈕的索引的時候(這個值在0-4之間),你給索引加1來計算評分(就是1-5之間的值)。如果用戶點擊的星星恰好是當前的評分,你就重置控件的rating屬性為0。否則,你就設置rating值為選中的評分值。

  1. 一旦評分值被設置,你需要一些方法來更新按鈕的顯示。在RatingControl.swift中,在結束的花括號前(}),添加如下方法:
private func updateButtonSelectionStates() {
        }

這個輔助方法可以用來更新按鈕的選擇狀態。

  1. 在updateButtonSelectionStates()方法中,添加如下for-in循環:
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
            }
        }

這個代碼遍歷按鈕數組,并基于評分的位置對每個按鈕的選中狀態進行設置。就像你較早前看到的,選中狀態影響按鈕的呈現。如果按鈕的索引小于評分,則isSelected屬性設置為true,并且按鈕顯示填充的星星圖片。否則,isSelected屬性設置為false,按鈕顯示空心星星圖片。

  1. 添加一個屬性觀察器到rating屬性,當rating值改變時都會調用updateButtonSelectionStates()方法。
var rating = 0 {
            didSet {
                updateButtonSelectionStates()
            }
        }
  1. 每當按鈕添加到控件的時候,也需要更新按鈕的選擇狀態。在setupButtons()方法中,在方法結束的花括號(})之前添加 updateButtonSelectionStates()方法。現在setupButtons()方法看上去是這樣的:
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
                
                // 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()
        }

檢查點:運行應用。你應該看到五顆星星,點擊一顆就改變評分。例如,點擊第三顆星星把評分改為3。第二次點擊同一顆星星,控件將評分重置為零顆星。

image: ../Art/ICC_sim_filledstars_2x.png

添加輔助信息

借助iOS內置輔助功能,您可以為每個客戶(包括有特殊需求的客戶)提供出色的移動體驗。 這些功能包括VoiceOver,開關控制,隱藏式字幕或音頻描述視頻的回放,指導訪問,文本到語音等。

在大多數情況下,用戶從這些功能中獲得好處而無需任何額外的工作。然而,VoiceOver,通常需要一些額外的工作。VoiceOver是為盲人和低視力用戶提供的革命性屏幕閱讀功能。VoiceOver把用戶界面讀給用戶聽。盡管內置控件的默認描述提供了一個很好的開端,但是你可能需要優化用戶界面的顯示;特別是自定義視圖和控件。

  • 附加功能標簽(Accessibility label)。一個簡短的本地化單詞或短語,簡潔的描述這個控件或視圖,但是不能辨認元素的類型。例如“添加”或“播放”。
  • 附加功能值(Accessibility value)。一個元素的當前值,當該值不由標簽表示時。例如一個滑塊(slider)的標簽可能是“速度”,但它的當前值可能是“50%”。
  • 附加功能提示(Accessibility hint)。一個簡短的本地化短語,用來描述一個元素的動作的結果。例如“添加一個標題”或者“打開購物單”。

在rating控件中,每個按鈕的附加功能標簽描述了每個按鈕設置的值。例如,第一個按鈕標簽是“設置一個評分。”附加功能值包含了控件當前的評分。例如,如果你有一個4星的評分,這個值是“4星設置”。最后,你分配一個提示給當前選中的星星,“點擊重置評分為零。”所有其他星星的提示值為nil,因為它們的效果是已經被它們的標簽描述了。

添加附加功能標簽、值、和提示

  1. 在 RatingControl.swift中,導航到setupButtons()方法,找到for-in聲明。
for index in 0..<starCount {
  1. 在for-in循環內部,緊接著約束,添加如下代碼:
// Set the accessibility label
        button.accessibilityLabel = "Set \(index + 1) star rating"

這段代碼使用按鈕的所以計算標簽字符串,然后把它分配到按鈕的accessibilityLabel屬性。setupButtons()方法看上去是這樣的:

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()
        }
  1. 導航到updateButtonSelectionStates()方法。在for-in循環內部,緊挨著設置按鈕的isSelected屬性下面,添加如下代碼:
// 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

這里,你通過檢查按鈕是否是當前選中的按鈕開始。如果它是,你就分配一個提示。如果不是,你就設置按鈕的hintString屬性為nil。
接下來,你基于控件的評分計算值。使用switch語句,如果評分是0或1,則分配一個自定義字符串。如果評分大于1,你就使用字符串插值來計算提示內容。最后,分配這些值給accessibilityHint和accessibilityValue屬性。

當用戶在VoiceOver可用的環境里運行應用,當用戶點擊其中一個按鈕的時候,VoicePver就會閱讀這個按鈕的標簽,跟在單詞按鈕后面。然后讀附加功能值。最后它讀附加功能提示(如果有)。這讓用戶知道控件當前的值,以及按下當前的按鈕會有什么結果。

進一步探索
更多關于附加功能的信息,參見Accessibility on iOS.
還有,因為本課的目的,你只是分配了簡單的字符串給附加功能屬性;但是,一個產品級的應用應該使用本地化字符串。更多關于國際化和本地化的信息,參見Build Apps for the World。

連接Rating控件到View Controller

作為設置rating控件的最后一步,你需要把它的一個引用給ViewController。

連接rating控件到ViewController.swift

  1. 打開storyboard。
  2. 點擊Xcode工具條上的Assistant 按鈕來打開助理編輯器。


    image: ../Art/assistant_editor_toggle_2x.png
  3. 想要更大空間,就把project navigator和utility area折疊起來。


    image: ../Art/navigator_utilities_toggle_on_2x.png

    也可以把大綱視圖折疊起來。

  4. 選擇rating 控件。
    ViewController.swift顯示在右側的編輯器。(如果不是這樣,在編輯器選擇器欄里選擇 Automatic > ViewController.swift)。
  5. 把rating控件拖拽到photoImageView屬性的下面。


    image: ../Art/ICC_ratingcontrol_dragoutlet_2x.png
    image: ../Art/ICC_ratingcontrol_dragoutlet_2x.png
  6. 在彈出的對話框中,Name字段鍵入ratingControl。
    其他選項不變。你的對話框看上去是這樣的:


    image: ../Art/ICC_ratingcontrol_addoutlet_2x.png
  7. 點擊連接。

ViewController類現在有一個引用指向storyboard中的rating控件。

清理項目

你已接近完成菜品場景的用戶界面了,但在之前你需要做一些清理工作。現在這個FoodTracker應用實現了很多比之前課程更高級的行為和不同的用戶界面,你應該移除一些不再需要的部分。你還需要把元素放到棧視圖的中心,以平衡界面。

清理UI

  1. 返回到標準編輯器。


    image: ../Art/standard_toggle_2x.png
  2. 打開storyboard。
  3. 選擇Set Default Label Text按鈕,然后按下刪除鍵刪除它。
    棧視圖布置你的界面元素填充按鈕留下來的控件。


    image: ../Art/ICC_deletebutton_2x.png
  4. 如果必要,打開大綱視圖,選擇Stack View對象。


    image: ../Art/ICC_outlineview_2x.png
  5. 打開Attributes inspector
  6. 在Attributes inspector中,找到Alignment(對齊)字段并選擇Center。
    在棧視圖中的元素都居中對齊:


    image: ../Art/ICC_centerstack_2x.png

現在,移除和你刪掉的按鈕對應的action方法。

清理代碼

  1. 打開 ViewController.swift.
  2. 在ViewController.swift中,刪除setDefaultLabelText(_:) action方法。
@IBAction func setDefaultLabelText(sender: UIButton) {
            mealNameLabel.text = "Default Text"
        }

這就是現在你需要刪除的全部了。你將在下一課對label outlet(mealNameLabel)作出一些改變。

檢查點:運行應用。所有事都應該和之前一樣,只是沒有那個刪掉的按鈕了,并且元素都水平居中了。按鈕應該是并排的。點擊任何一個按鈕仍然調用ratingButtonTapped(_:),并且會恰當的改變按鈕的圖片。

重要
如果你運行出現構建問題,嘗試按下Command-Shift-K組合鍵來清理你的項目。

image: ../Art/ICC_sim_finalUI_2x.png

小結

在本課中,你學習了如何構建一個自定義控件,它能顯示在Interface Builder中。這個控件還會在Attributes inspector中顯示可修改的屬性。最后,你添加了附加功能信息,確保控件能很好的使用Voice Over。

下一課,你將設計和連接應用的數據模型。

注意
想看本課的完整代碼,下載這個文件并在Xcode中打開。
下載文件

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容