App Development

How to Use AR to Create a 3D Calendar

Blogggg-07

The Value of Augmented Reality

People usually hear about augmented reality in the context of games like Pokemon Go or with apps like Ikea Place, which enables customers to be more confident in large purchases by placing true-to-scale 3D furniture in their space.

With AR, companies can create more innovative brand experiences that include participatory elements, making users more likely to engage. For industries such as media and hospitality where the experience is essentially the product, consumers can use AR to help design and personalize the experience. With AR, planning a vacation, purchasing a timeshare, or choosing a resort can become a much easier and more transparent process, providing the consumer with much clearer expectations leading up to the experience. For media companies, introducing AR can enhance an audience’s relationship with their branded characters and scenes by enabling consumers to join the worlds that they watch on television or read about.

One of the strongest business cases for AR is not consumer facing, but rather in field services and employee training. Field service technicians for food & beverage companies, utility companies, or home services can use AR when they are on-the-go to quickly diagnose problems and make repairs on-site, without needing to reference a large device or manual in order to get the job done. For healthcare companies, AR can improve the effectiveness and efficiency of medical training when using new systems or to regularly train practitioners.

Get in touch to learn more about use cases for AR that could transform your business.

divider 600

How We Do It

Apple defines AR as:

Augmented reality (AR) describes user experiences that add a 2D or 3D elements to the live view from a device’s camera, in a way that makes those elements appear to inhabit the real world. ARKit combines device motion tracking, camera scene capture, advanced scene processing, and display conveniences to simplify the task of building an AR experience. You can use these technologies to create many kinds of AR experiences, using either the back camera or front camera of an iOS device. - Apple

Xcode allows for developers to create AR experiences using ARKit. In ARKit, developers use models, scenes, nodes, and materials to create shapes. Our goal as developers is to experiment with all the tools Apple gives us so we can make experiences that are practical for the user, and AR is part of that goal.

The augmented reality experience we want to create is a 3D calendar. This can be used to view the months and days of the current year. We will be creating this calendar by following the steps needed to work in Apple’s ARKit: we will set up a scene, add a node, set its material to manipulate it for our needs, and add user interaction.

The type of 3D calendar we want to make is inspired by this tutorial.

arkit 1

Instead of making the helpful calendar reference pictured above by cutting paper and pasting it back together, we will create it using ARKit and 3D models. This method will make it portable and accessible for users everywhere they take their phone, not just a tool to keep on their desks. We will be using a dodecahedron, a 12-sided 3D shape, displaying a month of the year on each side.

To find a 3D model of a dodecahedron, we can go online to websites such as Turbosquid or clara.io. We look for a dae (digital asset exchange) model because that file type of 3D models is what is compatible with Xcode and can be easily edited. By using these websites and 3D editing software such as Blender and Cinema4D, we are able to find and make a dodecahedron 3D model with each side representing a node.

arkit2

Once we finish editing it, we can convert it to a scenekit file thanks to Xcode’s capabilities. We do this by creating a new SceneKit Catalog and dropping our dae file in that folder. By going to “Editor” and clicking “Convert to SceneKit scene file format” Xcode turns your dae file to a scn file. Now the model is in our project, and ready to use.

When using ARKit, you have to set up an ARSCNView in your ViewController, along with its delegates. We perform this step because Xcode uses Scenekit to create the 3D models and shapes we use in the augmented reality experience.

import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!

After setting up the basics of sceneView, we have to add the dodecahedron node to the project. dodecaNode is an instance variable that is an empty SCNNode. We set it at the beginning of the code, and then in viewDidLoad, we set its value to the dodecahedron model in the setShapeAndSceneOfNode function. The dodecahedron node is now loaded into the app’s memory. It’s also loaded at a smaller scale (SCNVector3) so that the dodecahedron doesn’t look too big when we open the app.

var dodecaNode = SCNNode()

override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the view's delegate
        sceneView.delegate = self
        
        // Show statistics such as fps and timing information
        sceneView.showsStatistics = true
        //set shape of dodecahedron and resize
        dodecaNode = setShapeAndSceneOfNode()
        //place dodecahedron into view and paste months onto each side
        putMonthViewsOnChildNodesAndAddToView(dodecahedronNode: dodecaNode, vector: SCNVector3(x: 0, y: -0.1, z: -0.1))
        
        addRotationGesture()

        sceneView.autoenablesDefaultLighting = true
    }
 func setShapeAndSceneOfNode() -> SCNNode {
        guard let scene = SCNScene(named: "dodecahedron.scnassets/Dodecahedron4.scn"),
            let node = scene.rootNode.childNode(withName: "dodecahedron", recursively: true) else {
                fatalError("Unable to load node")
        }
        
        node.scale = SCNVector3(0.1, 0.1, 0.1)
        return node
    }

