開源一個線上專案 WeAre-AR相簿

夏琪發表於2018-06-08

WeAre - AR相簿

ARKit

線上地址 https://itunes.apple.com/cn/app/weare/id1304227680?mt=8

WeAre.gif

開源地址 https://github.com/SherlockQi/HeavenMemoirs 歡迎(跪求)Star!

WeAre

技術點

AR初始化 在新建專案時可以直接建立 AR 專案, xcode 會創造一個 AR 專案的模板.

也可以建立普通的專案,在需要實現 AR 功能的控制器中實現如下程式碼進行初始化.

      import ARKit
      let sceneView = ARSCNView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        sceneView.frame = view.bounds
        view.addSubview(sceneView)

        sceneView.delegate = self
        sceneView.showsStatistics = true
      
        // 建立一個場景,系統預設是沒有的
        let scene = SCNScene()
        sceneView.scene = scene

          //不允許使用者操作攝像機
         sceneView.allowsCameraControl = false
          //抗鋸齒
         sceneView.antialiasingMode = .multisampling4X

    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }
複製程式碼

新增節點

      //我使用的是 SCNPlane 來充當相框,也可以使用"厚度"很小的 SCNBox
      let photo = SCNPlane(width: 1, height: 1)
      //photo.cornerRadius = 0.01
      let image = UIImage(named: "0")
      //紋路可以使圖片,也可以是顏色
      photo.firstMaterial?.diffuse.contents = image
      //photo.firstMaterial?.diffuse.contents = UIColor.red
      let photoNode = SCNNode(geometry: photo)
      //節點的位置
      let vector3 = SCNVector3Make(-1, -1, -1) 
      photoNode.position = vector3
      sceneView.scene.rootNode.addChildNode(photoNode)
複製程式碼
      let text = SCNText(string: "文字", extrusionDepth: 0.1)
      text.font = UIFont.systemFont(ofSize: 0.4)
      let textNode = SCNNode(geometry: text)
      textNode.position = SCNVector3Make(0,0, -1)
      //文字的圖片/顏色
      text.firstMaterial?.diffuse.contents = UIImage(named: color)
      sceneView.scene.rootNode.addChildNode(textNode)
複製程式碼
可供選擇的幾何圖形
      SCNText 文字  
      SCNPlane 平面  
      SCNBox 盒子  
      SCNPyramid 錐形  
      SCNSphere 球  
      SCNCylinder 圓柱  
      SCNCone 圓錐  
      SCNTube 圓筒  
      SCNCapsule 膠囊  
      SCNTorus 圓環  
      SCNFloor 地板  
      SCNShape 自定義
複製程式碼

全景圖實現

      想象自己站在一個球的球心處,球的內表面塗著壁畫,那麼是不是就實現了全景圖.
      所以用一個Sphere 節點包裹著相機節點(也就是0位置節點),再設定Sphere節點的內表面紋理,就實現了功能.

      let sphere = SCNSphere(radius: 15)
      let sphereNode = SCNNode(geometry: sphere)
      sphere.firstMaterial?.isDoubleSided = true
      sphere.firstMaterial?.diffuse.contents = image
      sphereNode.position = SCNVector3Zero
      scene.rootNode.addChildNode(sphereNode)
複製程式碼

