How to use "shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer" - swift

I am trying to programmatically create 6 CGRect objects on the right side of the screen, with three being blue and three red. The user would then drag them to the left side of the screen, and they need to drag the blue blocks where the target is the faded-out region of the same color. The picture below depicts what I am trying to do.
The issue is that when I drag any block over any of the faded areas, it accepts the block and deletes it. I realize that I need to implement shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer but I am confused on how to do so. I get that gestureRecognizer and otherGestureRecognizer represent the two conflicting objects, but I don't know how to implement it with the rest of my code so that I can make sure that only blue blocks can go over the faded blue block and activate the delete feature. What I have is below.
import UIKit
class ViewController: UIViewController {
private struct Constants {
static let padding: CGFloat = 75
static let correctPadding: CGFloat = 0
static let blockDimension: CGFloat = 50
}
var targetView0: UIView?
var targetView1: UIView?
var targetView2: UIView?
var targetView3: UIView?
var targetView4: UIView?
var targetView5: UIView?
var beginningPosition: CGPoint = .zero
var initialMovableViewPosition: CGPoint = .zero
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
addMovableViews(count: 6)
addtargetView0()
addtargetView1()
addtargetView2()
addtargetView3()
addtargetView4()
addtargetView5()
}
//This programtically creates the blocks w/ x and y
private func addMovableViews(count: Int) {
var xOffset = Constants.padding
for i in 0..<count {
print(i)
let movableView = UIView(frame: CGRect(x: 700, y: xOffset + 120, width: Constants.blockDimension, height: Constants.blockDimension))
if (i == 0) {
movableView.backgroundColor = .blue
view.addSubview(movableView)
let item0 = UIPanGestureRecognizer(target: self, action: #selector(touched))
movableView.addGestureRecognizer(item0)
} else if (i == 1) {
movableView.backgroundColor = .red
view.addSubview(movableView)
let item1 = UIPanGestureRecognizer(target: self, action: #selector(touched))
movableView.addGestureRecognizer(item1)
} else if (i == 2) {
movableView.backgroundColor = .blue
view.addSubview(movableView)
let item2 = UIPanGestureRecognizer(target: self, action: #selector(touched))
movableView.addGestureRecognizer(item2)
} else if (i == 3) {
movableView.backgroundColor = .red
view.addSubview(movableView)
let item3 = UIPanGestureRecognizer(target: self, action: #selector(touched))
movableView.addGestureRecognizer(item3)
} else if (i == 4) {
movableView.backgroundColor = .blue
view.addSubview(movableView)
let item4 = UIPanGestureRecognizer(target: self, action: #selector(touched))
movableView.addGestureRecognizer(item4)
} else if (i == 5) {
movableView.backgroundColor = .red
view.addSubview(movableView)
let item5 = UIPanGestureRecognizer(target: self, action: #selector(touched))
movableView.addGestureRecognizer(item5)
} else if (i == 6) {
movableView.backgroundColor = .blue
view.addSubview(movableView)
let item6 = UIPanGestureRecognizer(target: self, action: #selector(touched))
movableView.addGestureRecognizer(item6)
}
xOffset += Constants.blockDimension + Constants.padding
}
}
private func addtargetView0() {
let xOffset = Constants.correctPadding
targetView0 = UIView(frame: CGRect(x: xOffset + 100, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
targetView0?.backgroundColor = UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)
view.addSubview(targetView0!)
}
private func addtargetView1() {
let xOffset = Constants.correctPadding
targetView1 = UIView(frame: CGRect(x: 100 + xOffset + 50, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
targetView1?.backgroundColor = UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)
view.addSubview(targetView1!)
}
private func addtargetView2() {
let xOffset = Constants.correctPadding
targetView2 = UIView(frame: CGRect(x: 100 + xOffset + 100, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
targetView2?.backgroundColor = UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)
view.addSubview(targetView2!)
}
private func addtargetView3() {
let xOffset = Constants.correctPadding
targetView3 = UIView(frame: CGRect(x: 100 + xOffset + 150, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
targetView3?.backgroundColor = UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)
view.addSubview(targetView3!)
}
private func addtargetView4() {
let xOffset = Constants.correctPadding
targetView4 = UIView(frame: CGRect(x: 100 + xOffset + 200, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
targetView4?.backgroundColor = UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)
view.addSubview(targetView4!)
}
private func addtargetView5() {
let xOffset = Constants.correctPadding
targetView5 = UIView(frame: CGRect(x: 100 + xOffset + 250, y: 500, width: Constants.blockDimension, height: Constants.blockDimension))
targetView5?.backgroundColor = UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)
view.addSubview(targetView5!)
}
//Gesture recognizer
#objc private func touched(_ gestureRecognizer: UIGestureRecognizer) {
if let touchedView = gestureRecognizer.view {
if gestureRecognizer.state == .began {
beginningPosition = gestureRecognizer.location(in: touchedView)
initialMovableViewPosition = touchedView.frame.origin
} else if gestureRecognizer.state == .ended {
//Moves it back to where it was
// touchedView.frame.origin = initialMovableViewPosition
} else if gestureRecognizer.state == .changed {
let locationInView = gestureRecognizer.location(in: touchedView)
touchedView.frame.origin = CGPoint(x: touchedView.frame.origin.x + locationInView.x - beginningPosition.x, y: touchedView.frame.origin.y + locationInView.y - beginningPosition.y)
if touchedView.frame.intersects(targetView0!.frame) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView0?.backgroundColor = .blue
initialMovableViewPosition = .zero
}
if touchedView.frame.intersects(targetView1!.frame) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView1?.backgroundColor = .red
initialMovableViewPosition = .zero
}
if touchedView.frame.intersects(targetView2!.frame) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView2?.backgroundColor = .blue
initialMovableViewPosition = .zero
}
if touchedView.frame.intersects(targetView3!.frame) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView3?.backgroundColor = .red
initialMovableViewPosition = .zero
}
if touchedView.frame.intersects(targetView4!.frame) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView4?.backgroundColor = .blue
initialMovableViewPosition = .zero
}
if touchedView.frame.intersects(targetView5!.frame) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView5?.backgroundColor = .red
initialMovableViewPosition = .zero
}
}
}
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer)
-> Bool {
// If the gesture recognizers are on diferent views, do not allow
// simultaneous recognition.
if gestureRecognizer.view != otherGestureRecognizer.view {
return false
}
return true
}
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Can someone explain how to incorporate shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer, link a complete project doing something similar, or walk me through it. Any help is appreciated.

There are a few issues here. Firstly, the delegate for your gestures are not being set. To do this, you need ViewController to extend UIGestureRecognizerDelegate like so class ViewController: UIViewController, UIGestureRecognizerDelegate. Then, you need to set the delegate of each of your gestures to your ViewController:
let item0 = UIPanGestureRecognizer(target: self, action: #selector(touched))
item0.delegate = self
movableView.addGestureRecognizer(item0)
Second, even with the delegates set, shouldRecognizeSimultaneouslyWith would never be called since you only have one type of gesture (UIPanGestureRecognizer) for each of your moveable views. Your target views themselves do not have any gesture recognisers assigned to them. If you want to test when shouldRecognizeSimultaneouslyWith is called, you can try do the following:
let item0 = UIPanGestureRecognizer(target: self, action: #selector(touched))
item0.delegate = self
movableView.addGestureRecognizer(item0)
// Add another gesture
let testitem = UITapGestureRecognizer(target: self, action: #selector(test))
movableView.addGestureRecognizer(testitem)
With the example above, both UITapGestureRecognizer and UIPanGestureRecognizer would be triggered the moment you start dragging the moveable views.
=== Solution ===
Instead of using shouldRecognizeSimultaneouslyWith might I suggest adding tags to each of your moveable and target views? For example, for all blue moveable and target views, movableView.tag = 0 and for all red moveable and target views, movableView.tag = 1.
Then, in your touched action, you can just check if the tags match like so:
if touchedView.frame.intersects(targetView0!.frame) {
if touchedView.tag == targetView0?.tag {
touchedView.removeFromSuperview()
targetView0?.backgroundColor = .blue
initialMovableViewPosition = .zero
}
}

I figured it out. It's basically just in the gesture if statement.
#objc private func touched(_ gestureRecognizer: UIGestureRecognizer) {
if let touchedView = gestureRecognizer.view {
if gestureRecognizer.state == .began {
beginningPosition = gestureRecognizer.location(in: touchedView)
initialMovableViewPosition = touchedView.frame.origin
} else if gestureRecognizer.state == .ended {
//Moves it back to where it was
// touchedView.frame.origin = initialMovableViewPosition
} else if gestureRecognizer.state == .changed {
let locationInView = gestureRecognizer.location(in: touchedView)
touchedView.frame.origin = CGPoint(x: touchedView.frame.origin.x + locationInView.x - beginningPosition.x, y: touchedView.frame.origin.y + locationInView.y - beginningPosition.y)
if ((touchedView.backgroundColor == .blue) && (targetView0?.backgroundColor == UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView0!.frame))) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView0?.backgroundColor = .blue
initialMovableViewPosition = .zero
}
if ((touchedView.backgroundColor == .red) && (targetView1?.backgroundColor == UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView1!.frame))) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView1?.backgroundColor = .red
initialMovableViewPosition = .zero
}
if ((touchedView.backgroundColor == .blue) && (targetView2?.backgroundColor == UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView2!.frame))) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView2?.backgroundColor = .blue
initialMovableViewPosition = .zero
}
if ((touchedView.backgroundColor == .red) && (targetView3?.backgroundColor == UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView3!.frame))) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView3?.backgroundColor = .red
initialMovableViewPosition = .zero
}
if ((touchedView.backgroundColor == .blue) && (targetView4?.backgroundColor == UIColor(red: 51/255.0, green: 153/255.0, blue: 255/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView4!.frame))) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView4?.backgroundColor = .blue
initialMovableViewPosition = .zero
}
if ((touchedView.backgroundColor == .red) && (targetView5?.backgroundColor == UIColor(red: 255/255.0, green: 102/255.0, blue: 102/255.0, alpha: 1)) && (touchedView.frame.intersects(targetView5!.frame))) {
touchedView.removeFromSuperview() //Might uncomment when everything is set up so it deals with the overlay issue
// gestureRecognizer.isEnabled = false
targetView5?.backgroundColor = .red
initialMovableViewPosition = .zero
}
}
}
}
You check if the gestureRecognizer background color is blue, check if the targetView background color is the faded blue via UIColor, and see if the view intersects, and then you do what you need. You can even do it to fill the target view in the order you want with an additional if statement.

