Skip to content

Instantly share code, notes, and snippets.

@CastIrony
Created July 6, 2020 04:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CastIrony/01301e37123e8b7505eecbf3f3e0ec59 to your computer and use it in GitHub Desktop.
Save CastIrony/01301e37123e8b7505eecbf3f3e0ec59 to your computer and use it in GitHub Desktop.
//
// ContentView.swift
//
//
// Created by Bernstein, Joel on 7/4/20.
//
import SwiftUI
struct ContentView: View
{
var body: some View
{
ZStack
{
Color(white: 0.9).edgesIgnoringSafeArea(.all)
RoundedRectangle(cornerRadius: 15.0, style: .continuous)
.fill(Color.white)
.overlay(VStack(spacing: 15)
{
Text("Hello World!").font(.system(.largeTitle))
Text("Foo").font(.system(.headline))
Text("Bar").font(.system(.caption))
})
.modifier(DragResizable(frame: CGRect(x: 50, y: 270, width: 290, height: 250)))
}
}
}
struct ContentView_Previews: PreviewProvider
{
static var previews: some View
{
Group
{
ContentView()
}
}
}
struct HandleView: View
{
@Binding var frame: CGRect
@State var initialDragDelta: CGSize?
@State var isDragging = false
let anchorPostion: Alignment
let borderThickness: CGFloat = 2
let handleSizeRegular: CGFloat = 12
let handleSizeDragging: CGFloat = 50
var handleSize: CGFloat { isDragging ? handleSizeDragging : handleSizeRegular }
var handlePosition: CGPoint
{
var x: CGFloat
var y: CGFloat
switch anchorPostion.horizontal
{
case .leading: x = frame.minX
case .trailing: x = frame.maxX
default: x = frame.midX
}
switch anchorPostion.vertical
{
case .top: y = frame.minY
case .bottom: y = frame.maxY
default: y = frame.midY
}
return CGPoint(x: x, y: y)
}
func updateHandlePosition(newValue: CGPoint)
{
var minX = frame.minX
var maxX = frame.maxX
var minY = frame.minY
var maxY = frame.maxY
switch anchorPostion.horizontal
{
case .leading: minX = newValue.x - (initialDragDelta?.width ?? 0)
case .trailing: maxX = newValue.x - (initialDragDelta?.width ?? 0)
default: break
}
switch anchorPostion.vertical
{
case .top: minY = newValue.y - (initialDragDelta?.height ?? 0)
case .bottom: maxY = newValue.y - (initialDragDelta?.height ?? 0)
default: break
}
frame = CGRect(x: minX, y: minY, width: maxX - minX, height: maxY - minY)
}
var body: some View
{
ZStack
{
RoundedRectangle(cornerRadius: (isDragging ? 12 : 4), style: .continuous)
.fill(Color.black)
.frame(width: handleSize + 2 * borderThickness, height: handleSize + 2 * borderThickness, alignment: .center)
RoundedRectangle(cornerRadius: (isDragging ? 10 : 2), style: .continuous)
.fill(Color.white)
.frame(width: handleSize, height: handleSize, alignment: .center)
}
.frame(width: 60, height: 60, alignment: .center)
.contentShape(Rectangle())
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
.onChanged
{
value in
if initialDragDelta == nil
{
initialDragDelta = CGSize(width: value.startLocation.x - handlePosition.x, height: value.startLocation.y - handlePosition.y)
withAnimation(.spring(response: 0.3, dampingFraction:0.3)) { isDragging = true }
}
updateHandlePosition(newValue: value.location)
}
.onEnded
{
value in
initialDragDelta = nil
withAnimation(.spring(response: 0.3, dampingFraction:0.6)) { isDragging = false }
})
.position(handlePosition)
}
}
struct HandleBorder: View
{
@Binding var frame: CGRect
let color:Color
let width:CGFloat
var body: some View
{
ZStack
{
Rectangle()
.stroke(color, lineWidth: width)
.frame(width: frame.width, height: frame.height, alignment: .center)
.position(CGPoint(x: frame.midX, y: frame.midY))
}
}
}
struct DragResizable: ViewModifier
{
@State var frame: CGRect
@State var isSelected = false;
func body(content: Content) -> some View
{
ZStack
{
content
.frame(width: frame.width, height: frame.height, alignment: .center)
.position(CGPoint(x: frame.midX, y: frame.midY))
.onTapGesture { isSelected.toggle() }
if(isSelected)
{
HandleBorder(frame: $frame, color: .black, width: 8)
Group()
{
HandleView(frame: $frame, anchorPostion:Alignment(horizontal: .leading, vertical: .top))
HandleView(frame: $frame, anchorPostion:Alignment(horizontal: .center, vertical: .top))
HandleView(frame: $frame, anchorPostion:Alignment(horizontal: .trailing, vertical: .top))
HandleView(frame: $frame, anchorPostion:Alignment(horizontal: .leading, vertical: .center))
HandleView(frame: $frame, anchorPostion:Alignment(horizontal: .trailing, vertical: .center))
HandleView(frame: $frame, anchorPostion:Alignment(horizontal: .leading, vertical: .bottom))
HandleView(frame: $frame, anchorPostion:Alignment(horizontal: .center, vertical: .bottom))
HandleView(frame: $frame, anchorPostion:Alignment(horizontal: .trailing, vertical: .bottom))
}
HandleBorder(frame: $frame, color: .white, width: 4)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment