SwiftUI - MacOS - Draw a rectangle on image to crop - macos

I'm look to crop an image with a rectangle.
I'm displaying an image, and the user will drag a rectangle anywhere in the image.
It will give a new image, corresponding to the part of the image where the user drew the rectangle.
I manage to display the image and draw a rectangle.
Now I need to revert the drag gesture locations back to the original image for cropping, but I cannot find the link.
I have this for now.
var body: some View {
VStack {
Text("Drag a rectangle around the graph to crop the image").padding()
GeometryReader { geo in
ZStack {
Image(nsImage: NSImage(cgImage: inputImage, size: NSSize(width: inputImage.width, height: inputImage.height)))
.resizable()
.scaledToFit()
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global).onChanged { dragGesture in
print("START = \(dragGesture.startLocation)")
print(" END = \(dragGesture.location)")
print("G - GLOBAL = \(geo.frame(in: .global))")
print("G - GLOBAL MAXX = \(geo.frame(in: .global).maxX)")
print("G - GLOBAL MINX = \(geo.frame(in: .global).minX)")
print("G - GLOBAL MIDX = \(geo.frame(in: .global).midX)")
print("G - GLOBAL MAXY = \(geo.frame(in: .global).maxY)")
print("G - GLOBAL MINY = \(geo.frame(in: .global).minY)")
print("G - GLOBAL MIDY = \(geo.frame(in: .global).midY)")
print("G - LOCAL = \(geo.frame(in: .local))")
print("INMG = \(inputImage.width) * \(inputImage.height)")
positionX = dragGesture.startLocation.x - geo.frame(in: .global).minX
positionY = geo.frame(in: .global).size.height - dragGesture.startLocation.y + geo.frame(in: .global).minY
selectionWidth = 1 + abs(dragGesture.startLocation.x - dragGesture.location.x)
offsetX = selectionWidth / 2
selectionHeight = 1 + abs(dragGesture.startLocation.y - dragGesture.location.y)
offsetY = selectionHeight / 2
cropWidth = (selectionWidth / geo.frame(in: .global).minX) * CGFloat(inputImage.width)
print("X CROP = \(cropWidth)")
cropHeight = (selectionHeight / geo.frame(in: .global).height) * CGFloat(inputImage.height)
print("Y CROP = \(cropHeight)")
let extraWidthSide: CGFloat = ((geo.frame(in: .global).width - geo.frame(in: .global).minX) / 2)
print("EXTRA WIDE = \(extraWidthSide)")
cropPositionX = max(0, ((dragGesture.startLocation.x - geo.frame(in: .global).minX - extraWidthSide) / geo.frame(in: .global).minX) * CGFloat(inputImage.width))
print("CROP X POS = \(cropPositionX)")
cropPositionY = CGFloat(inputImage.height) -
(((dragGesture.startLocation.y - geo.frame(in: .global).minY) / geo.frame(in: .global).height) * CGFloat(inputImage.height))
print("CROP Y POS = \(cropPositionY)")
})
.border(Color.black)
Rectangle()
.border(Color.blue, width: 3)
.frame(width: selectionWidth, height: selectionHeight)
.foregroundColor(Color.clear)
.position(x: positionX, y: positionY)
.offset(x: offsetX, y: offsetY)
}
}
.frame(width: 600, height: 400)
.padding()
if let result = resultImage {
Image(nsImage: NSImage(cgImage: result.cropping(to: CGRect(x: cropPositionX, y: cropPositionY, width: cropWidth, height: cropHeight))!,
size: NSSize(width: inputImage.width, height: inputImage.height)))
.resizable()
.scaledToFit()
.border(Color.purple, width: 2)
.frame(width: 600, height: 400)
}
HStack {
Spacer()
Button("Dismiss") {
presentationMode.wrappedValue.dismiss()
}.mainButton()
Spacer()
Button("Crop") {
resultImage = inputImage.cropping(to: CGRect(x: cropPositionX, y: cropPositionY, width: cropWidth, height: cropHeight))
presentationMode.wrappedValue.dismiss()
}.mainButton()
Spacer()
}
}
.frame(width: 1400, height: 1000)
It "works" for landscape image, but not for portrait.
If you have a solution, or you know a package which does it. I'll take it all.
Thanks,
Nicolas

Related

How to move an object along a path in swiftui