Related

Getting trouble when hovering a custom segmented control in swift

I tried to create custom segmented control which should change its look if you are hovering it. But it does not work as I expected.
Here's the code (I am sorry that it is that long but I already made it 200 lines shorter then it was):
class MySegmentedControl: UIView {
var state = MySegmentedControl_State.unknown
var buttons = [MySegmentedControl_ButtonView]()
func setUpView(){
let view = UIView(frame: self.frame)
view.frame.origin.x = 0
view.frame.origin.y = 0
view.addSubview(holderView())
self.addSubview(view)
}
func holderView() -> UIView{
let view = UIView(frame: CGRect(origin: CGPoint(x: 100, y: 0), size: CGSize(width: 490, height: 70)))
view.layer.borderWidth = 5
view.layer.borderColor = UIColor.gray.cgColor
view.layer.cornerRadius = view.frame.height / 2
view.layer.masksToBounds = true
view.clipsToBounds = true
view.backgroundColor = #colorLiteral(red: 0.5741485357, green: 0.5741624236, blue: 0.574154973, alpha: 1)
//place the first button
let button_1 = MySegmentedControl_ButtonView(text: "One", frame: CGRect(x: 0, y: 0, width: 163.3333, height: 70), type: .one, delegate: self)
//place the second Button
let button_2 = MySegmentedControl_ButtonView(text: "Two", frame: CGRect(x: 163.3333, y: 0, width: 163.3333, height: 70), type: .two, delegate: self)
//place the third Button
let button_3 = MySegmentedControl_ButtonView(text: "Three", frame: CGRect(x: 163.3333*2, y: 0, width: 163.3333, height: 70), type: .three, delegate: self)
buttons.append(button_1); buttons.append(button_2); buttons.append(button_3)
view.addSubview(button_1)
view.addSubview(button_2)
view.addSubview(button_3)
return view
}
class MySegmentedControl_ButtonView: UIView{
private var selected = false
private var hovering = false
var text = ""
private var type: MySegmentedControl_State
private var delegate: MySegmentedControl_ButtonView_Delegate
private var label = UILabel()
func setUpView(){
layer.cornerRadius = frame.height/2
let label = UILabel(frame: frame)
self.label = label
label.tintColor = .white
label.text = text
label.sizeToFit()
label.center = self.center
label.font = label.font.withSize(15)
let regionizer = UIHoverGestureRecognizer(target: self, action: #selector(didHover(_:)))
addGestureRecognizer(regionizer)
addSubview(label)
//set up the button
let button = UIButton(frame: bounds, primaryAction: UIAction(handler: { [self] action in
selected = true
delegate.shouldChangeState(to: type)
delegate.shouldDeselectOthers(without: text)
if !hovering{
backgroundColor = #colorLiteral(red: 0.9961533629, green: 0.9931518435, blue: 1, alpha: 0.8)
}else{
self.backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.9435433103)
}
}))
addSubview(button)
}
func deselect(){
self.selected = false
if hovering{
backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.15)
UIView.animate(withDuration: 0.2) {[self]in
label.frame.origin.y = 10
label.font = label.font.withSize(12)
}
}else{
backgroundColor = #colorLiteral(red: 0.2605174184, green: 0.2605243921, blue: 0.260515637, alpha: 0.15)
UIView.animate(withDuration: 0.2) {[self]in
label.frame.origin.y = 10
label.font = label.font.withSize(12)
}
}
}
#objc
func didHover(_ recognizer: UIHoverGestureRecognizer){
switch recognizer.state{
case .began, .changed:
hovering = true
if selected{
backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.15)
UIView.animate(withDuration: 0.2) {[self]in
label.frame.origin.y = 10
label.font = label.font.withSize(12)
}
}else{
backgroundColor = #colorLiteral(red: 0.2605174184, green: 0.2605243921, blue: 0.260515637, alpha: 0.15)
UIView.animate(withDuration: 0.2) {[self]in
label.frame.origin.y = 10
label.font = label.font.withSize(12)
}
}
case .ended:
hovering = false
if selected{
self.backgroundColor = #colorLiteral(red: 0.9961533629, green: 0.9931518435, blue: 1, alpha: 0.2)
UIView.animate(withDuration: 0.2) {[self]in
label.center.y = center.y
label.font = label.font.withSize(15)
}
}else{
self.backgroundColor = .clear
UIView.animate(withDuration: 0.2) {[self]in
label.center.y = center.y
label.font = label.font.withSize(15)
}
}
default:break
}
}
init(text: String, frame: CGRect, type: MySegmentedControl_State, delegate: MySegmentedControl_ButtonView_Delegate){
self.type = type
self.delegate = delegate
super.init(frame: frame)
self.text = text
setUpView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
}
extension MySegmentedControl: MySegmentedControl_ButtonView_Delegate{
func shouldDeselectOthers(without: String) {
for button in buttons{
if button.text != without{
button.deselect()
}
}
}
func shouldChangeState(to state: MySegmentedControl_State) {
self.state = state
}
}
protocol MySegmentedControl_ButtonView_Delegate{
func shouldDeselectOthers(without: String)
func shouldChangeState(to state: MySegmentedControl_State)
}
enum MySegmentedControl_State: String{
case unknown = "Non specific case avalaible"
case one = "One selected"
case two = "Two selected"
case three = "Three selected"
}
But the second of my buttons is displayed as the third and the the third does not get displayed, but if I hover the place where button two SHOULD be, it still gets hovered.
Here is a shout video:
Video showing problems with this code
My App is running on MacOS with MacCatalyst
In setUpView() inside class MySegmentedControl_ButtonView, you are trying to set the label's position with:
label.center = self.center
However, self.center is relative to self's superview. So your labels are being shifted.
If you change that line to:
label.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
the label centering will be correct.
As I asked in my comment though... why aren't you using auto-layout? It would make things much easier (and much more flexible).

