SwiftUI:View mask

SwiftUI:View clipped
中闺阱,我們已經(jīng)探索了所有可以將剪輯蒙版應(yīng)用到視圖的方法诡壁。雖然剪輯功能強(qiáng)大,但它有兩個顯著的限制:

  • 它要求shape作為mask
  • 內(nèi)容要么被遮罩阅束,要么被修剪掉;沒有灰色地帶

讓我們探索一下超越剪輯的SwiftUI遮罩mask践美。

fadevscircle2.png

Mask

SwiftUI提供的最后一個蒙版視圖修飾符是mask(alignment:_:):

extension View {
  @inlinable public func mask<Mask: View>(
    alignment: Alignment = .center, 
    @ViewBuilder _ mask: () -> Mask
  ) -> some View
}

除了命名之外茴丰,這個修飾符聲明和其他一些我們可能非常熟悉的視圖修飾符是一樣的困介,overlay(alignment:_:)background(alignment:_:):

extension View {
  @inlinable public func overlay<V: View>(
    alignment: Alignment = .center, 
    @ViewBuilder content: () -> V
  ) -> some View

  @inlinable public func background<V: View>(
    alignment: Alignment = .center, 
    @ViewBuilder content: () -> V
  ) -> some View
}

這不是巧合.mask(alignment:_:)定位它的蒙版就像一個overlay覆蓋或background背景修改器一樣:

  • 修飾符將它應(yīng)用到的視圖的自然大小建議給它的mask
  • 一旦mask大小已知,它將根據(jù)指定的alignment對齊方式放置在視圖上

Mask alignment

當(dāng)蒙版和原始視圖有不同的尺寸時铃诬,對齊參數(shù)特別有用祭陷。在下面的例子中,蒙版是它應(yīng)用到的視圖的30%大小:

alignment.gif
struct FSView: View {
  private let alignments: [Alignment] = [
    .center, .leading, .trailing, .top, .bottom, .topLeading, .topTrailing, .bottomLeading, .bottomTrailing
  ]
  @State var alignment: Alignment = .center

  var body: some View {
    VStack {
      Color.yellow
        .frame(width: 200, height: 200)
        .mask(alignment: alignment) {
          Rectangle()
            .frame(width: 60, height: 60) // ???? 60 x 60 is smaller than 200x200
        }
        .border(.red)

      Button("Random alignment") {
        withAnimation {
          alignment = alignments.filter { $0 != alignment } .randomElement()!
        }
      }
    }
  }
}

紅色邊框顯示了原始視圖的邊界趣席,以提供視覺幫助:否則兵志,我們只能看到一個小矩形。

視圖作為蒙版

clipping剪輯修飾符的真正力量在于有機(jī)會使用任何View視圖作為遮罩吩坝。比如說Text呢?

text.png
Color.yellow
  .frame(width: 200, height: 200)
  .mask {
    Text("MASK")
      .fontWeight(.black)
      .font(.system(size: 60))
  }
  .border(Color.red)

不像shape形狀毒姨,視圖不會停留在它們所應(yīng)用的視圖的自然大小內(nèi)。因此钉寝,遮罩會導(dǎo)致內(nèi)容溢出弧呐。

在下面的例子中:

  • 視圖內(nèi)容擴(kuò)展到300x300的矩形
  • 視圖大小設(shè)置200x200
  • 應(yīng)用的遮罩超出了視圖邊界,允許內(nèi)容溢出
bleed.png
Color.yellow
  .frame(width: 300, height: 300)
  .frame(width: 200, height: 200)
  .mask {
    Text("MASK")
      .fontWeight(.black)
      .font(.system(size: 80))
      .fixedSize() // ???? 忽略建議的200x200的大小
  }
  .border(Color.red)

Opacity

mask(alignment:_:)使用蒙版不透明度來確定從原始視圖中顯示的內(nèi)容嵌纲,例如:

gradient.png
Color.yellow
  .frame(width: 200, height: 200)
  .mask {
    LinearGradient(colors: [.clear, .black, .clear], startPoint: .leading, endPoint: .trailing)
  }
  .border(Color.red)