I try to move an Image along a path in a vein. I didn't find how to do it. Here my present code:
struct ContentView: View {
#State var chemin = Path {chemin in
chemin.move(to: CGPoint(x: 620, y: 0))
chemin.addLine(to: CGPoint(x:460, y:120))
chemin.addQuadCurve(to: CGPoint(x: 480, y: 200), control: CGPoint(x: 380, y: 220))
chemin.addLine(to: CGPoint(x:590, y:120))
chemin.addQuadCurve(to: CGPoint(x: 680, y: 320), control: CGPoint(x: 890, y: 160))
chemin.addLine(to: CGPoint(x: 300, y: 330))
chemin.addLine(to: CGPoint(x: 0, y: 160))
}
var body: some View {
ZStack{
Image("carte").resizable().ignoresSafeArea()
VStack{
Image("corona").resizable().frame(width: 150, height: 100, alignment: .center).animation(Animation.linear(duration: 3), value: true)
}
chemin.stroke(Color.green, lineWidth: 3).ignoresSafeArea()
}
}
}
I finally found how to do it, here's the code with some extra-functions:
import SwiftUI
struct FollowEffect: GeometryEffect {
var pct: CGFloat = 0
let path: Path
var rotate = true
var animatableData: CGFloat {
get { return pct }
set { pct = newValue }
}
func effectValue(size: CGSize) -> ProjectionTransform {
if !rotate {
let pt = percentPoint(pct)
return ProjectionTransform(CGAffineTransform(translationX: pt.x, y: pt.y))
} else {
// Calculate rotation angle, by calculating an imaginary line between two points
// in the path: the current position (1) and a point very close behind in the path (2).
let pt1 = percentPoint(pct)
let pt2 = percentPoint(pct - 0.01)
let a = pt2.x - pt1.x
let b = pt2.y - pt1.y
let angle = a < 0 ? atan(Double(b / a)) : atan(Double(b / a)) - Double.pi
let transform = CGAffineTransform(translationX: pt1.x, y: pt1.y).rotated(by: CGFloat(angle))
return ProjectionTransform(transform)
}
}
func percentPoint(_ percent: CGFloat) -> CGPoint {
let pct = percent > 1 ? 0 : (percent < 0 ? 1 : percent)
let f = pct > 0.999 ? CGFloat(1-0.001) : pct
let t = pct > 0.999 ? CGFloat(1) : pct + 0.001
let tp = path.trimmedPath(from: f, to: t)
return CGPoint(x: tp.boundingRect.midX, y: tp.boundingRect.midY)
}
}
struct ContentView: View {
#State var chemin: Path = Path {path in
path.move(to: CGPoint(x: 620, y: 0))
path.addLine(to: CGPoint(x:460, y:120))
path.addQuadCurve(to: CGPoint(x: 480, y: 200), control: CGPoint(x: 380, y: 220))
path.addLine(to: CGPoint(x:590, y:120))
path.addQuadCurve(to: CGPoint(x: 680, y: 320), control: CGPoint(x: 890, y: 160))
path.addLine(to: CGPoint(x: 300, y: 330))
path.addLine(to: CGPoint(x: 0, y: 160))
}
#State private var flag = false
var body: some View {
GeometryReader { proxy in
ZStack(alignment: .topLeading) {
Image("carte").resizable().ignoresSafeArea()
// Animate movement of Image
Image("corona").resizable().foregroundColor(Color.red).frame(width: 50, height: 50).offset(x: -25, y: -25).modifier(FollowEffect(pct: self.flag ? 1 : 0, path: chemin)).onAppear {
withAnimation(Animation.linear(duration: 4.0).repeatForever(autoreverses: false)) {
self.flag.toggle()
}
}
}.frame(alignment: .topLeading)
}
}
}

SwiftUI Drag gesture is not ending