How to add Animation Button like this?

How can I create an animated and shadowed button like in this video
(Once you click the button, it shines and swings)
Here is my code:
(This code implement swing and shadow but needs to be organized and arranged so that it is capable of running the light and the swing with one click and the button doesn't move from its place unnecessarily)
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var aBtn: ButtonWithShadow!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func btnClick(_ sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
sender.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.50).cgColor
sender.layer.shadowOffset = CGSize(width: 0, height: 3)
sender.layer.shadowOpacity = 1.0
sender.layer.shadowRadius = 10.0
sender.layer.masksToBounds = false
} else {
sender.isSelected = true
sender.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.0).cgColor
}
aBtn.layer.anchorPoint = CGPoint(x: 0.5, y: 0)
aBtn.layer.transform = CATransform3DMakeRotation(-.pi / 15, 0, 0, 1)
let needleAnim = CABasicAnimation(keyPath: "transform.rotation.z")
needleAnim.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
needleAnim.duration = 1.0 as? CFTimeInterval ?? CFTimeInterval()
needleAnim.repeatCount = 5
needleAnim.autoreverses = true
// Setting fillMode means that, once the animation finishes, the needle stays in its end position.
needleAnim.fillMode = kCAFillModeForwards
needleAnim.isRemovedOnCompletion = true
needleAnim.toValue = Double.pi / 15
aBtn.layer.add(needleAnim, forKey: nil)
}
}
and
import UIKit
class ButtonWithShadow: UIButton {
override func draw(_ rect: CGRect) {
//updateLayerProperties()
}
}
Thanks for your time
Your code works as written and provides a swinging button when clicked. Make sure you control-click dragged from the button to the button definition.
You'll need to add two images to your button, one for the off state and one for the on state.
and on
Here is the intermediate result of adding setting the images.
and the way to change the images is to add them to the assets catalog and then change them with :
if sender.isSelected {
sender.isSelected = false
sender.setImage(#imageLiteral(resourceName: "off"), for: .normal)
sender.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.50).cgColor
sender.layer.shadowOffset = CGSize(width: 0, height: 3)
sender.layer.shadowOpacity = 1.0
sender.layer.shadowRadius = 10.0
sender.layer.masksToBounds = false
} else {
sender.isSelected = true
sender.setImage(#imageLiteral(resourceName: "onbtn"), for: .normal)
sender.layer.shadowColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.0).cgColor
}