在這里俘枫,我們使用帶有三個顏色的線性梯度。中間的漸變顏色并不重要逮走。它的顏色不透明度:我們可以用.white鸠蚪,.red等等來替換。結(jié)果是一樣的。

Blending混合

當(dāng)我們將視圖堆疊在其他視圖之上時茅信,底下的視圖被上面的視圖所隱藏盾舌。我們可以通過不透明度或使用不同大小的視圖來影響這一點(diǎn),但這個想法仍然有缺陷:底部的視圖總是會有一些部分被上面的視圖隱藏(或半隱藏)蘸鲸。

一種打破這種標(biāo)準(zhǔn)行為的強(qiáng)大技術(shù)是混合blending妖谴,它允許我們使用不同的視圖屬性(色值、不透明度酌摇、亮度等等)來組成最終的堆棧外觀膝舅。

混合模式有很多種,目前窑多,讓我們更多地關(guān)注目標(biāo)輸出混合模式仍稀,即BlendMode.destinationOut

在混合模式下埂息,source是頂部視圖技潘,而destination是底部視圖。

輸出后千康,最終視圖是底部視圖(destination)的位崭篡,它不與頂部視圖(source)重疊。

這里有一個例子吧秕,目標(biāo)destination是一個Rectangle琉闪,源source是一個Circle,兩者的大小相同:

rectanglevscircle1.png
ZStack {
  Rectangle() // destination
  Circle()    // source
    .blendMode(.destinationOut)
}
.compositingGroup()
.border(.red)

如果我們現(xiàn)在反轉(zhuǎn)兩個視圖砸彬,ZStack將是空白不會繪制任何東西颠毙,因?yàn)?code>Rectangle(source)完全重疊在Circle(目標(biāo)destination)上

rectanglevscircle2.png
ZStack {
  Circle()    // destination
  Rectangle() // source
    .blendMode(.destinationOut)
}
.compositingGroup()
.border(.red)

mask(alignment:_:)視圖修改器類似,blendMode(.destinationout)使用每個視圖不透明度來決定最終輸出砂碉。下面是和之前一樣的例子蛀蜜,我們用漸淡漸變替換Rectangle矩形:

fadevscircle1.png
ZStack {
  LinearGradient(
    colors: [.clear, .black], 
    startPoint: .leading, endPoint: .trailing
  )           // destination
  Circle()    // source
    .blendMode(.destinationOut)
}
.compositingGroup()
.border(.red)
fadevscircle2.png
ZStack {
  Circle()    // destination
  LinearGradient(
    colors: [.clear, .black], 
    startPoint: .leading, endPoint: .trailing
  )           // source
  .blendMode(.destinationOut)
}
.compositingGroup()
.border(.red)

我們可以使用目標(biāo)輸出混合技術(shù)來創(chuàng)建反向蒙版。

構(gòu)建反向蒙版修飾器

首先增蹭,我們想保持與mask(alignment:_:)修飾符相同的API滴某,這是它看起來的樣子:

extension View {
  @inlinable
  public func reverseMask<Mask: View>(
    alignment: Alignment = .center, 
    @ViewBuilder _ mask: () -> Mask
  ) -> some View
}

接下來,我們知道mask(alignment:_:)的工作原理是只顯示蒙版不透明度的原始視圖滋迈,現(xiàn)在我們想做相反的事霎奢。讓我們先用一個普通的蒙版:

extension View {
  @inlinable
  public func reverseMask<Mask: View>(
    alignment: Alignment = .center,
    @ViewBuilder _ mask: () -> Mask
  ) -> some View {
    self.mask {
      Rectangle()
    }
  }
}

通過在蒙版修改器中傳入Rectangle(),我們獲得了與.clipped()相同的效果:我們不再允許內(nèi)容溢出饼灿,但原始內(nèi)容仍然在視圖范圍內(nèi)可見幕侠。

接下來,我們想要應(yīng)用.destinationOut混合模式碍彭,我們的蒙版作為源晤硕,clipping rectangle剪切矩形作為目標(biāo):