I was trying to create a custom View with rotation effects in SwiftUI. Instead of using rotation gestures, I was trying to use the Drag Gesture to rotate the Parent by adding a separate view to the parent. Dragging the child (The Blue Dot) would rotate its parent (ZStack). But I've faced difficulty while dragging the blue dot. The drag gesture is not ending. Can you guys help me to find out what I did wrong?
import SwiftUI
struct EditingHolder: View {
/* For Rotation Circle Drag */
#State private var currDragRect : CGSize = .zero
#State private var prevDragRect : CGSize = .zero
var rectRadius : CGFloat {
let sum : CGFloat = 2*pow(150, 2)
return sqrt(sum/4)
}
var dragRadius : CGFloat {
let height = prevDragRect.height + currDragRect.height
let width = prevDragRect.width + currDragRect.width
let sum = pow(height, 2) + pow(width, 2)
return sqrt(sum/4)
}
var rotateAngle : CGFloat{
let angle = asin(dragRadius/rectRadius)
print("🍄 Angle Produced = ", angle)
return angle
}
var body: some View {
/* **** Gestures **** */
let rotateDrag = DragGesture()
.onChanged({ value in
print("🏵 Rotate Circle Drag Started ...")
currDragRect = value.translation
}).onEnded({ _ in
print("🏵 Rotate Circle Drag Ended ✅")
prevDragRect.height += currDragRect.height
prevDragRect.width += currDragRect.width
currDragRect = .zero
})
//************* Views *******************
GeometryReader { geo in
ZStack(alignment: .center) {
Rectangle()
.padding()
.foregroundColor(Color.yellow)
///Rotate Circle `top`
Circle()
.foregroundColor(Color.blue)
.frame(width: 20, height: 20)
.position(x: 150 - 3, y: 3)
.gesture(rotateDrag)
}.frame(width:150, height: 150, alignment: .center)
.border(.green, width: 3)
.position(x: geo.size.width/2, y: geo.size.height/2)
.rotationEffect(.radians(rotateAngle))
}
//************* Views *******************
}
}
struct EditingHolder_Previews: PreviewProvider {
static var previews: some View {
EditingHolder()
}
}
To provide high priority to a specific gesture there is a modifier called .highPriorityGesture(). Have a look here for a better explanation How to use gestures in SwiftUI.
I've updated the angle calculations,
/* **** Gestures **** */
let rotateDrag = DragGesture()
.onChanged({ value in
print("🏵 Rotate Circle Drag Started ...")
let difY = center.y - value.location.y
let difX = center.x - value.location.x
//Initial Angle when the drag started..
if deltaAngle == 0{
deltaAngle = atan2(difY, difX)
}else{
angle = atan2(difY, difX) - deltaAngle
}
}).onEnded({ _ in
print("🏵 Rotate Circle Drag Ended ✅")
withAnimation {
angle = 0
deltaAngle = 0
}
})
//************* Views *******************
Now to add .highPriorityGesture(rotateDrag) to ZStack.
.onTapGesture() is added to get the center for angle calculation. Tap on the view and then rotate by dragging the blue dot.
Here is the final implementation,
struct EditingHolder: View {
/* For Rotation Circle Drag */
#State private var center : CGPoint = .zero
#State private var angle : CGFloat = 0
#State private var deltaAngle : CGFloat = 0
var body: some View {
/* **** Gestures **** */
let rotateDrag = DragGesture()
.onChanged({ value in
print("🏵 Rotate Circle Drag Started ...")
let difY = center.y - value.location.y
let difX = center.x - value.location.x
//Initial Angle when the drag started..
if deltaAngle == 0{
deltaAngle = atan2(difY, difX)
}else{
angle = atan2(difY, difX) - deltaAngle
}
}).onEnded({ _ in
print("🏵 Rotate Circle Drag Ended ✅")
withAnimation {
angle = 0
deltaAngle = 0
}
})
//************* Views *******************
GeometryReader { geo in
ZStack(alignment: .center) {
Rectangle()
.padding()
.foregroundColor(Color.yellow)
///Rotate Circle `top`
Circle()
.foregroundColor(Color.blue)
.frame(width: 20, height: 20)
.position(x: 150 - 3, y: 3)
.gesture(rotateDrag)
}.frame(width:150, height: 150, alignment: .center)
.border(.green, width: 3)
.position(x: geo.size.width/2, y: geo.size.height/2)
.rotationEffect(Angle(radians: angle))
/* You have make the gesture a high priority */
.highPriorityGesture(rotateDrag)
.onTapGesture {
print("☘️ Center assigned..")
center = CGPoint(x: geo.frame(in: .global).size.width/2, y: geo.frame(in: .global).size.height/2)
}
}
//************* Views *******************
}
}
struct EditingHolder_Previews: PreviewProvider {
static var previews: some View {
EditingHolder()
}
}
The sequence of drag gesture and rotation is important, otherwise SwiftUI looses context of the dragged view (which is changing by drag).
Also you don't need GeometryReader. Here is an example that works in regards to the dragging, the angle calculation needs some more work.
struct ContentView: View {
/* For Rotation Circle Drag */
#State private var currDragRect : CGSize = .zero
#State private var prevDragRect : CGSize = .zero
let rectRadius : CGFloat = 75
var dragRadius : CGFloat {
let height = prevDragRect.height + currDragRect.height
let width = prevDragRect.width + currDragRect.width
let sum = pow(height, 2) + pow(width, 2)
return sqrt(sum/4)
}
var rotateAngle : CGFloat{
let x = min(1, max(-1, dragRadius/rectRadius)) // asin only defined in -1...1
let angle = asin(x)
print("🍄 Angle Produced = ", angle)
return angle
}
var body: some View {
/* **** Gestures **** */
let rotateDrag = DragGesture()
.onChanged { value in
print("🏵 Rotate Circle Drag Started ...")
currDragRect = value.translation
}
.onEnded { _ in
print("🏵 Rotate Circle Drag Ended ✅")
prevDragRect.height += currDragRect.height
prevDragRect.width += currDragRect.width
currDragRect = .zero
}
//************* Views *******************
ZStack(alignment: .topTrailing) {
Rectangle()
.padding()
.foregroundColor(Color.yellow)
.border(.green, width: 3)
///Rotate Circle `top`
Circle()
.foregroundColor(Color.blue)
.frame(width: 20, height: 20)
.offset(x: 8, y: -8)
}
.rotationEffect(.radians(rotateAngle)) // rotation here
.gesture(rotateDrag) // drag here
.frame(width:150, height: 150, alignment: .center)
//************* Views *******************
}
}