dodecaNode now has the shape of a dodecahedron.

Next we want to add the dodecaNode into our scene and set each side to a month of the year using the node’s materials.

func putMonthViewsOnChildNodesAndAddToView(dodecahedronNode: SCNNode, vector: SCNVector3) {
        //putting the node on eye level by lowering it
        dodecahedronNode.position.y = -10.0
        //setting each side's material to a month of the year using materials
        for item in 0...calendarViewControllers.count - 1 {
            let material = SCNMaterial()
            material.diffuse.contents = calendarViewControllers[item].view
            dodecahedronNode.childNodes[item].geometry?.firstMaterial = material
        }
        //adding the dodecahedron into the view 
        sceneView.scene.rootNode.addChildNode(dodecahedronNode)
        dodecahedronNode.position = vector
    }

calendarViewControllers[] is declared and returns an array of CalendarViewControllers and each one represents a month of the year. calendarViewControllers[] does this by taking in the current date and iterating through the twelve months of that date’s year, and calling CalendarViewController.make() to generate a View for each month.

static func make(date: Date) -> CalendarViewController {
        let viewController = UIStoryboard(name: "CalendarViewController", bundle: nil).instantiateInitialViewController() as! CalendarViewController
        viewController.setupDates(firstDayOfMonth: date)
        return viewController
    }

setupDates is given the first day of the month and then creates a viewModel that contains the rest of the days in a month.


private func setupDates(firstDayOfMonth: Date) {
        let range = Calendar.current.range(of: .day, in: .month, for: firstDayOfMonth)!
        let currentDate = Calendar.current.dateComponents([.year, .month],
                                                          from: firstDayOfMonth)
        dates = range.compactMap {
            return DateComponents(calendar: .current,
                                  timeZone: .current,
                                  year: currentDate.year,
                                  month: currentDate.month,
                                  day: $0).date
        }

        viewModels = dates.map {
            let isToday = Calendar.current.isDate($0, inSameDayAs: Date())
            let title = DateFormatter.singleDayDateFormatter.string(from: $0)
            let textColor: UIColor = isToday ? .black : .gray
            return DateCollectionViewCell.ViewModel(title: title,
                                                    textColor: textColor)
        }

It then uses its collectionView to create cells for each day, including the empty days before the 1st and after the 31st.


  guard let firstDayOfMonth = dates.first, let lastDayOfMonth = dates.last else {
            return
        }
        let frontBuffer = Calendar.current.component(.weekday, from: firstDayOfMonth) - 1
        let backBuffer = 7 - Calendar.current.component(.weekday, from: lastDayOfMonth)

        //add empty cells for days before the 1st day of the month and after the last 
        if frontBuffer > 0 {
            (1...frontBuffer).forEach { _ in
                viewModels.insert(DateCollectionViewCell.ViewModel(title: nil, textColor: .clear), at: 0)
            }
        }
       
        if backBuffer > 0 {
            (1...backBuffer).forEach { _ in
                viewModels.append(DateCollectionViewCell.ViewModel(title: nil, textColor: .clear))
            }
        }
    }

This code is our way of creating a calendar view, but other methods of creating calendars with table views should work as well.

We set each side’s material to the texture of a CalendarViewController view, and then add the dodecahedron to the scene (all inside the function putMonthViewsOnChildNodesAndAddToView())

Now that the dodecahedron is in the scene, we can run the app and see the shape in our world.

arkit3

We add a UIRotationGestureRecognizer, so that the user can turn the calendar around with their finger. The dodecahedron shape rotates by manipulating its euler angles to match the user’s fingers.


   // rotation gesture code from https://stackoverflow.com/questions/49473739/ios-arkit-how-to-create-rotate-object-gesture-function/49475626
    @objc func rotateNode(_ gesture: UIRotationGestureRecognizer){
        
        let rotation = Float(gesture.rotation)
        
        if gesture.state == .changed{
            isRotating = true
            dodecaNode.eulerAngles.y = currentAngleY + rotation
        }
        
        if(gesture.state == .ended) {
            currentAngleY = dodecaNode.eulerAngles.y
            isRotating = false
        }
    }

And now we have a 3D calendar ready to view when you open the app!

ezgif.com-video-to-gif (1)

The materials can also adopt the textures of labels, uiviews, and even videos!

ezgif.com-crop

There are a lot of possibilities with ARKit, and it is to our advantage to play around with them!

Join our team to work with Fortune 500 companies in solving real-world product strategy, design, and technical problems.

Find Your Role
Project Voice Header

The Monetization Challenge for Brands: Thoughts From Project Voice

Last week, the WillowTree team attended and exhibited at Project Voice, in Chattanooga,...

Read the article