ในระหว่างการประชุมเชิงปฏิบัติการ SwiftUI เรามักจะสังเกตเห็นว่ามีคนเพียงไม่กี่คนที่รู้เกี่ยวกับการเปลี่ยนภาพ แม้ว่าพวกเขาจะไม่ซับซ้อนและมีประโยชน์อย่างเหลือเชื่อ
การเปลี่ยนภาพเกิดขึ้นเมื่อมุมมองถูกลบออกจากแผนผังมุมมอง หรือเพิ่มลงในแผนผังมุมมอง อย่างไรก็ตาม หากคุณทำ SwiftUI เสร็จแล้ว คุณจะสังเกตเห็นว่าไม่มีทางจริงในการเพิ่มมุมมองให้กับแผนผังมุมมอง — ไม่มี addSubview(_:)
. คุณสามารถเพิ่มและลบมุมมองได้ผ่านการรวมกันของการเปลี่ยนแปลงสถานะและการใช้ an . เท่านั้น if
คำสั่ง (หรือ switch
หรือ ForEach
). กล่าวอีกนัยหนึ่ง มุมมองจะถูกเพิ่มและลบให้เราโดยอัตโนมัติ แต่การเปลี่ยนจะทำงานเพียงครั้งเดียว ก่อนที่เราจะลงรายละเอียดในเรื่องนี้ เรามาพิจารณาการเปลี่ยนแปลงง่ายๆ ก่อน:
struct ContentView: View {
@State var visible = false
var body: some View {
VStack {
Toggle("Visible", isOn: $visible)
if visible {
Text("Hello, world!")
}
}
.animation(.default, value: visible)
}
}
เมื่อเราเรียกใช้โค้ดข้างต้น เราจะเห็นข้อความค่อยๆ เข้าและออก นี่คือการเปลี่ยนแปลงเริ่มต้น (.opacity
). เมื่อมุมมองถูกแทรกลงในแผนผังมุมมอง มุมมองจะค่อยๆ จางลง และเมื่อนำออกแล้วจะค่อยๆ จางหายไป โปรดทราบว่าหาก body
ดำเนินการอีกครั้ง มุมมองจะไม่จางหายไปอีกเว้นแต่เงื่อนไขใน if
การเปลี่ยนแปลงคำสั่ง
เพื่อสร้างแบบจำลองทางจิตของสิ่งที่เกิดขึ้น เราสามารถพิจารณาแผนผังมุมมอง SwiftUI สำหรับมุมมองด้านบน:
มุมมอง SwiftUI นั้นชั่วคราว: เนื้อหาของ ContentView
ถูกประหารชีวิตและจากมัน a เรนเดอร์ต้นไม้ ถูกสร้างขึ้น แผนผังการแสดงผลนี้จะคงอยู่ตลอดการอัปเดตมุมมอง และแสดงถึงมุมมองจริงบนหน้าจอ เมื่อต้นไม้แสดงผลได้รับการปรับปรุง ค่าสำหรับ body
แล้วจากไป นี่คือแผนผังการแสดงผลหลังจากการเรนเดอร์ครั้งแรก:
เมื่อเราแตะสวิตช์ จะเกิดการเปลี่ยนแปลงสถานะและเนื้อความของ ContentView
ดำเนินการอีกครั้ง ต้นไม้แสดงผลที่มีอยู่แล้วคือ อัพเดท. ในกรณีนี้ SwiftUI สังเกตว่า if
สภาพเปลี่ยนจาก false
ถึง true
และมันจะแทรกของเรา Text
ดูต้นไม้แสดงผล:
การเปลี่ยนแปลงในแผนผังการแสดงผลคือสิ่งที่ทำให้เกิดการเปลี่ยนแปลง การเปลี่ยนภาพจะเคลื่อนไหวก็ต่อเมื่อธุรกรรมปัจจุบันมีภาพเคลื่อนไหว ในตัวอย่างข้างต้น .animation
การโทรทำให้เกิดการเปลี่ยนแปลงเพื่อเคลื่อนไหว
แผนผังการแสดงผลไม่มีอยู่จริงกับชื่อหรือแบบฟอร์มนั้น แต่เป็นเพียงแบบจำลองสำหรับการทำความเข้าใจวิธีการทำงานของ SwiftUI เราไม่แน่ใจอย่างสมบูรณ์ว่าสิ่งเหล่านี้แสดงอย่างไรภายใต้ประทุน
เมื่อเราเปลี่ยนทัศนะเป็น if/else
เงื่อนไข สิ่งต่าง ๆ น่าสนใจขึ้นเล็กน้อย นี่คือรหัส:
struct ContentView: View {
@State var visible = false
var body: some View {
VStack {
Toggle("Visible", isOn: $visible)
if visible {
Text("Hello, world!")
} else {
Image(systemName: "hand.wave")
}
}
.animation(.default, value: visible)
}
}
เมื่อเราสร้างโครงสร้างมุมมองเริ่มต้น มันจะประกอบด้วย a VStack
กับ Toggle
และ Text
. เมื่อสถานะเปลี่ยนจาก false
ถึง true
ข้อความจะถูกแทนที่ด้วยรูปภาพ ในแผนผังมุมมองชั่วคราวจะมี Text
หรือ Image
, ไม่เคยทั้งสองอย่าง อย่างไรก็ตาม ในแผนผังการแสดงผล ในระหว่างแอนิเมชัน ต้นไม้จะมี ทั้งสองมุมมอง:
เนื่องจากเราใช้การเปลี่ยนค่าเริ่มต้น ดูเหมือนว่าข้อความจะค่อยๆ จางลงในรูปภาพและย้อนกลับ อย่างไรก็ตาม คุณอาจมองว่าเป็นช่วงการเปลี่ยนภาพแยกกัน: ข้อความมีช่วงการเปลี่ยนภาพลบออก (เฟดออก) และภาพมีการเปลี่ยนแทรก (เฟดอิน)
เราไม่ จำกัด เฉพาะการเปลี่ยนการจางหายเริ่มต้น ตัวอย่างเช่น นี่คือการเปลี่ยนที่เลื่อนเข้ามาจากขอบชั้นนำเมื่อแทรกมุมมอง และเอามุมมองออกโดยการลดขนาดลง:
let transition = AnyTransition.asymmetric(insertion: .slide, removal: .scale)
จากนั้นเราสามารถรวมเข้ากับ an .opacity
(จาง) การเปลี่ยนแปลง ดิ .combined
โอเปอเรเตอร์รวมทรานซิชันทั้งสองแบบขนานกันเพื่อให้ได้เอฟเฟกต์ต่อไปนี้:
let transition = AnyTransition.asymmetric(insertion: .slide, removal: .scale).combined(with: .opacity)
VStack {
Toggle("Visible", isOn: $visible)
if visible {
Text("Hello, world!")
.transition(transition)
} else {
Text("Hello world!")
.transition(transition)
}
}
.animation(.default.speed(0.5), value: visible)
โปรดทราบว่าในตัวอย่างข้างต้น เราใช้ a visible
ค่าที่จะสลับไปมาระหว่างสอง Text
ถึงแม้ว่าพวกเขาจะเหมือนกันก็ตาม เราสามารถลดความซับซ้อนของโค้ดได้เล็กน้อยโดยใช้ id(_:)
. เมื่อไหร่ก็ตามที่คุณค่าที่เราส่งต่อไปยัง id
การเปลี่ยนแปลง SwiftUI ถือว่านี่เป็นมุมมองใหม่ในแผนผังการแสดงผล เมื่อเรารวมสิ่งนี้เข้ากับความรู้เกี่ยวกับการเปลี่ยนภาพ เราสามารถทำให้เกิดการเปลี่ยนแปลงได้โดยการเปลี่ยน id
ของมุมมอง ตัวอย่างเช่น เราสามารถเขียนตัวอย่างด้านบนใหม่ได้:
let transition = AnyTransition.asymmetric(insertion: .slide, removal: .scale).combined(with: .opacity)
VStack {
Toggle("Visible", isOn: $visible)
Text("Hello, world!")
.id(visible)
.transition(transition)
}
.animation(.default.speed(0.5), value: visible)
ก่อนที่แอนิเมชั่น จะมีข้อความ และระหว่างแอนิเมชั่น มุมมองที่แทรกใหม่ (ด้วย id(false)
) ถูกเปลี่ยน และมุมมองเก่า (ด้วย id(true)
) ถูกเปลี่ยนออก กล่าวอีกนัยหนึ่ง: มุมมองทั้งสองมีอยู่ในแอนิเมชัน:
เมื่อทรานซิชันในตัวไม่ครอบคลุมความต้องการของคุณ คุณสามารถสร้างทรานซิชันแบบกำหนดเองได้ มี .modifier(active:identity)
การเปลี่ยนแปลง เมื่อมุมมองไม่เปลี่ยน identity
ตัวแก้ไขถูกนำไปใช้ เมื่อลบมุมมอง ภาพเคลื่อนไหวจะแทรกระหว่าง identity
ตัวดัดแปลงและ active
ตัวแก้ไขก่อนลบมุมมองทั้งหมด ในทำนองเดียวกัน เมื่อแทรกมุมมอง มุมมองจะเริ่มต้นด้วยตัวแก้ไขที่ทำงานอยู่เมื่อเริ่มต้นภาพเคลื่อนไหว และจบลงด้วยตัวแก้ไขข้อมูลประจำตัวที่ส่วนท้ายของภาพเคลื่อนไหว
ต่อไปนี้คือตัวอย่างปุ่มโปรดพร้อมการเปลี่ยนแบบกำหนดเอง นี่ไม่ใช่การใช้งานที่สมบูรณ์แบบ (เราจะไม่ฮาร์ดโค้ดออฟเซ็ตและความกว้างของปุ่ม) แต่มันแสดงให้เห็นว่าเป็นไปได้อย่างไร:
รหัสเต็มมีให้เป็นส่วนสำคัญ
บางครั้งเมื่อทำการเปลี่ยนแปลง คุณอาจเห็นผลข้างเคียงที่ไม่คาดคิด ในกรณีของเรา เรามักจะสามารถแก้ไขปัญหาเหล่านี้ได้โดยการตัดมุมมองที่เรากำลังเปลี่ยนภายในคอนเทนเนอร์ (ตัวอย่างเช่น VStack
หรือ ZStack
). ซึ่งจะเพิ่ม “ความเสถียร” บางอย่างให้กับแผนผังมุมมองที่สามารถช่วยป้องกันความบกพร่องได้
โดยพื้นฐานแล้ว การเปลี่ยนภาพไม่ซับซ้อนมากนัก อย่างไรก็ตาม การบรรลุผลที่คุณต้องการอาจเป็นเรื่องยากในบางครั้ง เพื่อให้ทำงานกับทรานซิชันได้อย่างมีประสิทธิภาพ คุณต้องเข้าใจความแตกต่างระหว่างแผนผังมุมมองและแผนผังการแสดงผล และเมื่อคุณต้องการมีทรานซิชันแบบกำหนดเอง คุณต้องเข้าใจวิธีการทำงานของแอนิเมชั่นด้วย เราครอบคลุมเรื่องนี้ทั้งในเวิร์กชอปและหนังสือ Thinking in SwiftUI ของเรา
หากบริษัทของคุณสนใจเวิร์กช็อปเกี่ยวกับ SwiftUI โปรดติดต่อ