SwiftUI + MacOS + Xcode: Format right side of two view HStack such that it is centered in its parent view

I have a view that contains an HStack that contains two views. The right view contains a graph. The left view contains labels for the graphs YAxis. I would like to center the graph in its parent view. If someone could point me in the right direction to accomplish this it would be appreciated. Below is my code.
Regards,
Chris
import SwiftUI
struct MainView: View {
#EnvironmentObject var viewModel : ViewModel
var body: some View {
VStack (alignment: .center) {
let maxYValue = viewModel.data.max { $0.Value < $1.Value }?.Value
let minYValue = viewModel.data.max { $0.Value > $1.Value }?.Value
let deltaY = maxYValue! - minYValue!
let maxYVal = Int(round(maxYValue!))
let minYVal = Int(round(minYValue!))
HStack{
VStack{
Text(String("\(maxYVal)"))
Spacer()
Text("2")
Spacer()
Text("3")
Spacer()
Text("3")
Spacer()
Text(String("\(minYVal)"))
}.frame(width: 100, height: 510, alignment: .trailing)
GeometryReader { geometry in
Path { path in
for index in viewModel.data.indices {
let xPosition = ((geometry.size.width) / (CGFloat(viewModel.data.count - 1))) * CGFloat(index + 0)
let yPosition = (1 - (CGFloat(viewModel.data[index].Value - minYValue!)) / CGFloat(deltaY) ) * (geometry.size.height)
if index == 0 {
path.move(to: CGPoint(x : xPosition, y : yPosition))
}
path.addLine(to: CGPoint(x : xPosition, y: yPosition))
}
} .stroke(Color.blue, style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round))
Path { path in
path.move(to: CGPoint(x: 0, y: geometry.size.height))
path.addLine(to: CGPoint(x: 0, y: 0))
path.move(to: CGPoint(x: 0, y: geometry.size.height))
path.addLine(to: CGPoint(x: geometry.size.width, y: geometry.size.height))
}.stroke(Color.black, style: StrokeStyle(lineWidth: 2))
Path { path in
for index in 0...3 {
path.move(to: CGPoint(x: 0 , y: geometry.size.height * CGFloat(index) / 4))
path.addLine(to: CGPoint(x: geometry.size.width , y: geometry.size.height * CGFloat(index) / 4))
}
}.stroke(Color.gray, style: StrokeStyle(lineWidth: 0.5))
} // end geometry reader
.frame(width: 700, height: 500)// end inner geometry reader
}
} // end of vstack
}
}
Thank you for the response. After posting my query I did some research and came to the conclusion that my code was too complex. So, I rewrote it and I think simplified it. I create a geometry reader and use it to create two rectangles that combined take up the entire parent view. In the upper rectangle I place the graph title and in the lower rectangle I place the graph and am in the process of putting labels on the X and Y axis. Using this approach has allowed me to exactly place items within the rectangle including centering the graph. Problem solved.
The only thing I need to figure out is how to change the stroke for a given line without having to create multiple paths as I have done.
Below is the updated code.
Thanks again for responding.
Chris
import SwiftUI
struct MainView: View {
var body: some View {
GeometryReader { geometry in
VStack (spacing: 0){
let frameWidth = geometry.size.width
let frameHeight = geometry.size.height
Rectangle()
.fill(Color.clear)
// .border(Color.red)
.frame(width: frameWidth, height: frameHeight * 1/8 )
.overlay(
Text("Principal Values Over the Last Year")
.font(.largeTitle)
)
Rectangle()
.fill(Color.clear)
.frame(width: frameWidth, height: frameHeight * 7/8 )
// .border(Color.black, width: 1)
.overlay(
PrincipalChart(frameWidth: frameWidth, frameHeight: frameHeight * 7/8)
)
.padding(0)
} // end Vstack
} // end geometry reader
} // end body var
} // end of MainView
struct PrincipalChart: View {
let frameWidth : CGFloat
let frameHeight : CGFloat
#EnvironmentObject var viewModel : ViewModel
var body: some View {
ZStack {
let maxYValue = viewModel.data.max { $0.Value < $1.Value }?.Value
let minYValue = viewModel.data.max { $0.Value > $1.Value }?.Value
let deltaY = maxYValue! - minYValue!
let maxYVal = Int(round(maxYValue!))
let minYVal = Int(round(minYValue!))
let yAxisStep = Int((maxYVal - minYVal) / 4)
Path { path in
for index in viewModel.data.indices {
let xPosition = (frameWidth * 1/10) + ((frameWidth * 8/10) / (CGFloat(viewModel.data.count - 1))) * CGFloat(index + 0)
let yPosition = (1 - (CGFloat(viewModel.data[index].Value - minYValue!)) / CGFloat(deltaY) ) * (frameHeight * 9/10)
if index == 0 {
path.move(to: CGPoint(x : xPosition, y : yPosition))
}
path.addLine(to: CGPoint(x : xPosition, y: yPosition))
}
} .stroke(Color.blue, style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round))
Path { path in
path.move(to: CGPoint(x: frameWidth * 1/10, y: 0))
path.addLine(to: CGPoint(x: frameWidth * 1/10 , y: frameHeight * 9/10 ))
path.addLine(to: CGPoint(x: frameWidth * 9/10, y: frameHeight * 9/10))
}.stroke(Color.black, style: StrokeStyle(lineWidth: 2))
Path { path in
for index in 0...3 {
path.move(to: CGPoint(x: frameWidth * 1/10, y: (frameHeight * 9/10) * (CGFloat (index) / 4) ))
path.addLine(to: CGPoint(x: frameWidth * 9/10, y: (frameHeight * 9/10) * (CGFloat (index) / 4)))
}
}.stroke(Color.gray, style: StrokeStyle(lineWidth: 0.5))
Text("\(maxYVal)").position(x: (frameWidth * 1/10) - 20, y: 1)
let stepValue3 = maxYVal - yAxisStep
Text("\(stepValue3)").position(x: (frameWidth * 1/10) - 20, y: -2 + (frameHeight * 9/10) * 1 / 4 )
let stepValue2 = stepValue3 - yAxisStep
Text("\(stepValue2)").position(x: (frameWidth * 1/10) - 20, y: -2 + (frameHeight * 9/10) * 2 / 4 )
let stepValue1 = stepValue2 - yAxisStep
Text("\(stepValue1)").position(x: (frameWidth * 1/10) - 20, y: -2 + (frameHeight * 9/10) * 3 / 4 )
Text("\(minYVal)").position(x: (frameWidth * 1/10) - 20, y: -2 + (frameHeight * 9/10) * 4 / 4 )
} // end z stack
} // end body var
} // end Principal Chart view