播放視訊

      let height:CGFloat = CGFloat(width) * videoSize.height/videoSize.width
      let box = SCNBox(width: width, height: height, length: 0.3, chamferRadius: 0)
      boxNode.geometry = box;
      boxNode.geometry?.firstMaterial?.isDoubleSided = true
      boxNode.position = SCNVector3Make(0, 0, -5);
      box.firstMaterial?.diffuse.contents = UIColor.red
      self.scene.rootNode.addChildNode(boxNode);
            
            
      let avplayer = AVPlayer(url: url)
      avplayer.volume = rescoucceConfiguration.video_isSilence ? 0.0 : 3.0
      videoPlayer = avplayer
      let videoNode = SKVideoNode(avPlayer: avplayer)
      NotificationCenter.default.addObserver(self, selector: #selector(playEnd(notify:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil)
            
      videoNode.size = CGSize(width: 1600, height: 900)
      videoNode.position = CGPoint(x: videoNode.size.width/2, y: videoNode.size.height/2)
            videoNode.zRotation = CGFloat(Float.pi)
      let skScene = SKScene()
      skScene.addChild(videoNode)
      skScene.size = videoNode.size
      box.firstMaterial?.diffuse.contents = skScene
      videoNode.play()
複製程式碼

粒子效果

      /*
        particleName = "bokeh.scnp"
        particleName = "rain.scnp"
        particleName = "confetti.scnp"
        **/
      particleSytem = SCNParticleSystem(named: particleName, inDirectory: nil){
      particleNode.addParticleSystem(particleSytem)
      particleNode.position = SCNVector3Make(0, Y, 0)
      self.scene.rootNode.addChildNode(particleNode)
複製程式碼

節點點選事件

      //給 場景檢視sceneView 新增點選事件
      let tap = UITapGestureRecognizer(target: self, action: #selector(tapHandle(gesture:)))
      sceneView.addGestureRecognizer(tap)
複製程式碼
  @objc func tapHandle(gesture:UITapGestureRecognizer){
      let results:[SCNHitTestResult] = (self.sceneView?.hitTest(gesture.location(ofTouch: 0, in: self.sceneView), options: nil))!
      guard let firstNode  = results.first else{
            return
      }
        // 這就是點選到的節點 可以對他做一些事情 或者根據這個節點的某些屬性執行不同的方法
      let node = firstNode.node.copy() as! SCNNode
      if firstNode.node == self.selectNode {
            ...推遠照片...
      }else{
            ...拉近照片...
            selectNode = node
      }
}
複製程式碼

節點動畫 我的另一篇文章中有詳細記錄ARKit-動畫 //拉近(推遠)照片

      //這只是其中一種方法
      let newPosition  = SCNVector3Make(firstNode.node.worldPosition.x*2, firstNode.node.worldPosition.y*2, firstNode.node.worldPosition.z*2)
      let comeOut = SCNAction.move(to: newPosition, duration: 1.2)
      firstNode.node.runAction(comeOut)
複製程式碼

自傳/公轉

      //自轉
      let box = SCNBox(width: boxW, height: boxW, length: boxW, chamferRadius: 0)
      let boxNode = SCNNode(geometry: box)
      boxNode.position = vector3
      let emptyNode = SCNNode()
      emptyNode.position = SCNVector3Zero
      emptyNode.rotation = SCNVector4Make(0, 1, 0, Float.pi/Float(L/2) * Float(index))
      emptyNode.addChildNode(boxNode)
      photoRingNode.addChildNode(emptyNode)
      let ringAction = SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: right, z: 0, duration: 2))
      boxNode.runAction(ringAction)
            //公轉 把節點加到一個正在自傳的節點上就可以了
複製程式碼

錄屏 錄屏是使用ReplayKit完成的 開始錄屏

      協議      
      RPScreenRecorderDelegate,RPPreviewViewControllerDelegate

      RPScreenRecorder.shared().startRecording(handler: nil)
      RPScreenRecorder.shared().delegate = self
複製程式碼

錄製代理

func screenRecorder(_ screenRecorder: RPScreenRecorder, didStopRecordingWith previewViewController: RPPreviewViewController?, error: Error?) {
        print(error ?? "error")
        if error != nil{
            print("error:", error ?? "")
            DispatchQueue.main.async {
                let string = error?.localizedDescription
                ITTPromptView .showMessage(string, andFrameY: 0)
                print(string ?? "")
                //錄製期間失敗
                self.showFailReplay()
            }
        }else{
            print("else")
        }
        print("start recording handler")
    }
    //錄製失敗
    func showFailReplay(){
        let sb = UIStoryboard(name: "Main", bundle: nil)
        let vc = sb.instantiateViewController(withIdentifier: "HKExplainViewController")
        self.navigationController?.pushViewController(vc, animated: true)
        self.replayButtonRight.constant = 85;
        for button in self.smailButtons {
            button.alpha = 1
        }
        self.mainButton.alpha = 1
        UIView.animate(withDuration: 2.5) {
            self.stopReplayButton.alpha = 0
            self.view.layoutIfNeeded()
        }
    }
複製程式碼

結束並彈出預覽控制器

      RPScreenRecorder.shared().stopRecording { (vc, erroe) in
            vc?.previewControllerDelegate = self
            vc?.title = "We Are"
            self.present(vc!, animated: true, completion: nil)
      }
複製程式碼

預覽控制器的代理

    func previewController(_ previewController: RPPreviewViewController, didFinishWithActivityTypes activityTypes: Set<String>) {
        print(activityTypes)
        //取消
        if activityTypes.count == 0 {
            previewController.dismiss(animated: true, completion: nil)
        }
        //儲存
        if activityTypes.contains("com.apple.UIKit.activity.SaveToCameraRoll"){
            ITTPromptView .showMessage("視訊已儲存在相簿", andFrameY: 0)
            previewController.dismiss(animated: true, completion: nil)
            //檢測到您剛剛儲存了視訊 是否想要分享
            let delay = DispatchTime.now() + .seconds(2)
            DispatchQueue.main.asyncAfter(deadline: delay) {
                self.outputVideo()
            }
        }
    }
複製程式碼

歡迎(跪求)Star!

相關文章