ฉันจะใช้มุมมองการแปลงเป็น UIView ได้อย่างไร


199

ฉันกำลังมองหาการเปลี่ยนมุมมองบน UIView (เช่นเห็นใน coverflow)

มีใครรู้บ้างไหมว่าสิ่งนี้เป็นไปได้หรือไม่?

ฉันได้ตรวจสอบการใช้งานCALayerและใช้งานพอดคาสต์ Core Animation ที่ใช้งานได้จริงทั้งหมด แต่ฉันยังไม่ชัดเจนเกี่ยวกับวิธีสร้างการแปลงรูปแบบนี้บน iPhone

ความช่วยเหลือคำแนะนำหรือตัวอย่างโค้ดจะได้รับการชื่นชมจริงๆ!


ฉันไม่แน่ใจว่าสิ่งนี้เหมาะสำหรับคุณหรือไม่ แต่เมื่อฉันสร้างแอนิเมชั่นฉันพบว่า m14 ดีกว่าสำหรับฉันเพียงเพื่อการอ้างอิง :)
b123400

ขอบคุณ! - คุณให้ค่าอะไร
Nick Cartwright

1
ด้วยการตั้งค่า m14 คุณจะบิดเบือนมุมมองที่แปลกประหลาดบนแกน X อย่างประหลาด คณิตศาสตร์นั้นถูกต้องสมบูรณ์ แต่มันจะทำให้เกิดความสับสนในกรณีทั่วไป
ปุย

บทความที่ดีมากเกี่ยวกับการแปลงและมุมมองของ CALayer 3D รวมถึงคำอธิบายอย่างละเอียดเกี่ยวกับฟิลด์ m34 สามารถพบได้ในบทความที่ยอดเยี่ยมนี้: core-animation-3d-model
Markus

คำตอบ:


329

ขณะที่เบนกล่าวว่าคุณจะต้องทำงานร่วมกับUIView'sชั้นโดยใช้การดำเนินการCATransform3D layer's rotationเคล็ดลับที่จะได้รับมุมมองการทำงานเป็นอธิบายในที่นี้คือการหนึ่งในการเข้าถึงโดยตรงของเมทริกซ์cellsของCATransform3D(M34) คณิตศาสตร์เมทริกซ์ไม่เคยเป็นของฉันดังนั้นฉันไม่สามารถอธิบายได้อย่างชัดเจนว่าทำไมสิ่งนี้ถึงได้ผล แต่มันก็เป็นเช่นนั้น คุณจะต้องตั้งค่านี้ให้เป็นเศษส่วนเชิงลบสำหรับการแปลงเริ่มต้นของคุณจากนั้นให้ใช้การแปลงเลเยอร์หมุนของคุณกับสิ่งนั้น คุณควรทำสิ่งต่อไปนี้:

Objective-C

UIView *myView = [[self subviews] objectAtIndex:0];
CALayer *layer = myView.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 45.0f * M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;

สวิฟท์ 5.0

if let myView = self.subviews.first {
    let layer = myView.layer
    var rotationAndPerspectiveTransform = CATransform3DIdentity
    rotationAndPerspectiveTransform.m34 = 1.0 / -500
    rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 45.0 * .pi / 180.0, 0.0, 1.0, 0.0)
    layer.transform = rotationAndPerspectiveTransform
}

ซึ่งสร้างเลเยอร์เปลี่ยนจากรอยขีดข่วนสำหรับการหมุนแต่ละครั้ง

ตัวอย่างเต็มของนี้ (พร้อมรหัส) สามารถพบได้ที่นี่ที่ฉันได้ใช้การหมุนแบบสัมผัสและปรับขนาดสองสามCALayersตามตัวอย่างโดย Bill Dudney โปรแกรมรุ่นใหม่ล่าสุดที่ด้านล่างสุดของหน้าใช้การดำเนินการของเปอร์สเปคทีฟชนิดนี้ รหัสควรอ่านง่ายพอสมควร