What is the image size limitation for ScrollView in SwiftUI?

I am trying to create a scrollable image for large images that are 30,000 by 1800 in size. When I add the image to Assets and reference it in ContentView nothing shows. Smaller images (640 x 480) show and are scrollable.
Here is the code:
struct ContentView: View {
#State var scale: CGFloat = 1.0
#State var isTapped: Bool = false
#State var pointTapped: CGPoint = CGPoint.zero
#State var draggedSize: CGSize = CGSize.zero
#State var previousDragged: CGSize = CGSize.zero
var body: some View {
GeometryReader {reader in
Image("scroll")
.resizable()
.scaledToFit()
.animation(.default)
.offset(x: self.draggedSize.width, y: 0)
.scaleEffect(self.scale)
.scaleEffect(self.isTapped ? 2 : 1, anchor: UnitPoint(x: self.pointTapped.x / reader.frame(in: .global).maxX, y: self.pointTapped.y / reader.frame(in: .global).maxY))
.gesture(TapGesture(count: 2)
.onEnded({
self.isTapped = !self.isTapped
}).simultaneously(with: DragGesture(minimumDistance: 0, coordinateSpace: .global).onChanged({ (value) in
self.pointTapped = value.startLocation
self.draggedSize = CGSize(width: value.translation.width + self.previousDragged.width, height: value.translation.height + self.previousDragged.height)
// print(value.startLocation)
}).onEnded({ (value) in
// print(value.location)
let offsetWidth = (reader.frame(in: .global).maxX * self.scale - reader.frame(in: .global).maxX) / 2
let newDraggedWidth = self.draggedSize.width * self.scale
if (newDraggedWidth > offsetWidth) {
self.draggedSize = CGSize(width: offsetWidth / self.scale, height: value.translation.height + self.previousDragged.height)
} else if (newDraggedWidth < -offsetWidth) {
self.draggedSize = CGSize(width: -offsetWidth / self.scale, height: value.translation.height + self.previousDragged.height)
} else {
self.draggedSize = CGSize(width: value.translation.width + self.previousDragged.width, height: value.translation.height + self.previousDragged.height)
}
self.previousDragged = self.draggedSize
}))).gesture(MagnificationGesture().onChanged({ (scale) in
self.scale = scale.magnitude
}).onEnded({ (scaleFinal) in
self.scale = scaleFinal.magnitude
}))
}
}
}
try adding to the Image:
.renderingMode(.original)
just before
.resizable()

