To preface this question, I would like to mention that I’m only a month or so into swiftUI. I was previously using React Native for my projects, so pardon my unfamiliarity with standard swift practices.
Code:
import SwiftUI
import UIKit
let transforms = [
// Front face
CATransform3DMakeTranslation(0, 0, 50),
// Right face
CATransform3DRotate(CATransform3DMakeTranslation(50, 0, 0), CGFloat.pi / 2, 0, 1, 0),
// Top face
CATransform3DRotate(CATransform3DMakeTranslation(0, -50, 0), CGFloat.pi / 2, 1, 0, 0),
// Bottom face
CATransform3DRotate(CATransform3DMakeTranslation(0, 50, 0), -CGFloat.pi / 2, 1, 0, 0),
// Left face
CATransform3DRotate(CATransform3DMakeTranslation(-50, 0, 0), -CGFloat.pi / 2, 0, 1, 0),
// Back face
CATransform3DRotate(CATransform3DMakeTranslation(0, 0, -50), CGFloat.pi, 0, 1, 0)
]
struct TransformableView: UIViewRepresentable {
var views: [AnyView]
@Binding var angleX: CGFloat
@Binding var angleY: CGFloat
func makeUIView(context: Context) -> UIView {
let container = UIView()
container.backgroundColor = .clear
// Apply perspective to the container
/*
var perspective = CATransform3DIdentity
perspective.m34 = -1.0 / 300
container.layer.sublayerTransform = perspective
*/
for (index, view) in views.enumerated() {
let hostingController = UIHostingController(rootView: view)
hostingController.view.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
hostingController.view.backgroundColor = .clear
hostingController.view.layer.transform = transforms[index]
container.addSubview(hostingController.view)
}
return container
}
func updateUIView(_ uiView: UIView, context: Context) {
let rotationX = CATransform3DMakeRotation(angleX, 1, 0, 0)
let rotationY = CATransform3DMakeRotation(angleY, 0, 1, 0)
let combinedRotation = CATransform3DConcat(rotationX, rotationY)
// ISSUE HERE MAYBE?
uiView.layer.anchorPoint = CGPoint(x: 0, y: 0)
uiView.layer.sublayerTransform = combinedRotation
}
}
struct ContentView: View {
@State private var angleX: CGFloat = 0
@State private var angleY: CGFloat = 0
@State private var lastAngleX: CGFloat = 0
@State private var lastAngleY: CGFloat = 0
var body: some View {
TransformableView(views: [
AnyView(Rectangle().fill(Color.red)),
AnyView(Rectangle().fill(Color.green)),
AnyView(Rectangle().fill(Color.purple)),
AnyView(Rectangle().fill(Color.yellow)),
AnyView(Rectangle().fill(Color.blue)),
AnyView(Rectangle().fill(Color.white)),
], angleX: $angleX, angleY: $angleY)
.frame(width: 100, height: 100)
.gesture(
DragGesture()
.onChanged { value in
// Update the angles based on the drag amount
angleX = lastAngleX + value.translation.height / 300
angleY = lastAngleY + value.translation.width / 300
}
.onEnded { value in
// Store the last angles when the gesture ends
lastAngleX = angleX
lastAngleY = angleY
}
)
.background(.pink)
}
}
#Preview {
ContentView()
}
The code generates a 3D cube using swiftUI transformations, and rotates the cube according to user gestures. The code is supposed to anchor the cube to the center of the screen but the cube strays off both vertically and horizontally.
Image of cube straying off course
The image provided above shows the cube translating off course and not staying fixed.
Already I’ve tried to go through various routes to get a rotating cube(fixed in place).
I’ve tried:
- using the metal, however would not work for my purposes since I would like to place views on each of the surfaces of the cubes.
- Using rotational3D
Resources used:
This article was really helpful for getting started.
https://www.hackingwithswift.com/articles/135/how-to-render-uiviews-in-3d-using-catransformlayer
I’ve also looked at Aheze’s prism code
https://github.com/aheze/Prism/tree/main
However, from what I can tell it looks like his renderer is not truly producing a 3d cube but just imitating one.
Renish is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.