I am trying to create a test application using SwiftUI where the user can draw on the screen when they drag on the screen. However, I am having some difficulties getting the Circles that I am using to represent the pen to appear.
Here is the ContentView.swift code that I am using.
import SwiftUI
var list_of_points = [CGPoint]()
struct ContentView: View {
var body: some View {
ZStack{
Rectangle().fill(Color.gray)
Text("Hello!")
}.gesture(DragGesture().onChanged({
value in
drag_responder(point: value.location)
}))
}
}
func drag_responder(point: CGPoint){
print("Drawing at \(point)")
list_of_points.append(point)
let pen = Circle().size(CGSize(width:10, height:10)).position(point)
pen
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import SwiftUI
struct Drawing {
var points: [CGPoint] = [CGPoint]()
}
struct ContentView: View {
#State private var currentDrawing: Drawing = Drawing()
#State private var drawings: [Drawing] = [Drawing]()
#State private var color: Color = Color.black
#State private var lineWidth: CGFloat = 3.0
var body: some View {
VStack(alignment: .center) {
DrawingPad(currentDrawing: $currentDrawing,
drawings: $drawings,
color: $color,
lineWidth: $lineWidth)
}
}
}
struct DrawingPad: View {
#Binding var currentDrawing: Drawing
#Binding var drawings: [Drawing]
#Binding var color: Color
#Binding var lineWidth: CGFloat
var body: some View {
GeometryReader { geometry in
Path { path in
for drawing in self.drawings {
self.add(drawing: drawing, toPath: &path)
}
self.add(drawing: self.currentDrawing, toPath: &path)
}
.stroke(self.color, lineWidth: self.lineWidth)
.background(Color(white: 0.95))
.gesture(
DragGesture(minimumDistance: 0.1)
.onChanged({ (value) in
let currentPoint = value.location
if currentPoint.y >= 0
&& currentPoint.y < geometry.size.height {
self.currentDrawing.points.append(currentPoint)
}
})
.onEnded({ (value) in
self.drawings.append(self.currentDrawing)
self.currentDrawing = Drawing()
})
)
}
.frame(maxHeight: .infinity)
}
private func add(drawing: Drawing, toPath path: inout Path) {
let points = drawing.points
if points.count > 1 {
for i in 0..<points.count-1 {
let current = points[i]
let next = points[i+1]
path.move(to: current)
path.addLine(to: next)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Related
I could not rotate the green part in the circular path.
I want to set dynamically infinite time.
I created a struct for ProgressView and want to set it as ProgressView in my project.
how to implement the red and black colour curve design in the rotation
path
struct CircularProgressView: View {
#State var progressValue: Float = 0.0
var body: some View {
ZStack {
ProgressBar(progress: self.$progressValue)
.frame(width: 150.0, height: 150.0)
.padding(40.0)
}.onAppear{
self.incrementProgress()
}
}
func incrementProgress() {
let randomValue = Float([0.012, 0.022, 0.034, 0.016, 0.11, 0.012].randomElement()!)
self.progressValue += randomValue
}
}
struct ProgressBar: View {
#Binding var progress: Float
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 20.0)
.opacity(0.3)
.foregroundColor(Color.red)
Circle()
.trim(from: 0.0, to: CGFloat(min(self.progress, 2.0)))
.stroke(style: StrokeStyle(lineWidth: 20.0, lineCap: .round, lineJoin: .round))
.foregroundColor(Color.green)
.rotationEffect(Angle(degrees: 270))
.animation(.linear)
}
}
}
This is the Preview struct.
struct CircularProgressView_Previews: PreviewProvider {
static var previews: some View {
CircularProgressView()
}
}
Thanks in advance for helping me.
struct CircularProgressView: View {
#State var progressValue: Float = 0.0
#State private var isLoaderVisible: Bool = false
var degree = 90
var body: some View {
ZStack {
ProgressBar(progress: self.$progressValue)
.frame(width: 150.0, height: 150.0)
.padding(40.0)
}.overlay(LoaderView(showLoader: isLoaderVisible))
.onAppear {
isLoaderVisible.toggle()
}
}
}
struct ProgressBar: View {
#Binding var progress: Float
#State var degree:Double = 90
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 20.0)
.foregroundColor(Color.green)
}
}
}
struct CircularProgressView_Previews: PreviewProvider {
static var previews: some View {
CircularProgressView()
}
}
// enum for LoaderView
public enum LoaderAnimation {
case low, medium, high
var animationSpeed: Double {
switch self {
case .low: return 1.0
case .medium: return 0.8
case .high: return 10.2
}
}
}
This this the LoaderView which we set on overlay and animate
public struct LoaderView: View {
var loaderAnimationSpeed: LoaderAnimation? = .high
var showLoader: Bool = false
public var body: some View {
GeometryReader { reader in
ZStack {
Circle()
.stroke(lineWidth: 20.0)
.opacity(0.3)
.foregroundColor(Color.yellow)
Circle()
.trim(from: 0.6, to: 1)
.stroke(.green, lineWidth: 20)
.rotationEffect(.degrees(showLoader ? 360 : 0))
.animation(Animation.easeInOut(duration: showLoader ? loaderAnimationSpeed?.animationSpeed ?? 0.0 : 1).repeatForever(autoreverses: false), value: showLoader)
}
}
}
public init( loaderAnimationSpeed: LoaderAnimation? = .medium, showLoader: Bool) {
self.loaderAnimationSpeed = loaderAnimationSpeed
self.showLoader = showLoader
}
}
I have a structure, where the parent view is a hidden pop-up with content. On some user action parent View starts to slide up. I need all its content to slide up with the parent View. It was working perfectly before iOS 16, but now it's broken. If the child's View subview #State is changed during the animation, then this View appears instantly on a screen, i.e. not sliding. As I understand, because View's #State was changed SwiftUI redraws this particular View and disables its animation, so it appears in its final position without animation. I was trying to force the animation of this particular View using .animation or withAnimation. Nothing of it helped. How to fix this bug in iOS 16?
Minimal reproducible example:
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
import SwiftUI
struct ContentView: View {
#State var shouldShow: SlideCardPosition = .bottom
#State var text1: String = ""
var body: some View {
VStack {
Button {
shouldShow = .top
} label: {
Text("Show PopUp")
.padding(.top, 100)
}
PopUpUIView(shouldShow: $shouldShow)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
import SwiftUI
struct SlideOverCard<Content: View>: View {
#GestureState private var dragState = SlideDragState.inactive
#Binding private var position: SlideCardPosition
#State private var highlightedBackground = false
private var contentHeight: CGFloat
private var backgroundColor: Color?
private var withCorners: Bool
private var isHandleHidden: Bool
private var overlayOpacity: CGFloat
init(position: Binding<SlideCardPosition>,
contentHeight: CGFloat,
backgroundColor: Color? = nil,
withCorners: Bool = true,
isHandleHidden: Bool = false,
overlayOpacity: CGFloat = 0.75,
content: #escaping () -> Content) {
_position = position
self.content = content
self.contentHeight = contentHeight
self.backgroundColor = backgroundColor
self.withCorners = withCorners
self.isHandleHidden = isHandleHidden
self.overlayOpacity = overlayOpacity
}
var content: () -> Content
var body: some View {
return Rectangle()
.frame(width: UIScreen.screenWidth, height: UIScreen.screenHeight)
.foregroundColor(Color.black.opacity(highlightedBackground ? overlayOpacity : 0))
.position(x: UIScreen.screenWidth / 2, y: (UIScreen.screenHeight) / 2)
.edgesIgnoringSafeArea([.top, .bottom])
.overlay(
Group {
VStack(spacing: 0) {
if !isHandleHidden {
Handle()
}
self.content()
Spacer()
}
}
.frame(width: UIScreen.screenWidth, height: UIScreen.screenHeight)
.background(backgroundColor != nil ? backgroundColor! : Color.black)
.cornerRadius(withCorners ? 40.0 : 0)
.shadow(color: Color(.sRGBLinear, white: 0, opacity: 0.13), radius: 10.0)
.offset(y: position(from: position) + dragState.translation.height)
.animation(dragState.isDragging ? nil : .interpolatingSpring(stiffness: 300.0, damping: 30.0, initialVelocity: 10.0), value: UUID())
.edgesIgnoringSafeArea([.top, .bottom])
.onTapGesture {}
.onChange(of: position) { _ in
withAnimation(.easeInOut) {
highlightedBackground.toggle()
}
}
)
.onTapGesture {
position = position == .bottom ? .top : .bottom
}
}
private func position(from cardPosition: SlideCardPosition) -> CGFloat {
switch cardPosition {
case .top: return UIScreen.screenHeight - contentHeight - UIScreen.topSafeAreaHeight
case .bottom: return 1000
}
}
}
enum SlideCardPosition {
case top
case bottom
}
private enum SlideDragState {
case inactive
case dragging(translation: CGSize)
var translation: CGSize {
switch self {
case .inactive:
return .zero
case .dragging(let translation):
return translation
}
}
var isDragging: Bool {
switch self {
case .inactive:
return false
case .dragging:
return true
}
}
}
private struct Handle: View {
private let handleThickness: CGFloat = 5
var body: some View {
RoundedRectangle(cornerRadius: handleThickness / 2.0)
.frame(width: 34, height: handleThickness)
.foregroundColor(.white)
.padding(.top, 8)
}
}
import UIKit
extension UIScreen {
static let screenWidth = UIScreen.main.bounds.size.width
static let screenHeight = UIScreen.main.bounds.size.height
static let screenSize = UIScreen.main.bounds.size
private static let window = UIApplication.shared.windows[0]
private static let safeFrame = window.safeAreaLayoutGuide.layoutFrame
static var topSafeAreaHeight: CGFloat {
safeFrame.minY
}
static var bottomSafeAreaHeight: CGFloat {
window.frame.maxY - safeFrame.maxY
}
}
import SwiftUI
struct PopUpUIView: View {
#Binding var shouldShow: SlideCardPosition
#State var text1 = "some random text"
var body: some View {
SlideOverCard(position: $shouldShow,
contentHeight: 300) {
VStack(spacing: 10) {
Text(text1)
.foregroundColor(.white)
.padding(.top, 80)
}
}.onChange(of: shouldShow) { _ in
if shouldShow == .top {
text1 = UUID().uuidString
}
}
}
}
struct PopUpUIView_Previews: PreviewProvider {
static var previews: some View {
PopUpUIView(shouldShow: .constant(.bottom))
}
}
Example of incorrect animation with a dynamic text.
Example of what I want to achieve. It is working fine if text is static.
When I press the button, I want to make the blue rectangle smaller and then move it to the right.
In the current code, the animation to make it smaller and the animation to move it to the right happen at the same time.
How do you start an animation after the previous one has finished?
sample code:
import SwiftUI
struct ContentView: View {
#State private var scale: CGFloat = 1
#State private var offsetX: CGFloat = 1
var body: some View {
VStack {
Button(
action: {
withAnimation {
scale = scale - 0.1
}
withAnimation {
offsetX = offsetX + 25
}
},
label: {
Text("Tap Me")
}
)
RectangleView().scaleEffect(
scale
)
.offset(
x: offsetX,
y: 0
)
}
}
}
struct RectangleView: View {
var body: some View {
Rectangle().fill(
Color.blue
)
.frame(
width: 200,
height: 150
)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You can adjust by duration and delay.
struct ContentView: View {
#State private var scale: CGFloat = 1
#State private var offsetX: CGFloat = 1
var body: some View {
VStack {
Button(
action: {
withAnimation(.linear(duration: 0.2)) { //<-- Here
scale = scale - 0.1
}
withAnimation(Animation.default.delay(0.2)) { //<-- Here
offsetX = offsetX + 25
}
},
label: {
Text("Tap Me")
}
)
RectangleView().scaleEffect(
scale
)
.offset(
x: offsetX,
y: 0
)
}
}
}
From the code below, I can't figure out why Preference key is not read from the view put in background of HStack. Strangely, If I put that BGViewSetter to the Text Views inside ForEach loop, it works. Unless I have ommitted something too obvious, it looks like a bug. Anybody can confirm it? Thanks
xcode12.2
ios 14.2
struct TestRectPreference : Equatable
{
var frame : CGRect
}
struct TestRectPreferenceKey : PreferenceKey
{
typealias Value = TestRectPreference
static var defaultValue: TestRectPreference = TestRectPreference(frame: CGRect.zero)
static func reduce(value: inout TestRectPreference, nextValue: () -> TestRectPreference)
{
value.frame = nextValue().frame
}
}
struct BGViewSetter: View {
var body: some View {
GeometryReader { geometry in
RoundedRectangle(cornerRadius: 25.0)
.fill(Color.init(#colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)))
.preference(key: TestRectPreferenceKey.self,
value: TestRectPreference(frame: geometry.frame(in: .global)))
}
}
}
struct FinalView : View
{
#State var offsetX : CGFloat = .zero
#State var info = ""
#State var size : CGFloat = .zero
var body: some View
{
VStack
{
Text("FinalView : \(info)")
HStack
{
ForEach( 1 ..< 10)
{ i in
Text("\(i)")
.frame(width: 100)
.opacity(0.8)
}
}
.background(BGViewSetter())
.animation(.easeOut)
.gesture(
DragGesture()
.onChanged
{ gesture in
self.offsetX = gesture.translation.width
}
.onEnded
{ _ in
self.offsetX = .zero
}
)
.offset(x: self.offsetX)
Text("Footer")
Divider()
Spacer()
}
.onPreferenceChange(TestRectPreferenceKey.self)
{
self.size = $0.frame.height
self.info = "Pref Chng : \(self.size)"
}
}
}
struct PreferenceKeyTest_Previews: PreviewProvider {
static var previews: some View {
FinalView()
}
}
I am trying to move a view when it is being tapped. But unfortunately nothing happens. How do I toggle the position of it using the offset y axis?
struct Test: View {
#State var cards : [Card] = [Card(), Card(), Card(), Card(), Card(), Card()]
var body: some View {
VStack {
ZStack (alignment: .top){
ForEach (0..<cards.count) { i in
RoundedRectangle(cornerRadius: 10).frame(width: 250, height: 150)
.foregroundColor(Color.random).offset(y: self.cards[i].isFocus ? CGFloat(500) : CGFloat(i*50))
.zIndex(Double(i))
.onTapGesture {
self.cards[i].isFocus.toggle() // now the touched Card should move to an offset of y: 500, but nothing happens
}
}
}
Spacer()
}
}
}
struct Card :Identifiable{
#State var isFocus :Bool = false
var id = UUID()
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}
extension Color {
static var random: Color {
return Color(red: .random(in: 0...1),
green: .random(in: 0...1),
blue: .random(in: 0...1))
}
}
I see your code and I am sorry to say but its wrong in many aspects. I would recommend you to go through SwiftUI basics and understand how and when we use #State, #ObservedObject, #Binding etc. Also always try to break your Views into the smallest components.
Below is the code which I think fulfills your requirement.
struct Test: View {
var cards : [Card] = [Card(), Card(), Card(), Card(), Card(), Card()]
var body: some View {
VStack {
ZStack (alignment: .top){
ForEach (0..<cards.count) { i in
CardView(card: self.cards[i], i: i)
}
}
Spacer()
}
}
}
struct CardView: View {
#ObservedObject var card: Card
let i: Int
var body: some View {
RoundedRectangle(cornerRadius: 10).frame(width: 250, height: 150)
.foregroundColor(Color.random)
.offset(y: card.isFocus ? CGFloat(500) : CGFloat(i*10))
.zIndex(Double(i))
.onTapGesture {
withAnimation {
self.card.isFocus.toggle()
}
}
}
}
class Card: ObservableObject{
#Published var isFocus: Bool = false
var id = UUID()
}