sublayerTransformคุณอ้างถึงในการตอบสนองของคุณคือการแปลงที่ใช้กับ sublayers UIView's CALayerของคุณ หากคุณไม่มีเลเยอร์ย่อยไม่ต้องกังวลกับมัน ฉันใช้ sublayerTransform ในตัวอย่างของฉันเพียงเพราะมีสองสิ่งCALayersอยู่ภายในเลเยอร์เดียวที่ฉันหมุน


1
ขอบคุณแบรด - คุณเป็นดารา ป.ล. : ขออภัยสำหรับคำตอบที่ยอดเยี่ยมของคุณ! กรงขัง
Nick Cartwright

มันใช้งานได้ดีสำหรับฉันยกเว้นฉันดูเหมือนจะเสียครึ่งภาพของฉัน (ตามแนวตั้ง) - บางครั้ง LHS บางครั้ง RHS
iPadDeveloper2011

18
@ iPadDeveloper2011 - อัตราต่อรองคือคุณกำลังพยายามหมุนมุมมองหรือเลเยอร์เมื่อมีมุมมองหรือเลเยอร์ทึบอื่นบนระนาบเดียวกัน ครึ่งหนึ่งของมุมมองหรือเลเยอร์ของคุณที่อยู่ห่างจากหน้าจอจะต่ำกว่ามุมมองอื่นและทำให้ถูกซ่อนอยู่ คุณสามารถจัดลำดับมุมมองใหม่ของคุณเพื่อป้องกันหรือใช้คุณสมบัติ zPosition เพื่อย้ายมุมมองเบื้องหน้าของคุณให้สูงพอที่จะตัดมุมมองออก
แบรดลาร์สัน

26
FYI เหตุผลที่เซลล์ m34 มีผลต่อการเปลี่ยนมุมมองคือเซลล์ในเมทริกซ์ที่มีผลต่อวิธีที่ค่า Z พื้นที่โลกแมปกับค่า W พื้นที่คลิป (ซึ่งใช้สำหรับการประมาณการมุมมอง) อ่านเพิ่มเติมเกี่ยวกับเมทริกซ์การแปลงเอกพันธ์สำหรับข้อมูลเพิ่มเติม
ปุย

2
@uerceg - คุณจะต้องถามว่าเป็นคำถามแยกต่างหากโดยเฉพาะอย่างยิ่งกับภาพที่แสดงสิ่งที่เกิดขึ้นและสิ่งที่คุณต้องการที่จะเกิดขึ้น
แบรดลาร์สัน

7

คุณสามารถใช้การแปลงคอร์กราฟิก (ควอตซ์, 2D เท่านั้น) เท่านั้นที่นำไปใช้โดยตรงกับคุณสมบัติการแปลงของ UIView ในการรับเอฟเฟกต์ใน coverflow คุณจะต้องใช้ CATransform3D ซึ่งใช้ในพื้นที่ 3 มิติและเพื่อให้มุมมองมุมมองที่คุณต้องการ คุณสามารถใช้ CATransform3D กับเลเยอร์เท่านั้นไม่ใช่มุมมองดังนั้นคุณจะต้องเปลี่ยนเป็นเลเยอร์สำหรับสิ่งนี้

ลองดูตัวอย่าง "CovertFlow" ที่มาพร้อมกับ Xcode เป็น mac เท่านั้น (ไม่ใช่สำหรับ iPhone) แต่มีคอนเซปต์จำนวนมากที่ถ่ายโอนได้ดี


ฉันกำลังมองหาตัวอย่างใบปะหน้านี้ใน / Developer / ตัวอย่างโดยไม่สำเร็จคุณจะหาได้ที่ไหน
อเล็กซ์

จริงๆแล้วมันเป็น "CovertFlow" และอยู่ใน / Developer / ตัวอย่าง / Quartz / Core Animation / "
Ben Gottlieb

3

