Categories
apple ios

Isometric ViewModifier

SwiftUI instructional on changing 2D Views and Images into isometric perspectives the usage of customized View Modifiers.

Jean-Marc Boullianne

Isometric SwiftUI View

The different day I began messing round with changing 2D designs into isometric perspectives in Figma. I assumed it could be neat to create a ViewModifier in SwiftUI that does the similar factor. After posting a screenshot of my paintings on twitter, I made up our minds to put in writing this instructional.

Overview

I’ve damaged the educational into two portions (each under):

  1. Applying an Isometric Transformation to a View
  2. Extruding an Isometric View

Before getting began, please imagine subscribing the usage of this hyperlink, and should you aren’t studying this on TrailingClosure.com, please come test us out someday!

Throughout this instructional, you’re going to look my perfect 2D sq. drawing of a watermelon slice. Yes…I say sorry upfront, however there’s one sitting in entrance of me as I write this instructional, in order that’s the picture you’re going to get… 😊

Pretend it’s a Watermelon slice…

Converting 2D Views to Isometric Views

One of the most straightforward techniques to develop into a 2D view is to rotate it 45° after which scale its top through an element of 0.5. Simply rotate and shrink!

Written as a customized ViewModifier

Now we will be able to take the stairs from above and convert this to a customized SwiftUI ViewModifier like this:

struct IsometricViewModifier: ViewModifier {
func frame(content material: Content) -> some View {
content material
.rotationEffect(Angle(levels: 45), anchor: .heart)
.scaleEffect(x: 1.0, y: 0.5, anchor: .heart)
}
}

This similar method is used in every single place for when designers wish to mockup their UI design on an actual software. Below you’ll be able to see the way it used to be used on a design from one of my different tutorials. It actually is so simple as a rotation and scaling of the peak.

Typical Mockup of UI Design

Reverse Reverse!

And if we opposite the stairs above at the mockup symbol, then we get the unique symbol earlier than the isometric transformation.

Pre-Isometric Transformation

Extruding Isometric Views

Now for the tougher phase. What our isometric perspectives are lacking is intensity. For this subsequent phase, we’re going to be developing the extruded base of our isometric view.

Flat vs Extruded Isometric View

Please observe, this phase supplies an extrusion method for non-rounded oblong perspectives. I’m nonetheless messing round with doable techniques to use an isometric transformation and extrude the view with rounded corners. In design methods like Figma it may be simple, but it surely has confirmed tough shifting the method to code.

Simulating Extrusion

In order to create a super extrusion of a view, we wish to take the colours from the outdoor (entrance two isometric facets) and prolong them down alongside the isometric z-axis. The approach I accomplish that is through scaling the equipped content material alongside the x or y-axis after which clip it to the right kind dimension and form. This lets in us to retain the precise colours from perspectives similar to Images and Gradients.

For this system, I damage down the extrusion into two portions: The entrance left and entrance proper aspect. In code, they’re in fact two separate overlays on best of the 2D isometric view.

Custom ExtrudeModifier

Here is the template for the ExtrudeModifier struct. Below I’m going to stroll thru extruding the entrance left aspect by the use of a suite of images. Then I can in short replica the similar procedure to the other aspect to finish the extrusion.

struct ExtrudeModifier<Texture: View> : ViewModifier {

var intensity: CGFloat // Extrusion Depth
var texture: Texture

func frame(content material: Content) -> some View {
content material
// Front Left Side
.overlay(
// Content (Texture) right here
, alignment: .heart)

// Front Right Side
.overlay(
// Content (Texture) right here
, alignment: .heart)
}
}

Steps reference in code under

Below I’ve commented at the steps I’ve taken to create the primary part of the extrusion.

Note: The pictures above have the offset(x: 0, geo.dimension.top) already carried out. I did this to assist the reader visualize the stairs as I went thru them within the code.

struct ExtrudeModifier<Texture: View> : ViewModifier {

var intensity: CGFloat
var texture: Texture

func frame(content material: Content) -> some View {
content material
// Front Left Side
.overlay(
GeometryReader { geo in
texture // Step 2
.brightness(-0.05)
.scaleEffect(x: 1, y: geo.dimension.top * geo.dimension.top, anchor: .backside) // Step 3
.body(top: intensity, alignment: .best) // Step 4
.masks(Rectangle())
.rotation3DEffect(
Angle(levels: 180),
axis: (x: 1.0, y: 0.0, z: 0.0),
anchor: .heart,
anchorZ: 0.0,
point of view: 1.0
)
.projectionEffect(ProjectionTransform(CGAffineTransform(a: 1, b: 0, c: 1, d: 1, tx: 0, ty: 0))) // Step 5
.offset(x: 0, y: geo.dimension.top)

}
, alignment: .heart)

// Front Right Side
.overlay(
// TO DO
, alignment: .heart)

}
}

Now the Other Side
Now I’ll practice the similar strategy to the opposite aspect of the isometric view.

struct ExtrudeModifier<Texture: View> : ViewModifier {

var intensity: CGFloat
var texture: Texture

func frame(content material: Content) -> some View {
content material
// Front Left Side
.overlay(
// See code from earlier than...
, alignment: .heart)

// Front Right Side
.overlay(
GeometryReader { geo in
texture
.brightness(-0.1)
.scaleEffect(x: geo.dimension.width * geo.dimension.width, y: 1.0, anchor: .trailing)
.body(width: intensity, alignment: .main)
.clipped()
.rotation3DEffect(
Angle(levels: 180),
axis: (x: 0.0, y: 1.0, z: 0.0),
anchor: .main,
anchorZ: 0.0,
point of view: 1.0
)
.projectionEffect(ProjectionTransform(CGAffineTransform(a: 1, b: 1, c: 0, d: 1, tx: 0, ty: 0)))
.offset(x: geo.dimension.width + intensity, y: 0 + intensity)
}
, alignment: .heart)

}
}

Why did you exchange the brightness of the edges?

If you spotted above, I changed the brightness of the two overlays to cause them to darker. This is helping simulate the three-D nature of the isometric view. Without it, the view would glance undeniable, and flat at the display screen. Notice how giant of a distinction it makes within the comparability under.

No Change vs Change in Brightness

Putting It All Together

If you’d like, you’ll be able to create a customized IsometricView part which makes use of the ExtrudeModifier and IsometricViewModifier above. This customized part offers the developer the method to dynamically trade whether or not or now not the view is isometric in addition to the intensity of the extrusion.

struct IsometricView<Content: View>: View {

var lively: Bool
var content material: Content
var extruded: Bool
var intensity: CGFloat

init(lively: Bool, extruded: Bool = false, intensity: CGFloat = 20, @ViewBuilder content material: ()-> Content) {
self.lively = lively
self.extruded = extruded
self.intensity = intensity
self.content material = content material()
}

@ViewBuilder var frame: some View {
if lively {
if extruded {
content material
.modifier(ExtrudeModifier(intensity: intensity, background: content material))
.modifier(IsometricViewModifier(lively: lively))
.animation(.easeInOut)
} else {
content material
.modifier(IsometricViewModifier(lively: lively))
.animation(.easeInOut)
}
} else {
content material
.animation(.easeInOut)
}

}
}

Example Use

Once you get started taking part in with the IsometricViewModifier and ExtrudeModifier you’ll be able to make some fascinating issues. Below I’ve a floating isometric view, every other with two other extruded textures, and in addition a picture.

Show us what you’ve made!

We wish to see what you’ve made the usage of this instructional! Send us pics! Find us on Twitter @TrailingClosure, on Instagram, or electronic mail us at [email protected]com.