[MACRO] AR-Kit Code Keep

2022. 11. 7. 02:40ใ†์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

 

 

 

1. UniverseSearchViewController+DataSource.swift

//
//  UniverseSearchViewController+DataSource.swift
//  Tars
//
//  Created by ๊น€์†Œํ˜„ on 2022/11/04.
//

import UIKit

extension UniverseSearchViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return planetList.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SelectPlanetCollectionViewCell.identifier, for: indexPath) as? SelectPlanetCollectionViewCell
            else { return UICollectionViewCell() }
        
        // reusable cell init
        cell.backgroundView = nil
        cell.planetNameLabel.textColor = .white
        
        let selectedPlanetName = planetList[indexPath.row].planetName
        let selectedPlanetImage = planetList[indexPath.row].planetImage
        
        cell.planetNameLabel.text = selectedPlanetName
        cell.planetImageView.image = selectedPlanetImage
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard let cell = collectionView.cellForItem(at: indexPath) as? SelectPlanetCollectionViewCell else { return }
        
        // dequeueReusableCell -> cellForItem ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋‹ˆ ์ž˜ ์ ์šฉ๋จ! :)
        // ๋‘˜์˜ ์ฐจ์ด ์ œ๋Œ€๋กœ ์กฐ์‚ฌํ•ด์„œ ์ •๋ฆฌํ•˜๊ธฐ (cell์„ ํŠน์ •ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค๋Š” ํฌ์ธํŠธ)
        
//        let selectedPlanet = planetList[indexPath.row]
                
        if cell.isSelected {
            DispatchQueue.main.async {
                cell.planetNameLabel.textColor = .black
                cell.backgroundView = cell.planetBackgroundView
                self.navigationController?.navigationItem.title = "์ฒœ์ฒด ํƒ์ƒ‰ ์ค‘"
                self.navigationController?.navigationBar.backgroundColor = .systemYellow
            }
        }
    }

    func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
        if let cell = collectionView.cellForItem(at: indexPath) as? SelectPlanetCollectionViewCell {
            cell.planetNameLabel.textColor = .white
            cell.backgroundView = nil
        }
    }
}

 

 

 

 

 

2. UniverseSearchViewController.swift

//
//  UniverseSearchViewController.swift
//  Tars
//
//  Created by ๊น€์†Œํ˜„ on 2022/11/02.
//

import UIKit
import SceneKit
import ARKit

class UniverseSearchViewController: UIViewController, ARSCNViewDelegate, LocationManagerDelegate {

    public var guideCircleView = CustomCircleView()
    public var selectedSquareView = CustomSquareView()
    let contentsViewController = ContentsViewController()
    
    var planetObjectList: [SCNNode] = []
    
    let searchGuideLabel: UILabel = {
        let label: UILabel = UILabel()
        label.text = "๋น ๋ฅด๊ฒŒ ์ฒœ์ฒด ์ฐพ๊ธฐ"
        label.textColor = .white
        label.textAlignment = .center
        label.font = UIFont.systemFont(ofSize: 20, weight: .semibold)
        return label
    }()
    
    public let selectPlanetCollectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.minimumLineSpacing = screenWidth * 0.05
        layout.minimumInteritemSpacing = CGFloat(UInt16.max)
        
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.register(SelectPlanetCollectionViewCell.self,
                                forCellWithReuseIdentifier: SelectPlanetCollectionViewCell.identifier)
        collectionView.contentInset = UIEdgeInsets(top: 0, left: screenWidth * 0.09, bottom: 0, right: screenWidth * 0.09)
        collectionView.showsHorizontalScrollIndicator = true
        collectionView.backgroundColor = .black
        
