Skip to content
This repository was archived by the owner on Dec 23, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions DragAndDrop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
/* Begin PBXBuildFile section */
52C3B2371C7CD0BA00F3151C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C3B2361C7CD0BA00F3151C /* AppDelegate.swift */; };
52C3B2391C7CD0BA00F3151C /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C3B2381C7CD0BA00F3151C /* ViewController.swift */; };
52C3B23C1C7CD0BA00F3151C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52C3B23A1C7CD0BA00F3151C /* Main.storyboard */; };
52C3B23E1C7CD0BA00F3151C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 52C3B23D1C7CD0BA00F3151C /* Assets.xcassets */; };
52C3B2411C7CD0BA00F3151C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52C3B23F1C7CD0BA00F3151C /* LaunchScreen.storyboard */; };
52C3B24C1C7CD0BA00F3151C /* DragAndDropTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C3B24B1C7CD0BA00F3151C /* DragAndDropTests.swift */; };
F6BAF15C1C8CC49200B6BD66 /* DraggableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6BAF15B1C8CC49200B6BD66 /* DraggableView.swift */; };
F6DCCAED1C8CBAE0005A8A4E /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = F6DCCAEC1C8CBAE0005A8A4E /* View.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -29,13 +30,14 @@
52C3B2331C7CD0BA00F3151C /* DragAndDrop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DragAndDrop.app; sourceTree = BUILT_PRODUCTS_DIR; };
52C3B2361C7CD0BA00F3151C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
52C3B2381C7CD0BA00F3151C /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
52C3B23B1C7CD0BA00F3151C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
52C3B23D1C7CD0BA00F3151C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
52C3B2401C7CD0BA00F3151C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
52C3B2421C7CD0BA00F3151C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
52C3B2471C7CD0BA00F3151C /* DragAndDropTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DragAndDropTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
52C3B24B1C7CD0BA00F3151C /* DragAndDropTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DragAndDropTests.swift; sourceTree = "<group>"; };
52C3B24D1C7CD0BA00F3151C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F6BAF15B1C8CC49200B6BD66 /* DraggableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DraggableView.swift; sourceTree = "<group>"; };
F6DCCAEC1C8CBAE0005A8A4E /* View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -79,7 +81,8 @@
children = (
52C3B2361C7CD0BA00F3151C /* AppDelegate.swift */,
52C3B2381C7CD0BA00F3151C /* ViewController.swift */,
52C3B23A1C7CD0BA00F3151C /* Main.storyboard */,
F6BAF15B1C8CC49200B6BD66 /* DraggableView.swift */,
F6DCCAEC1C8CBAE0005A8A4E /* View.swift */,
52C3B23D1C7CD0BA00F3151C /* Assets.xcassets */,
52C3B23F1C7CD0BA00F3151C /* LaunchScreen.storyboard */,
52C3B2421C7CD0BA00F3151C /* Info.plist */,
Expand Down Expand Up @@ -179,7 +182,6 @@
files = (
52C3B2411C7CD0BA00F3151C /* LaunchScreen.storyboard in Resources */,
52C3B23E1C7CD0BA00F3151C /* Assets.xcassets in Resources */,
52C3B23C1C7CD0BA00F3151C /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -199,6 +201,8 @@
files = (
52C3B2391C7CD0BA00F3151C /* ViewController.swift in Sources */,
52C3B2371C7CD0BA00F3151C /* AppDelegate.swift in Sources */,
F6DCCAED1C8CBAE0005A8A4E /* View.swift in Sources */,
F6BAF15C1C8CC49200B6BD66 /* DraggableView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -221,14 +225,6 @@
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
52C3B23A1C7CD0BA00F3151C /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
52C3B23B1C7CD0BA00F3151C /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
52C3B23F1C7CD0BA00F3151C /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
Expand Down Expand Up @@ -388,6 +384,7 @@
52C3B2521C7CD0BA00F3151C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
52C3B2531C7CD0BA00F3151C /* Build configuration list for PBXNativeTarget "DragAndDropTests" */ = {
isa = XCConfigurationList;
Expand All @@ -396,6 +393,7 @@
52C3B2551C7CD0BA00F3151C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
3 changes: 3 additions & 0 deletions DragAndDrop/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
return true
}

Expand Down
6 changes: 3 additions & 3 deletions DragAndDrop/Base.lproj/LaunchScreen.storyboard
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9532" systemVersion="14F1605" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9530"/>
</dependencies>
<scenes>
<!--View Controller-->
Expand All @@ -15,7 +16,6 @@
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
Expand Down
25 changes: 0 additions & 25 deletions DragAndDrop/Base.lproj/Main.storyboard

This file was deleted.

73 changes: 73 additions & 0 deletions DragAndDrop/DraggableView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// Drag.swift
// DragAndDrop
//
// Created by Stanley Chiang on 3/6/16.
// Copyright © 2016 SantaClaraiOSConnect. All rights reserved.
//

import UIKit

protocol draggableViewDelegate {
func didDragToTrash() -> Bool
}

class DraggableView: UIView {

var panGestureRecognizer:UIPanGestureRecognizer!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would love to see this not be an ImplicitlyUnwrappedOptional 😄

For this one specifically we can just initialize it directly.
let panGestureRecognizer = UIPanGestureRecognizer()
Then remove the declaration from init()

var initialPosition:CGPoint!

var delegate:draggableViewDelegate!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delegate should be weak. If not there is good chance of a retain cycle.


override init(frame: CGRect) {
super.init(frame: frame)
initialPosition = frame.origin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps move this saving of the initialPosition to where the PanGesture starts?

This is a win for me on 2 points:

  • Moves stuff out of initializer
  • Moves the variable initial value closer to where it is actually used.

panGestureRecognizer = UIPanGestureRecognizer()
panGestureRecognizer.addTarget(self, action: "updatePosition:")
addGestureRecognizer(panGestureRecognizer)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func updatePosition(sender: UIPanGestureRecognizer) {
let translation = sender.translationInView(self)

frame.origin.x = initialPosition.x + translation.x
frame.origin.y = initialPosition.y + translation.y

let willTrash: Bool = delegate.didDragToTrash()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: don't force the type to be Bool, let the compiler do it. (this avoids issues where the function call might return a Bool? but you are forcing it to Bool and sadness ensues.


if willTrash {
layer.borderColor = UIColor.redColor().CGColor
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way to make the layer.BorderColor always red?

layer.borderWidth = 3
} else {
layer.borderWidth = 0
}

if panGestureRecognizer.state == UIGestureRecognizerState.Ended {
if !willTrash {
snapToOriginAnimation()
} else {
trashActionAnimation()
}
}
}

func snapToOriginAnimation() {
UIView.animateWithDuration(0.2) { () -> Void in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop the () -> Void in the closure signature.
(same comment below)

self.frame.origin = self.initialPosition
}
}

func trashActionAnimation() {
UIView.animateWithDuration(0.1, animations: { () -> Void in
self.alpha = 0
}) { (complete) -> Void in
if complete {
self.removeFromSuperview()
}
}
}
}
2 changes: 0 additions & 2 deletions DragAndDrop/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
Expand Down
72 changes: 72 additions & 0 deletions DragAndDrop/View.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// View.swift
// DragAndDrop
//
// Created by Stanley Chiang on 3/6/16.
// Copyright © 2016 SantaClaraiOSConnect. All rights reserved.
//

import UIKit

protocol ViewDelegate {
func reset()
}

class View: UIView, draggableViewDelegate {

var draggableView:DraggableView!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More ImplicitlyUnwrappedOptionals 😟
Just initialize your views here.

Also let all the things that you can.
let draggableView = initDraggableView()

var trashArea:UIView!
var resetButton:UIButton!

var delegate:ViewDelegate!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weak this delegate.


override init(frame: CGRect) {
super.init(frame: frame)

trashArea = initTrashArea()
addSubview(trashArea)

resetButton = initResetButton()
addSubview(resetButton)

draggableView = initDraggableView()
addSubview(draggableView)

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is on the initializer.

We don't need it. Since we are already init-ing our views above.
We should add our subViews by

override layoutSubviews() {
    addSubView(trashArea)
    ...
}

Once we have done that we don't need this initializer any more,
And for the super win we don't need that stupid required init?(coder aDecoder: NSCoder) one either 💯


required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func initDraggableView() -> DraggableView {
let view = DraggableView(frame: CGRectMake(100,100,100,100))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would love to see this using Auto Layout. 😄

I tried this and what I ended up doing was init with frame zero, then have another function also called in layoutSubviews called constrainTrashArea() then put all the auto layout gunk in that function.

view.delegate = self
view.backgroundColor = UIColor.orangeColor()
return view
}

func initTrashArea() -> UIView {
let view = UIView(frame: CGRectMake(0, frame.height * 2 / 3,frame.width,frame.height / 3 - 50))
view.backgroundColor = UIColor.lightGrayColor()
return view
}

func initResetButton() -> UIButton {
let button = UIButton(frame: CGRectMake(0,frame.height - 50,frame.width, 50))
button.setTitle("Reset", forState: UIControlState.Normal)
button.setTitleColor(UIColor.lightTextColor(), forState: UIControlState.Normal)
button.backgroundColor = UIColor.darkGrayColor()

// TODO: learn more about what it means to pass target as self all the time
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could call button.addTarget(delegate, action "reset", forControlEvents: .TouchUpInside)

However this will not work since you delegate is not necessarily an NSObject subclass, and might not know how to to magical target action stuff.
If you mark your delegate as @objc then this might work. Maybe.
(The way you have it is great. Just wanted to expand upon your note here.) You are correct the object does not always have to be self. it just often is.

button.addTarget(self, action: "resetAction:", forControlEvents: UIControlEvents.TouchUpInside)
return button
}

func resetAction(sender: UIButton){
delegate.reset()
}

func didDragToTrash() -> Bool {
return draggableView.frame.intersects(trashArea.frame)
}
}
16 changes: 14 additions & 2 deletions DragAndDrop/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,30 @@

import UIKit

class ViewController: UIViewController {
class ViewController: UIViewController, ViewDelegate {

var mainView:View!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More 😟

This one is a bit trickier to get rid of...
We can't just initialize this one here, since it depends on self.frame
And self is not guaranteed to be valid at init time.
We could make this a full blown optional, I would vote that as an improvement.

My suggestion is to use lazy evaluation. We can put the lazy keyword in front of this and then instantiate it with a closure. The closure is then run the first time the value is accessed.

lazy var mainView: View = {
    let v = View(frame: self.view.frame)
    v.delegate = self
    self.view.addSubView(v)
    return v
}()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My other note is I wish we did not need this main view, perhaps instead of creating this the adding it as a subview we should be overriding loadView() to set the ViewController's view property to use our custom view subclass. I think this might be the correct choice.


override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = UIColor.whiteColor()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the default?

mainView = View(frame: self.view.frame)
mainView.delegate = self
self.view.addSubview(mainView)
}

override func didReceiveMemoryWarning() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove Unused Function.

super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func reset() {
mainView.removeFromSuperview()

mainView = View(frame: self.view.frame)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe combine these repeated lines in some way.
maybe just in a function called

initMainView() -> View
Then you could use it here and for the closure at the top.

mainView.delegate = self
self.view.addSubview(mainView)
}

}