.mask()

重要觀念

  • 一般而言,當我們使用:「view.mask(mask)」這樣的語法時,我們是把 view 當作「要被剪裁的色紙」,而 mask 則是當作色紙「要被剪裁的形狀」,所以我們可以把 mask 想像成某種「裁刀鋼模」。

  • 但實際上,它背後運作的機制是「把 maskopacity 套用到 view 上面」,所以 mask 圖上的像素只要 opacity > 0 (前景),在它相對應的位置上的 view 的像素就會保留下來 (除非 view 上該點 opacity = 0)。反之,如果 mask 圖上的像素 opacity = 0 (背景),那麼 view 上相對應的點就會不見了,也就形同「被裁掉」了。

  • 在預設的情況下,view 與 mask 是對齊「左上角」。

  • ⚠️ 但注意:不管 mask 是文字或圖片,只要是設定了 .frame()、.scaledToFit()、.resizable() 等其中任何一個 (不知還有沒有其他的),馬上會變成「置中」對齊,而且這時 .frame() 所做的任何尺寸設定,完全不會改變原物件的大小,也就是對於 mask 來說,.frame() 除了可設定置中對齊外,似乎沒有任何作用

  • ⚠️ 注意:一個 mask 一旦設定了 .resizable() 或 .scaledToFit(),它就會縮放到它的 parent view 的大小置中對齊,不管你有沒有設定 .frame()‼️

  • 另外,對一般的 view 來說,做「縮放效應」(.scaleEffect) 的時候,預設情況下,是以自已的中心點為縮放中心,就算這個 view 當別人的 overlay 也是如此。但對於一個 mask 來說就不一樣了,不管 mask 是「置中對齊」還是「對齊左上角」,它的縮放中心都是「它的 parent view 的中心點」。

安排 mask 的位置

我們在上面的「重要觀念」有提到,就是:

  • 如果我們對一個 mask 設定了 .resizable() 或 .scaledToFit() 之後,它就會縮放到「整個」 parent view 的大小置中對齊,不管你有沒有設定 .frame()。

如果這樣,那麼問題就來了:

如果我們要安排 mask 在母視窗的位置,但又不希望它佔滿整個母視窗,那要怎麼辦❓

方法一:用 ZStack 來安排 mask 的位置

  1. 先用 ZStack 當作是 mask 的 container。

  2. 然後畫一個有「白色前景色」的 Rectangle

  3. 最後設定 mask大小位置 (如果是 ZStack 內會自動置中)。

⚠️ 注意:IconCard 📦 有修改過,所以下方的程式碼是舊版的。

import SwiftUI

public struct IconCard: View {
    
    let size: CGFloat       // icon size
    let icon: String        // icon name
    
    public init(icon: String, size: CGFloat = 32) {
        self.icon    = icon
        self.size    = size
    }
    
    // body
    public var body: some View {
        
        // 1. 選用 ZStack,讓物件可以在上下層間輕鬆堆疊。
        ZStack {
            
            // 2. 先準備一張白紙。
            Rectangle()                              // ⭐️ 注意:
                .foregroundColor(.white)             //    畫白色「前景色」,不是「背景色」!
            
            // 3. 然後在上面畫圖示,並設定大小。
            // ⭐️ 注意:雖然沒有指定前景色,所以在「深色模式」下,
            //    會用「白色」當圖的前景色,又因為下面是一張白紙,
            //    所以如果直接將這個 view 畫出來,在「深色模式」下,
            //    會整張都白白的,但如果拿來當「反面遮罩」的話,
            //    這個圖示還是會被「挖掉」喔!奇怪吧!
            Image(systemName: icon)                  
                .resizable()                         // ⭐️ 讓圖示可以自由縮放
                .scaledToFit()           
                .frame(width: size, height: size)    // ⭐️ 縮放到指定的大小
            
        } // container (ZStack)        // ⭐️ 先不限制 ZStack 的大小,它的大小由外部環境決定。
        
    } // body
}

Last updated