// ?? this is not the final code.
extension View {
  @inlinable
  public func reverseMask<Mask: View>(
    alignment: Alignment = .center,
    @ViewBuilder _ mask: () -> Mask
  ) -> some View {
    self.mask {
      ZStack {
        Rectangle() // destination
        mask()      // source
          .blendMode(.destinationOut)
      }
    }
  }
}

多虧了ZStack悼潭,我們應(yīng)用了相同的效果在上面的混合章節(jié)中覆蓋,然后使用結(jié)果作為常規(guī)蒙版的輸入舞箍,獲得一個反向蒙版舰褪。

最后,我們想要遵守alignment參數(shù)疏橄,最好的方法抵知,就是用一個比他們應(yīng)用到視圖的尺寸更大的視圖處理蒙版,可以運(yùn)用overlay疊加到我們的Rectangle:

extension View {
  @inlinable
  public func reverseMask<Mask: View>(
    alignment: Alignment = .center,
    @ViewBuilder _ mask: () -> Mask
  ) -> some View {
    self.mask {
      Rectangle()
        .overlay(alignment: alignment) {
          mask()
            .blendMode(.destinationOut)
        }
    }
  }
}

把它應(yīng)用到我們之前的代碼中:

star.png
HStack {
  Color.yellow
    .frame(width: 200, height: 200)
    .mask {
      Star()
    }
    .border(.red)

  Color.yellow
    .frame(width: 200, height: 200)
    .reverseMask {
      Star()
    }
    .border(.red)
}
text_blend.png
HStack {
  Color.yellow
    .frame(width: 200, height: 200)
    .mask {
      Text("MASK")
        .font(.system(size: 60).weight(.black))
    }
    .border(.red)

  Color.yellow
    .frame(width: 200, height: 200)
    .reverseMask {
      Text("MASK")
        .font(.system(size: 60).weight(.black))
    }
    .border(.red)
}
fade.png
HStack {
  Color.yellow
    .frame(width: 200, height: 200)
    .mask {
      LinearGradient(
        colors: [.clear, .black],
        startPoint: .leading, endPoint: .trailing
      )
    }
    .border(.red)
  
  Color.yellow
    .frame(width: 200, height: 200)
    .reverseMask {
      LinearGradient(
        colors: [.clear, .black],
        startPoint: .leading, endPoint: .trailing
      )
    }
    .border(.red)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末软族,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子残制,更是在濱河造成了極大的恐慌立砸,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件初茶,死亡現(xiàn)場離奇詭異颗祝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)恼布,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進(jìn)店門螺戳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人折汞,你說我怎么就攤上這事倔幼。” “怎么了爽待?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵损同,是天一觀的道長。 經(jīng)常有香客問我鸟款,道長膏燃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任何什,我火速辦了婚禮组哩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘处渣。我一直安慰自己伶贰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布罐栈。 她就那樣靜靜地躺著幕袱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悠瞬。 梳的紋絲不亂的頭發(fā)上们豌,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天涯捻,我揣著相機(jī)與錄音,去河邊找鬼望迎。 笑死障癌,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辩尊。 我是一名探鬼主播涛浙,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼摄欲!你這毒婦竟也來了轿亮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤胸墙,失蹤者是張志新(化名)和其女友劉穎我注,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體迟隅,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡但骨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了智袭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奔缠。...
    茶點(diǎn)故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吼野,靈堂內(nèi)的尸體忽然破棺而出校哎,到底是詐尸還是另有隱情,我是刑警寧澤瞳步,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布贬蛙,位于F島的核電站,受9級特大地震影響谚攒,放射性物質(zhì)發(fā)生泄漏阳准。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一馏臭、第九天 我趴在偏房一處隱蔽的房頂上張望野蝇。 院中可真熱鬧,春花似錦括儒、人聲如沸绕沈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乍狐。三九已至,卻和暖如春固逗,著一層夾襖步出監(jiān)牢的瞬間浅蚪,已是汗流浹背藕帜。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惜傲,地道東北人洽故。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像盗誊,于是被迫代替她去往敵國和親时甚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容