Getting trouble when hovering a custom segmented control in swift - 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).

Related

How to use "shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer"

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.

Swift How to make the previous label disappear and the new one to appear

I’m making my first project that is a truth or dare game but when I tap the button to print a Truth/dare the labels are getting stacked on each other. How do I make it that the previous label disappear and the new one is there alone. Thanks in advance
import UIKit
import PlaygroundSupport
public class ViewController: UIViewController {
public override func loadView() {
//adding baground
super.loadView()
let view = UIView()
view.backgroundColor = .red
self.view = view
//dare button
let dareButton = UIButton(frame: CGRect(x:300, y: 500, width: 100, height: 69))
dareButton.layer.cornerRadius = 10
dareButton.backgroundColor = #colorLiteral(red: 0.6352941393852234, green: 0.6470588445663452, blue: 0.886274516582489, alpha: 1.0)
dareButton.setTitle("dare", for: UIControl.State.normal)
dareButton.isUserInteractionEnabled = true
dareButton.addTarget(self, action: #selector(dareButtonDidTap), for: .touchDown)
view.addSubview(dareButton)
self.view = view
view.addSubview(dareButton)
}
#objc
private func dareButtonDidTap() {
let lbl = UILabel(frame: CGRect(x:95 , y:100, width: 325, height: 500))
lbl.text = daresEng.randomElement()
lbl.textColor = #colorLiteral(red: 0.6313725709915161, green: 0.6470588445663452, blue: 0.9058823585510254, alpha: 1.0)
lbl.numberOfLines = 6
view.addSubview(lbl)
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = ViewController()
The labels are stacked because you create a new label and add it to the view each time you click the dareButton, and the previous labels are not removed from the superView. So you can move the label to the class level and add it to the view in loadView(). This way you only have one label, and in the dareButtonDidTap(), you only have to change the text of this label.
import UIKit
import PlaygroundSupport
public class ViewController: UIViewController {
let lbl = UILabel(frame: CGRect(x:95 , y:100, width: 325, height: 500))
public override func loadView() {
//adding baground
super.loadView()
let view = UIView()
view.backgroundColor = .red
self.view = view
//dare button
let dareButton = UIButton(frame: CGRect(x:300, y: 500, width: 100, height: 69))
dareButton.layer.cornerRadius = 10
dareButton.backgroundColor = #colorLiteral(red: 0.6352941393852234, green: 0.6470588445663452, blue: 0.886274516582489, alpha: 1.0)
dareButton.setTitle("dare", for: UIControl.State.normal)
dareButton.isUserInteractionEnabled = true
dareButton.addTarget(self, action: #selector(dareButtonDidTap), for: .touchDown)
view.addSubview(dareButton)
self.view = view
view.addSubview(lbl)
view.addSubview(dareButton)
}
#objc
private func dareButtonDidTap() {
lbl.text = daresEng.randomElement()
lbl.textColor = #colorLiteral(red: 0.6313725709915161, green: 0.6470588445663452, blue: 0.9058823585510254, alpha: 1.0)
lbl.numberOfLines = 6
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = ViewController()

Why are UIViews background color is not updating?

Switch statement works but won't reset view background colours etc.
I have a UIImage (icon) and a UIButton embedded within a UIView (of custom type DropShadowCircleView) as per image below.
When the walking button is tapped a var navigationOption is set to either walking or driving and setupNavigationSelectionView() is executed.
Problem is: case "walking" of the switch works perfectly, but case "driving" doesn't reset the UIView and icon tint color witcback to their original setting eg; background color etc.. any ideas why?
func setupNavigationSelectionView(){
switch navigationOption {
case "walking":
walkingBg.setGradientBackground(colourOne: softGreen, ColourTwo: softBlue)
walkingBg.layer.cornerRadius = walkingBg.frame.width / 2
walkingBg.clipsToBounds = true
walkingIcon.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
case "driving":
walkingBg.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
walkingBg.layer.cornerRadius = walkingBg.frame.width / 2
walkingBg.clipsToBounds = true
walkingIcon.tintColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
default:
break
}
}
EDIT: this is my DropShadowCircleView class
class DropShadowCircleView: UIView {
override func awakeFromNib() {
setupView()
super.awakeFromNib()
}
func setupView(){
self.layer.shadowOpacity = 0.50
self.layer.shadowRadius = 20
self.layer.shadowColor = UIColor.black.cgColor
self.layer.cornerRadius = self.frame.width / 2
}
}
EDIT: This is my setGradientBackground function which is within an extension file to UIView
func setGradientBackground(colourOne: UIColor, ColourTwo: UIColor) {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [colourOne.cgColor, ColourTwo.cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.startPoint = CGPoint(x: 1.0, y: 1.0)
gradientLayer.endPoint = CGPoint(x: 0.0, y: 0.0)
layer.insertSublayer(gradientLayer, at: 0)
}
You need to remove your gradient layer when you reset your icon.
Add this to your extension UIView:
func removeGradientBackground() {
guard
let idx = layer.sublayers?.index(where: { $0 is CAGradientLayer })
else { return }
layer.sublayers?.remove(at: idx)
}
and call it when you are resetting your icon.

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