Subview Not Removing after Several "Remove View" Methods

I have this nice function that adds an activity indicator view, with a regular view behind it. When I add it, it adds fine. The problem is, when I try to remove it, nothing happens. I've tried:
.removeFromSuperview,
.isHidden = true and putting these methods in the main queue:
DispatchQueue.main.async() {
alertView.alpha = 0
alertView.removeFromSuperview()
activityIndicator.removeFromSuperview()
alertView.isHidden = true
activityIndicator.isHidden = true
}
I don't know what other methods to try...It seems all the other questions like this have one of the methods that I have as a solution. The function uses a boolean to determine whether or not to stop the activityIndicator. Here is my code:
static func showLoadingView(inViewController: UIViewController, turning: Bool){
let activityIndicator = UIActivityIndicatorView()
let alertView = UIView(frame: CGRect(x: activityIndicator.frame.origin.x, y: activityIndicator.frame.origin.y , width: 35, height: 35))
if(turning){
alertView.backgroundColor = UIColor(displayP3Red: 230, green: 230, blue: 230, alpha: 0.8)
alertView.layer.cornerRadius = 5
activityIndicator.center = alertView.center
inViewController.view.addSubview(alertView)
activityIndicator.activityIndicatorViewStyle = .gray
activityIndicator.startAnimating()
alertView.alpha = 0
activityIndicator.backgroundColor = UIColor.lightGray
alertView.center = inViewController.view.center
alertView.addSubview(activityIndicator)
alertView.transform = CGAffineTransform.init(scaleX: 1.3,y: 1.3)
UIView.animate(withDuration: 0.4) {
alertView.alpha = 1
alertView.transform = CGAffineTransform.identity
}
}
///////
else {
activityIndicator.stopAnimating()
alertView.transform = CGAffineTransform.init(scaleX: 1.3, y:1.3)
DispatchQueue.main.async() {
alertView.alpha = 0
alertView.removeFromSuperview()
activityIndicator.removeFromSuperview()
alertView.isHidden = true
activityIndicator.isHidden = true
}
print("Done")
}
}
You declared alertView which is out of scope when you called false.As a result, your call could not identify the alertView instance.
You can solve this issue in tow ways:
Declare your alertView outside of the function as a static.
static let activityIndicator = UIActivityIndicatorView()
static let alertView = UIView(frame: CGRect(x: activityIndicator.frame.origin.x, y: activityIndicator.frame.origin.y , width: 35, height: 35))
static func showLoadingView(inViewController: UIViewController, turning: Bool){}
In false case: find the subview using restorationIdentifier and remove.
static func showLoadingView(inViewController: UIViewController, turning: Bool){
if(turning){
let activityIndicator = UIActivityIndicatorView()
let alertView = UIView(frame: CGRect(x: activityIndicator.frame.origin.x, y: activityIndicator.frame.origin.y , width: 35, height: 35))
alertView.restorationIdentifier = "myalert"
alertView.backgroundColor = UIColor(displayP3Red: 230, green: 230, blue: 230, alpha: 0.8)
alertView.layer.cornerRadius = 5
activityIndicator.center = alertView.center
inViewController.view.addSubview(alertView)
activityIndicator.activityIndicatorViewStyle = .gray
activityIndicator.startAnimating()
alertView.alpha = 0
activityIndicator.backgroundColor = UIColor.lightGray
alertView.center = inViewController.view.center
alertView.addSubview(activityIndicator)
alertView.transform = CGAffineTransform.init(scaleX: 1.3,y: 1.3)
UIView.animate(withDuration: 0.4) {
alertView.alpha = 1
alertView.transform = CGAffineTransform.identity
}
}
///////
else {
for view in inViewController.view.subviews {
if (view.restorationIdentifier == "myalert") {
print("I FIND IT");
(view as! UIView).removeFromSuperview();
}
}
}
}

Swift 2.1 - How do I change the uidatepicker so it only allows for time instead of date?

The following code below allows for the user to select a date, however I want to change that to 'Time' via AM/PM, so the user can only select a set time instead it being a date. I want this separate and not combined, I appreciate if someone could help me here thanks.
import Foundation
import UIKit
import QuartzCore
class DatePickerDialog: UIView {
typealias DatePickerCallback = (date: NSDate) -> Void
/* Consts */
private let kDatePickerDialogDefaultButtonHeight: CGFloat = 50
private let kDatePickerDialogDefaultButtonSpacerHeight: CGFloat = 1
private let kDatePickerDialogCornerRadius: CGFloat = 7
private let kDatePickerDialogDoneButtonTag: Int = 1
/* Views */
private var dialogView: UIView!
private var titleLabel: UILabel!
private var datePicker: UIDatePicker!
private var cancelButton: UIButton!
private var doneButton: UIButton!
/* Vars */
private var defaultDate: NSDate?
private var datePickerMode: UIDatePickerMode?
private var callback: DatePickerCallback?
/* Overrides */
init() {
super.init(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height))
setupView()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func setupView() {
self.dialogView = createContainerView()
self.dialogView!.layer.shouldRasterize = true
self.dialogView!.layer.rasterizationScale = UIScreen.mainScreen().scale
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.mainScreen().scale
self.dialogView!.layer.opacity = 0.5
self.dialogView!.layer.transform = CATransform3DMakeScale(1.3, 1.3, 1)
self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
self.addSubview(self.dialogView!)
UIApplication.sharedApplication().windows.first!.addSubview(self)
}
/* Handle device orientation changes */
func deviceOrientationDidChange(notification: NSNotification) {
/* TODO */
}
/* Create the dialog view, and animate opening the dialog */
func show(title: String, datePickerMode: UIDatePickerMode = .Date, callback: DatePickerCallback) {
show(title, doneButtonTitle: "Done", cancelButtonTitle: "Cancel", datePickerMode: datePickerMode, callback: callback)
}
func show(title: String, doneButtonTitle: String, cancelButtonTitle: String, defaultDate: NSDate = NSDate(), datePickerMode: UIDatePickerMode = .Date, callback: DatePickerCallback) {
self.titleLabel.text = title
self.doneButton.setTitle(doneButtonTitle, forState: .Normal)
self.cancelButton.setTitle(cancelButtonTitle, forState: .Normal)
self.datePickerMode = datePickerMode
self.callback = callback
self.defaultDate = defaultDate
self.datePicker.datePickerMode = self.datePickerMode ?? .Date
self.datePicker.date = self.defaultDate ?? NSDate()
/* Anim */
UIView.animateWithDuration(
0.2,
delay: 0,
options: UIViewAnimationOptions.CurveEaseInOut,
animations: { () -> Void in
self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
self.dialogView!.layer.opacity = 1
self.dialogView!.layer.transform = CATransform3DMakeScale(1, 1, 1)
},
completion: nil
)
}
/* Dialog close animation then cleaning and removing the view from the parent */
private func close() {
let currentTransform = self.dialogView.layer.transform
let startRotation = (self.valueForKeyPath("layer.transform.rotation.z") as? NSNumber) as? Double ?? 0.0
let rotation = CATransform3DMakeRotation((CGFloat)(-startRotation + M_PI * 270 / 180), 0, 0, 0)
self.dialogView.layer.transform = CATransform3DConcat(rotation, CATransform3DMakeScale(1, 1, 1))
self.dialogView.layer.opacity = 1
UIView.animateWithDuration(
0.2,
delay: 0,
options: UIViewAnimationOptions.TransitionNone,
animations: { () -> Void in
self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
self.dialogView.layer.transform = CATransform3DConcat(currentTransform, CATransform3DMakeScale(0.6, 0.6, 1))
self.dialogView.layer.opacity = 0
}) { (finished: Bool) -> Void in
for v in self.subviews {
v.removeFromSuperview()
}
self.removeFromSuperview()
}
}
/* Creates the container view here: create the dialog, then add the custom content and buttons */
private func createContainerView() -> UIView {
let screenSize = countScreenSize()
let dialogSize = CGSizeMake(
300,
230
+ kDatePickerDialogDefaultButtonHeight
+ kDatePickerDialogDefaultButtonSpacerHeight)
// For the black background
self.frame = CGRectMake(0, 0, screenSize.width, screenSize.height)
// This is the dialog's container; we attach the custom content and the buttons to this one
let dialogContainer = UIView(frame: CGRectMake((screenSize.width - dialogSize.width) / 2, (screenSize.height - dialogSize.height) / 2, dialogSize.width, dialogSize.height))
// First, we style the dialog to match the iOS8 UIAlertView >>>
let gradient: CAGradientLayer = CAGradientLayer(layer: self.layer)
gradient.frame = dialogContainer.bounds
gradient.colors = [UIColor(red: 218/255, green: 218/255, blue: 218/255, alpha: 1).CGColor,
UIColor(red: 233/255, green: 233/255, blue: 233/255, alpha: 1).CGColor,
UIColor(red: 218/255, green: 218/255, blue: 218/255, alpha: 1).CGColor]
let cornerRadius = kDatePickerDialogCornerRadius
gradient.cornerRadius = cornerRadius
dialogContainer.layer.insertSublayer(gradient, atIndex: 0)
dialogContainer.layer.cornerRadius = cornerRadius
dialogContainer.layer.borderColor = UIColor(red: 198/255, green: 198/255, blue: 198/255, alpha: 1).CGColor
dialogContainer.layer.borderWidth = 1
dialogContainer.layer.shadowRadius = cornerRadius + 5
dialogContainer.layer.shadowOpacity = 0.1
dialogContainer.layer.shadowOffset = CGSizeMake(0 - (cornerRadius + 5) / 2, 0 - (cornerRadius + 5) / 2)
dialogContainer.layer.shadowColor = UIColor.blackColor().CGColor
dialogContainer.layer.shadowPath = UIBezierPath(roundedRect: dialogContainer.bounds, cornerRadius: dialogContainer.layer.cornerRadius).CGPath
// There is a line above the button
let lineView = UIView(frame: CGRectMake(0, dialogContainer.bounds.size.height - kDatePickerDialogDefaultButtonHeight - kDatePickerDialogDefaultButtonSpacerHeight, dialogContainer.bounds.size.width, kDatePickerDialogDefaultButtonSpacerHeight))
lineView.backgroundColor = UIColor(red: 198/255, green: 198/255, blue: 198/255, alpha: 1)
dialogContainer.addSubview(lineView)
// ˆˆˆ
//Title
self.titleLabel = UILabel(frame: CGRectMake(10, 10, 280, 30))
self.titleLabel.textAlignment = NSTextAlignment.Center
self.titleLabel.font = UIFont.boldSystemFontOfSize(17)
dialogContainer.addSubview(self.titleLabel)
self.datePicker = UIDatePicker(frame: CGRectMake(0, 30, 0, 0))
self.datePicker.autoresizingMask = UIViewAutoresizing.FlexibleRightMargin
self.datePicker.frame.size.width = 300
dialogContainer.addSubview(self.datePicker)
// Add the buttons
addButtonsToView(dialogContainer)
return dialogContainer
}
/* Add buttons to container */
private func addButtonsToView(container: UIView) {
let buttonWidth = container.bounds.size.width / 2
self.cancelButton = UIButton(type: UIButtonType.Custom) as UIButton
self.cancelButton.frame = CGRectMake(
0,
container.bounds.size.height - kDatePickerDialogDefaultButtonHeight,
buttonWidth,
kDatePickerDialogDefaultButtonHeight
)
self.cancelButton.setTitleColor(UIColor(red: 0, green: 0.5, blue: 1, alpha: 1), forState: UIControlState.Normal)
self.cancelButton.setTitleColor(UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5), forState: UIControlState.Highlighted)
self.cancelButton.titleLabel!.font = UIFont.boldSystemFontOfSize(14)
self.cancelButton.layer.cornerRadius = kDatePickerDialogCornerRadius
container.addSubview(self.cancelButton)
self.doneButton = UIButton(type: UIButtonType.Custom) as UIButton
self.doneButton.frame = CGRectMake(
buttonWidth,
container.bounds.size.height - kDatePickerDialogDefaultButtonHeight,
buttonWidth,
kDatePickerDialogDefaultButtonHeight
)
self.doneButton.tag = kDatePickerDialogDoneButtonTag
self.doneButton.setTitleColor(UIColor(red: 0, green: 0.5, blue: 1, alpha: 1), forState: UIControlState.Normal)
self.doneButton.setTitleColor(UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5), forState: UIControlState.Highlighted)
self.doneButton.titleLabel!.font = UIFont.boldSystemFontOfSize(14)
self.doneButton.layer.cornerRadius = kDatePickerDialogCornerRadius
self.doneButton.addTarget(self, action: "buttonTapped:", forControlEvents: UIControlEvents.TouchUpInside)
container.addSubview(self.doneButton)
}
func buttonTapped(sender: UIButton!) {
if sender.tag == kDatePickerDialogDoneButtonTag {
self.callback?(date: self.datePicker.date)
}
close()
}
/* Helper function: count and return the screen's size */
func countScreenSize() -> CGSize {
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
return CGSizeMake(screenWidth, screenHeight)
}
}
Here is the action in a separate view controller.
#IBAction func datePickerTapped(sender: AnyObject) {
DatePickerDialog().show("Select Date of Incident", doneButtonTitle: "Done", cancelButtonTitle: "Cancel", datePickerMode: .Date) {
(date) -> Void in
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy" //format style. you can change according to yours
let dateString = dateFormatter.stringFromDate(date)
self.textField3.text = dateString
}
}
If by "separately," you mean prompting for the date and then prompting for the time, you can simply call the show method again and pass in the appropriate UIDatePickerMode (.Time in this case) and format your time according to your requirements.
Like so:
DatePickerDialog().show("Select Time of Incident", doneButtonTitle: "Done", cancelButtonTitle: "Cancel", datePickerMode: .Time) {
//format your time accordingly
//self.textField3.text = timeString
}