เพื่อเพิ่มคำตอบของแบรดและเพื่อเป็นตัวอย่างที่เป็นรูปธรรมฉันยืมคำตอบของฉันเองในโพสต์อื่นในหัวข้อที่คล้ายกัน ในคำตอบของฉันฉันสร้างง่าย Swift สนามเด็กเล่นที่คุณสามารถดาวน์โหลดและเล่นกับที่ฉันแสดงให้เห็นถึงวิธีการที่จะสร้างผลกระทบที่มุมมองของ UIView ที่มีลำดับชั้นที่เรียบง่ายของเลเยอร์และฉันจะแสดงผลลัพธ์ที่แตกต่างระหว่างการใช้และtransform sublayerTransformลองดูสิ

นี่คือภาพบางส่วนจากโพสต์:

ตัวเลือก. sublayerTransform

ตัวเลือกเปลี่ยนรูปแบบ


ควรโพสต์รหัสเป็นข้อความ เป็นการยากที่จะอ่านรหัสในรูปภาพ รวมทั้งรหัสในรูปภาพไม่สามารถอ้างอิงคัดลอกหรือค้นหาได้
rmaddy

@ rmaddy ฉันมีรหัสอยู่ในลิงค์ Bitbucket ในคำตอบของฉัน
HuaTham

ลิงก์แย่มากเมื่อเวลาผ่านไป เป็นการดีที่สุดที่จะวางโค้ดสำคัญเป็นข้อความลงในคำตอบโดยตรง
rmaddy

-1

คุณสามารถรับเอฟเฟกต์ม้าหมุนได้อย่างแม่นยำโดยใช้ iCarousel SDK

คุณสามารถรับเอฟเฟกต์ Cover Flow บน iOS ได้ทันทีโดยใช้ไลบรารี่ iCarousel ที่ยอดเยี่ยมและฟรี คุณสามารถดาวน์โหลดได้จากhttps://github.com/nicklockwood/iCarouselและวางลงในโครงการ Xcode ของคุณได้อย่างง่ายดายโดยเพิ่มส่วนหัว bridging (เขียนใน Objective-C)

หากคุณยังไม่ได้เพิ่มรหัส Objective-C ให้กับโครงการ Swift มาก่อนให้ทำตามขั้นตอนเหล่านี้:

  • ดาวน์โหลด iCarousel และ unzip
  • ไปที่โฟลเดอร์ที่คุณคลายซิปเปิดโฟลเดอร์ย่อยของ iCarousel จากนั้นเลือก iCarousel.h และ iCarousel.m แล้วลากเข้าสู่การนำทางโครงการของคุณนั่นคือบานหน้าต่างด้านซ้ายใน Xcode ด้านล่าง Info.plist นั้นใช้ได้
  • ทำเครื่องหมาย "คัดลอกรายการหากจำเป็น" จากนั้นคลิกเสร็จสิ้น
  • Xcode จะแจ้งให้คุณทราบพร้อมข้อความ "คุณต้องการกำหนดค่าส่วนหัวการเชื่อมต่อ Objective-C หรือไม่" คลิก "สร้าง Bridging Header" คุณควรเห็นไฟล์ใหม่ในโครงการของคุณชื่อ YourProjectName-Bridging-Header.h
  • เพิ่มบรรทัดนี้ในไฟล์: #import "iCarousel.h"
  • เมื่อคุณเพิ่ม iCarousel ในโครงการของคุณคุณสามารถเริ่มใช้งานได้
  • ตรวจสอบให้แน่ใจว่าคุณได้ปฏิบัติตามทั้งโปรโตคอล iCarouselDelegate และ iCarouselDataSource

โค้ดตัวอย่าง Swift 3:

    override func viewDidLoad() {
      super.viewDidLoad()
      let carousel = iCarousel(frame: CGRect(x: 0, y: 0, width: 300, height: 200))
      carousel.dataSource = self
      carousel.type = .coverFlow
      view.addSubview(carousel) 
    }

   func numberOfItems(in carousel: iCarousel) -> Int {
        return 10
    }

    func carousel(_ carousel: iCarousel, viewForItemAt index: Int, reusing view: UIView?) -> UIView {
        let imageView: UIImageView

        if view != nil {
            imageView = view as! UIImageView
        } else {
            imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 128, height: 128))
        }

        imageView.image = UIImage(named: "example")

        return imageView
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.