        return collectionView
    }()
    
    /// ARKit ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ view ์„ ์–ธ
    lazy var sceneView: ARSCNView = {
        let sceneView = ARSCNView()
        sceneView.delegate = self
        return sceneView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        selectPlanetCollectionView.delegate = self
        selectPlanetCollectionView.dataSource = self
        
        [guideCircleView, sceneView, selectedSquareView, searchGuideLabel, selectPlanetCollectionView].forEach { view.addSubview($0) }
        sceneView.addSubview(guideCircleView)
        configureConstraints()
        
        selectedSquareView.isHidden = true
        view.bringSubviewToFront(searchGuideLabel)
        
        let locationManager = LocationManager.shared
        locationManager.delegate = self
        locationManager.updateLocation()
    }
    
    /// ํ–‰์„ฑ์„ ๋ฆฌ์ŠคํŠธ์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜
    private func getPlanetNodeList(planets: [Body]) {
        for planet in planets {
            if planet.name != "Earth" && planet.name != "Pluto" {
                let sphere = SCNSphere(radius: 0.2)
                let sphereNode = SCNNode(geometry: sphere)
                sphereNode.position = SCNVector3(planet.coordinate.x, planet.coordinate.y, planet.coordinate.z)
                planetObjectList.append(sphereNode)
            }
        }
    }
    
    /// ํ–‰์„ฑ์„ ๋ฐฐ์น˜ํ•˜๊ธฐ ์œ„ํ•œ ํ•จ์ˆ˜
    private func setPlanetPosition(to scene: SCNScene?, planets: [Body]) {
        for planet in planets {
            if planet.name == "Earth" || planet.name == "Pluto" {
                continue
            } else {
                let sphere = SCNSphere(radius: 0.2)
                sphere.firstMaterial?.diffuse.contents = UIImage(named: planet.name + "_Map")
                let sphereNode = SCNNode(geometry: sphere)
                sphereNode.position = SCNVector3(planet.coordinate.x, planet.coordinate.y, planet.coordinate.z)
                scene?.rootNode.addChildNode(sphereNode)
            }
        }
    }
    
    // TODO: ํ–‰์„ฑ ์„ ํƒ ์‹œ, ๋„ค๋น„๊ฒŒ์ด์…˜ ํšจ๊ณผ ์‚ฝ์ž…
    // hit_test๋กœ ํ™œ์šฉ: vivi
    
    private func configureConstraints() {
        sceneView.anchor(top: view.topAnchor, leading: view.leadingAnchor, bottom: view.bottomAnchor, trailing: view.trailingAnchor)
        
        selectedSquareView.centerX(inView: sceneView)
        selectedSquareView.centerY(inView: sceneView)
        
        guideCircleView.centerX(inView: view)
        guideCircleView.anchor(top: view.topAnchor, paddingTop: screenHeight * 0.23)
        
        searchGuideLabel.centerX(inView: view)
        searchGuideLabel.anchor(top: view.topAnchor, paddingTop: screenHeight * 0.7)
        
        selectPlanetCollectionView.anchor(top: view.topAnchor, paddingTop: screenHeight * 0.68)
        selectPlanetCollectionView.setHeight(height: screenHeight * 0.35)
        selectPlanetCollectionView.setWidth(width: screenWidth)
        selectPlanetCollectionView.centerX(inView: view)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.isNavigationBarHidden = false

        DispatchQueue.main.async {
            self.navigationController?.navigationItem.title = "์šฐ์ฃผ ๋‘˜๋Ÿฌ๋ณด๊ธฐ"
            self.navigationController?.navigationBar.tintColor = .white
            self.navigationController?.navigationBar.backgroundColor = .black
        }
        
        let configuration = ARWorldTrackingConfiguration()
        configuration.worldAlignment = .gravityAndHeading
        sceneView.session.run(configuration)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneView.session.pause()
    }

    // MARK: - ARSCNViewDelegate
    func session(_ session: ARSession, didFailWithError error: Error) {
        // Present an error message to the user
        
    }
    
    func sessionWasInterrupted(_ session: ARSession) {
        // Inform the user that the session has been interrupted, for example, by presenting an overlay
        
    }
    
    func sessionInterruptionEnded(_ session: ARSession) {
        // Reset tracking and/or remove existing anchors if consistent tracking is required
        
    }
    
    // MARK: - LocationManagerDelegate
    
    func didUpdateUserLocation() {
        Task {
            let bodies = try await AstronomyAPIManager().requestBodies()
            setPlanetPosition(to: sceneView.scene, planets: bodies)
            getPlanetNodeList(planets: bodies)
        }
    }
}

 

 

 

 

 

3. Hittest result

 

    /// hitTest ๊ฐ์ฒด(ํ–‰์„ฑ ๋…ธ๋“œ)๋ฅผ ์ธ์‹ํ•˜๋Š” ํ•จ์ˆ˜
    @objc func handleTap(sender: UITapGestureRecognizer) {
        let sceneViewTappdeOn = sender.view as! SCNView
        let touchCoordinates = sender.location(in: sceneViewTappdeOn)
        let hitTest = sceneViewTappdeOn.hitTest(touchCoordinates)
        
        if hitTest.isEmpty {
            print("didn't touch anything")
        } else {
            let results = hitTest.first!
            let geometry = results.node.geometry
            print(geometry?.firstMaterial?.diffuse.contents)
            let node = results.node
            print(node)
        }
    }

 

 

# viewDidLoad ์— ๋‘ ์ค„ ์ถ”๊ฐ€ (ํƒญ ์ œ์Šค์ณ, ์”ฌ ๋ทฐ์— add)

    override func viewDidLoad() {
        super.viewDidLoad()
        selectPlanetCollectionView.delegate = self
        selectPlanetCollectionView.dataSource = self
        
        [guideCircleView, sceneView, selectedSquareView, searchGuideLabel, selectPlanetCollectionView].forEach { view.addSubview($0) }
        sceneView.addSubview(guideCircleView)
        configureConstraints()
        
        selectedSquareView.isHidden = true
        view.bringSubviewToFront(searchGuideLabel)
        
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
        self.sceneView.addGestureRecognizer(tapGestureRecognizer)
        
        let locationManager = LocationManager.shared
        locationManager.delegate = self
        locationManager.updateLocation()
    }

 

 

# ๊ฒฐ๊ณผ (print)

didn't touch anything
didn't touch anything
Optional(<UIImage:0x2830f0090 named(main: Venus_Map) {2048, 1024} renderingMode=automatic(original)>)
<SCNNode: 0x283ecda00 pos(3.323599 -3.629274 -0.884342) | geometry=<SCNSphere: 0x2839cf0c0 | radius=0.200> | no child>
Optional(<UIImage:0x2830df3c0 named(main: Sun_Map) {2048, 1024} renderingMode=automatic(original)>)
<SCNNode: 0x283ec8f00 pos(3.565236 -3.416369 -0.785825) | geometry=<SCNSphere: 0x2839ca150 | radius=0.200> | no child>
Optional(<UIImage:0x2830df210 named(main: Mercury_Map) {2048, 1024} renderingMode=automatic(original)>)
<SCNNode: 0x283ef0900 pos(3.637146 -3.337214 -0.796352) | geometry=<SCNSphere: 0x2839cebe0 | radius=0.200> | no child>
didn't touch anything