How to update different views using SwiftUI?

I am aiming to make a program in which I am using using SwiftUI buttons to update by SCNView in SceneKit. I have a cylinder as a SCNCylinder in my SCNView inside a frame in SwiftUI. I want my cylinder to rotate about 180° after I press the button. In my current code I have used #State and #Binding to update the view. But somehow the cylinder rotates as soon as I run the code, not waiting for me to touch the button. Not sure why this happens
Here is my code :
struct ContentView: View {
#State var rotationAngle: Float = 180
var body: some View {
VStack{
Button(action: {
// What to perform
self.rotationAngle = 180
}) {
// How the button looks like
Text("180°")
.frame(width: 100, height: 100)
.position(x: 225, y: 500)
}
SceneKitView(angle: self.$rotationAngle)
.frame(width: 300, height: 300)
.position(x: 225, y: 0)
}
}
}
struct SceneKitView: UIViewRepresentable {
#Binding var angle: Float
func degreesToRadians(_ degrees: Float) -> CGFloat {
return CGFloat(degrees * .pi / 180)
}
func makeUIView(context: UIViewRepresentableContext<SceneKitView>) -> SCNView {
let sceneView = SCNView()
sceneView.scene = SCNScene()
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.white
sceneView.frame = CGRect(x: 0, y: 10, width: 0, height: 1)
return sceneView
}
func updateUIView(_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
let cylinder = SCNCylinder(radius: 0.02, height: 2.0)
let cylindernode = SCNNode(geometry: cylinder)
cylindernode.position = SCNVector3(x: 0, y: 0, z: 0)
cylinder.firstMaterial?.diffuse.contents = UIColor.green
cylindernode.pivot = SCNMatrix4MakeTranslation(0, -1, 0)
let rotation = SCNAction.rotate(by: self.degreesToRadians(self.angle), around: SCNVector3(1, 0, 0), duration: 5)
sceneView.scene?.rootNode.addChildNode(cylindernode)
cylindernode.runAction(rotation)
}
typealias UIViewType = SCNView
}
I want the cylinder to rotate after I press the button. Please help me with this problem.
just set startingAngle to 0
#State var rotationAngle: Float = 0

Resources