How to remove the time from Date Picker?

What I want to achieve here is to remove the "TIME" from the UITextfield once I have selected a date. For example "D/M/Y" I want this only to display and not the time "00:00:00" or "+0000".
This piece of code does not require the element of "UIDate Picker" to be selected as I already know how to disable the time this way through the attributes inspector but not this way.
I think it needs to be done programmatically.
Code in the separate class:
import Foundation
import UIKit
import QuartzCore
class DatePickerDialog: UIView {
typealias DatePickerCallback = (date: NSDate) -> Void
/* Consts */
private let kDatePickerDialogDefaultButtonHeight: CGFloat = 50
private let kDatePickerDialogDefaultButtonSpacerHeight: CGFloat = 1
private let kDatePickerDialogCornerRadius: CGFloat = 7
private let kDatePickerDialogDoneButtonTag: Int = 1
/* Views */
private var dialogView: UIView!
private var titleLabel: UILabel!
private var datePicker: UIDatePicker!
private var cancelButton: UIButton!
private var doneButton: UIButton!
/* Vars */
private var defaultDate: NSDate?
private var datePickerMode: UIDatePickerMode?
private var callback: DatePickerCallback?
/* Overrides */
init() {
super.init(frame: CGRectMake(0, 0, UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height))
setupView()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
func setupView() {
self.dialogView = createContainerView()
self.dialogView!.layer.shouldRasterize = true
self.dialogView!.layer.rasterizationScale = UIScreen.mainScreen().scale
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.mainScreen().scale
self.dialogView!.layer.opacity = 0.5
self.dialogView!.layer.transform = CATransform3DMakeScale(1.3, 1.3, 1)
self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
self.addSubview(self.dialogView!)
UIApplication.sharedApplication().windows.first!.addSubview(self)
}
/* Handle device orientation changes */
func deviceOrientationDidChange(notification: NSNotification) {
/* TODO */
}
/* Create the dialog view, and animate opening the dialog */
func show(title: String, datePickerMode: UIDatePickerMode = .Date, callback: DatePickerCallback) {
show(title, doneButtonTitle: "Done", cancelButtonTitle: "Cancel", datePickerMode: datePickerMode, callback: callback)
}
func show(title: String, doneButtonTitle: String, cancelButtonTitle: String, defaultDate: NSDate = NSDate(), datePickerMode: UIDatePickerMode = .Date, callback: DatePickerCallback) {
self.titleLabel.text = title
self.doneButton.setTitle(doneButtonTitle, forState: .Normal)
self.cancelButton.setTitle(cancelButtonTitle, forState: .Normal)
self.datePickerMode = datePickerMode
self.callback = callback
self.defaultDate = defaultDate
self.datePicker.datePickerMode = self.datePickerMode ?? .Date
self.datePicker.date = self.defaultDate ?? NSDate()
/* Anim */
UIView.animateWithDuration(
0.2,
delay: 0,
options: UIViewAnimationOptions.CurveEaseInOut,
animations: { () -> Void in
self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.4)
self.dialogView!.layer.opacity = 1
self.dialogView!.layer.transform = CATransform3DMakeScale(1, 1, 1)
},
completion: nil
)
}
/* Dialog close animation then cleaning and removing the view from the parent */
private func close() {
let currentTransform = self.dialogView.layer.transform
let startRotation = (self.valueForKeyPath("layer.transform.rotation.z") as? NSNumber) as? Double ?? 0.0
let rotation = CATransform3DMakeRotation((CGFloat)(-startRotation + M_PI * 270 / 180), 0, 0, 0)
self.dialogView.layer.transform = CATransform3DConcat(rotation, CATransform3DMakeScale(1, 1, 1))
self.dialogView.layer.opacity = 1
UIView.animateWithDuration(
0.2,
delay: 0,
options: UIViewAnimationOptions.TransitionNone,
animations: { () -> Void in
self.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
self.dialogView.layer.transform = CATransform3DConcat(currentTransform, CATransform3DMakeScale(0.6, 0.6, 1))
self.dialogView.layer.opacity = 0
}) { (finished: Bool) -> Void in
for v in self.subviews {
v.removeFromSuperview()
}
self.removeFromSuperview()
}
}
/* Creates the container view here: create the dialog, then add the custom content and buttons */
private func createContainerView() -> UIView {
let screenSize = countScreenSize()
let dialogSize = CGSizeMake(
300,
230
+ kDatePickerDialogDefaultButtonHeight
+ kDatePickerDialogDefaultButtonSpacerHeight)
// For the black background
self.frame = CGRectMake(0, 0, screenSize.width, screenSize.height)
// This is the dialog's container; we attach the custom content and the buttons to this one
let dialogContainer = UIView(frame: CGRectMake((screenSize.width - dialogSize.width) / 2, (screenSize.height - dialogSize.height) / 2, dialogSize.width, dialogSize.height))
// First, we style the dialog to match the iOS8 UIAlertView >>>
let gradient: CAGradientLayer = CAGradientLayer(layer: self.layer)
gradient.frame = dialogContainer.bounds
gradient.colors = [UIColor(red: 218/255, green: 218/255, blue: 218/255, alpha: 1).CGColor,
UIColor(red: 233/255, green: 233/255, blue: 233/255, alpha: 1).CGColor,
UIColor(red: 218/255, green: 218/255, blue: 218/255, alpha: 1).CGColor]
let cornerRadius = kDatePickerDialogCornerRadius
gradient.cornerRadius = cornerRadius
dialogContainer.layer.insertSublayer(gradient, atIndex: 0)
dialogContainer.layer.cornerRadius = cornerRadius
dialogContainer.layer.borderColor = UIColor(red: 198/255, green: 198/255, blue: 198/255, alpha: 1).CGColor
dialogContainer.layer.borderWidth = 1
dialogContainer.layer.shadowRadius = cornerRadius + 5
dialogContainer.layer.shadowOpacity = 0.1
dialogContainer.layer.shadowOffset = CGSizeMake(0 - (cornerRadius + 5) / 2, 0 - (cornerRadius + 5) / 2)
dialogContainer.layer.shadowColor = UIColor.blackColor().CGColor
dialogContainer.layer.shadowPath = UIBezierPath(roundedRect: dialogContainer.bounds, cornerRadius: dialogContainer.layer.cornerRadius).CGPath
// There is a line above the button
let lineView = UIView(frame: CGRectMake(0, dialogContainer.bounds.size.height - kDatePickerDialogDefaultButtonHeight - kDatePickerDialogDefaultButtonSpacerHeight, dialogContainer.bounds.size.width, kDatePickerDialogDefaultButtonSpacerHeight))
lineView.backgroundColor = UIColor(red: 198/255, green: 198/255, blue: 198/255, alpha: 1)
dialogContainer.addSubview(lineView)
// ˆˆˆ
//Title
self.titleLabel = UILabel(frame: CGRectMake(10, 10, 280, 30))
self.titleLabel.textAlignment = NSTextAlignment.Center
self.titleLabel.font = UIFont.boldSystemFontOfSize(17)
dialogContainer.addSubview(self.titleLabel)
self.datePicker = UIDatePicker(frame: CGRectMake(0, 30, 0, 0))
self.datePicker.autoresizingMask = UIViewAutoresizing.FlexibleRightMargin
self.datePicker.frame.size.width = 300
dialogContainer.addSubview(self.datePicker)
// Add the buttons
addButtonsToView(dialogContainer)
return dialogContainer
}
/* Add buttons to container */
private func addButtonsToView(container: UIView) {
let buttonWidth = container.bounds.size.width / 2
self.cancelButton = UIButton(type: UIButtonType.Custom) as UIButton
self.cancelButton.frame = CGRectMake(
0,
container.bounds.size.height - kDatePickerDialogDefaultButtonHeight,
buttonWidth,
kDatePickerDialogDefaultButtonHeight
)
self.cancelButton.setTitleColor(UIColor(red: 0, green: 0.5, blue: 1, alpha: 1), forState: UIControlState.Normal)
self.cancelButton.setTitleColor(UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5), forState: UIControlState.Highlighted)
self.cancelButton.titleLabel!.font = UIFont.boldSystemFontOfSize(14)
self.cancelButton.layer.cornerRadius = kDatePickerDialogCornerRadius
container.addSubview(self.cancelButton)
self.doneButton = UIButton(type: UIButtonType.Custom) as UIButton
self.doneButton.frame = CGRectMake(
buttonWidth,
container.bounds.size.height - kDatePickerDialogDefaultButtonHeight,
buttonWidth,
kDatePickerDialogDefaultButtonHeight
)
self.doneButton.tag = kDatePickerDialogDoneButtonTag
self.doneButton.setTitleColor(UIColor(red: 0, green: 0.5, blue: 1, alpha: 1), forState: UIControlState.Normal)
self.doneButton.setTitleColor(UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5), forState: UIControlState.Highlighted)
self.doneButton.titleLabel!.font = UIFont.boldSystemFontOfSize(14)
self.doneButton.layer.cornerRadius = kDatePickerDialogCornerRadius
self.doneButton.addTarget(self, action: "buttonTapped:", forControlEvents: UIControlEvents.TouchUpInside)
container.addSubview(self.doneButton)
}
func buttonTapped(sender: UIButton!) {
if sender.tag == kDatePickerDialogDoneButtonTag {
self.callback?(date: self.datePicker.date)
}
close()
}
/* Helper function: count and return the screen's size */
func countScreenSize() -> CGSize {
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
return CGSizeMake(screenWidth, screenHeight)
}
}
Code here defined in Viewcontroller.swift:
#IBAction func datePickerTapped(sender: AnyObject) {
DatePickerDialog().show("Select Date of Incident", doneButtonTitle: "Done", cancelButtonTitle: "Cancel", datePickerMode: .Date) {
(date) -> Void in
self.textField3.text = "\(date)"
}
}
Its simple
change this line to
self.textField3.text = "\(date)"
to
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd" //format style. you can change according to yours
var dateString = dateFormatter.stringFromDate(date)
self.textField3.text = dateString