Change label color with animateWithDuration in Swift - ios

I'm trying to animate a label text so that if the value greater it will change the text color to blue and if the value less it will change the color to Red, else remain the same "Black color".
But UIView.animateWithDuration() it change the color to Blue permanently, all I am trying to do is if the value great or less than, I want to change the label color to blue or red for few seconds then return its color to black.
Here is my code:
#IBOutlet weak var label: UILabel!
let x = 10
let y = 20
if x > y
{
UIView.animateWithDuration(2,animations:
{ () -> Void in self.label.textColor = UIColor.blueColor(); })
}
else if y < x
{
UIView.animateWithDuration(2,animations:
{ () -> Void in self.label.textColor = UIColor.redColor(); })
}
else
{
self.label.textColor = UIColor.blackColor()
}
also I tried to use Sleep function as follow but it didn't work out
self.label.textColor = UIColor.blueColor()
sleep(3)
self.label.textColor = UIColor.blackColor()

UIView animation api cannot animate the textColor property of UILabel, for that you will need to use CAAnimation. Here is a implementation using CATransition.
func animate() {
let x = 10
let y = 20
var finalColor: UIColor!
if x > y {
finalColor = UIColor.blueColor()
} else {
finalColor = UIColor.redColor()
}
let changeColor = CATransition()
changeColor.type = kCATransitionFade
changeColor.duration = 2.0
CATransaction.begin()
CATransaction.setCompletionBlock {
self.label.textColor = UIColor.blackColor()
self.label.layer.addAnimation(changeColor, forKey: nil)
}
self.label.textColor = finalColor
self.label.layer.addAnimation(changeColor, forKey: nil)
CATransaction.commit()
}

Building on Acluda's answer, I'd recommend putting his code in the completion handler of the animateWithDuration:animations:completion variant.
UIView.animateWithDuration(2,
animations: { () -> Void in self.label.textColor = UIColor.blueColor(); },
completion: { (wentThrough: Bool) -> Void in
{ UIView.animateWithDuration(2,
animations: { () -> Void in self.label.textColor = UIColor.blackColor(); }) })

UIView.transitionWithView(myLabel, duration: 0.25, options: .TransitionCrossDissolve, animations: {() -> Void in
label.textColor = UIColor.redColor()
}, completion: {(finished: Bool) -> Void in
})
Try :)

There is no logic that tells the UIView to return back to UIColor.blackColor once the first animation is complete.
Consider adding this after animation calls for blue/red.
UIView.animateWithDuration(2,animations:
{ () -> Void in self.label.textColor = UIColor.blackColor(); })

Related

Make custom-triggered UIColorPickerViewController hug bottom of screen?

I am using custom control to trigger the presentation of a UIColorPickerViewController. It triggers the color picker, but not as expected.
I was using UIColorWell previously, which does present the picker correctly (but it is an opaque implementation so I don't know how it does so). I don't want to use UIColorWell, because the shape and appearance aren't right for where I'm invoking the picker from.
Everything works exactly as desired except I can't get the color picker to present at the bottom of the screen like UIColorWell would do it. Instead, whatever I've tried to present (or show), UIColorPickerController appears near the top of the screen.
Note: Currently, to present the color picker controller, I'm using the latest iOS 15 trick with sheet controller / detents to try to anchor the picker to the bottom of the screen, but it's not working as expected (it's the technique is from the Apple Docs example, and as I've seen documented online).
What might be happening? What can I do to get the picker at the bottom of the screen, so that it doesn't obstruct my interface?
The Extension:
extension UIView {
func findViewController() -> UIViewController? {
if let nextResponder = self.next as? UIViewController {
return nextResponder
} else if let nextResponder = self.next as? UIView {
return nextResponder.findViewController()
} else {
return nil
}
}
}
Reproducible example (almost. It needs to be implemented/invoked from whatever VC [not shown here]):
This is custom control that that creates a rectangular color picker view (I'm using it to replace UIColorWell). It attempts to locate the VC which contains the view of which this custom control is a subview.
import UIKit
import QuartzCore
class RectangularColorWell : UIControl {
var colorPickerController = UIColorPickerViewController()
var selectedColor : UIColor = UIColor.clear
var lineWidth : CGFloat = 4.0 {
didSet {
self.setNeedsDisplay()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
colorPickerController.supportsAlpha = false
colorPickerController.delegate = self
selectedColor = backgroundColor ?? UIColor.clear
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(CustomToggleControl.controlTapped(_:)))
tapGestureRecognizer.numberOfTapsRequired = 1;
tapGestureRecognizer.numberOfTouchesRequired = 1;
self.addGestureRecognizer(tapGestureRecognizer)
self.setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.setLineWidth(lineWidth)
ctx.setFillColor(backgroundColor!.cgColor)
ctx.fill(rect)
drawGradientBorder(rect, context: ctx)
}
func drawGradientBorder(_ rect: CGRect, context: CGContext) {
context.saveGState()
context.setLineWidth(lineWidth)
let path = UIBezierPath(rect: rect)
context.addPath(path.cgPath)
context.replacePathWithStrokedPath()
context.clip()
let rainbowColors : [UIColor] = [ .red, .orange, .yellow, .green, .blue, .purple ]
let colorDistribution : [CGFloat] = [ 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 ]
let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: rainbowColors.map { $0.cgColor } as CFArray, locations: colorDistribution)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0, y: 0), end: CGPoint(x: rect.width, y: rect.height), options: [])
context.restoreGState()
}
#objc func controlTapped(_ gestureRecognizer :UIGestureRecognizer) {
UIView.animate(withDuration: 0.2, delay: 0.0, options: UIView.AnimationOptions.curveEaseIn, animations: {
self.alpha = 0.5
}, completion: { (finished: Bool) -> Void in
UIView.animate(withDuration: 0.2, delay: 0.0, options: UIView.AnimationOptions.curveEaseOut, animations: {
self.alpha = 1.0
}, completion: { (finished: Bool) -> Void in
})
})
setNeedsDisplay()
let vc = self.findViewController()
if let sheet = vc?.sheetPresentationController {
sheet.detents = [.medium()]
sheet.largestUndimmedDetentIdentifier = .medium
sheet.prefersScrollingExpandsWhenScrolledToEdge = false
sheet.prefersEdgeAttachedInCompactHeight = true
sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
}
vc?.present(colorPickerController, animated: true, completion: nil)
sendActions(for: .touchDown)
}
}
extension RectangularColorWell : UIColorPickerViewControllerDelegate {
func colorPickerViewControllerDidFinish(_ controller : UIColorPickerViewController) {
self.selectedColor = controller.selectedColor
}
func colorPickerViewController(_ controller : UIColorPickerViewController, didSelect color: UIColor, continuously: Bool) {
self.backgroundColor = color
self.setNeedsDisplay()
}
}
#JordanH's final comment caused me to read up on the show/present methods on UIViewController docs. The default presentation style was what I'd been using (e.g. I hadn't set any of the presentation options on the color picker's view controller when I filed the question.
Surprisingly after reading that I could control the style, setting the picker's modalPresentationStyle property to '.pageSheet' which is the one that looks like it would work, to do a partial covering, did not work!.
The solution turned out to be adding colorPickerController.modalPresentationStyle = .popover before calling present()in the custom RectangularColorWellView). It now behaves as if it was presented from a UIColorWell control view.

programatically add two image to above my label

I'm using a custom library : CAPSPageMenu . Here I have one view controller and I programmatically add the name for that, the view has two tabs. When i click any tab that respective page will display below.
I have set the name and added the animation to swipe between two tabs. But now I need to add image on above of my label, how would I accomplish that?
Here is my code !:
var pageMenu : CAPSPageMenu? // pager object
var controllerArray : [UIViewController] = [] // number view pager
override func viewDidLoad() {
super.viewDidLoad()
pageSettings()
}
func pageSettings() {
tableView.dataSource = self
tableView.delegate = self
let controller1 : UIViewController = UIViewController()
controller1.title = "FAVOURITES"
controllerArray.append(controller1)
let controller2 : UIViewController = UIViewController()
controller2.title = "RECENT SEARCH"
controllerArray.append(controller2)
let leftRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(self.didLeftSwipe(_:)))
leftRecognizer.direction = .Left
self.tableView.addGestureRecognizer(leftRecognizer)
let rightRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(self.didRightSwipe(_:)))
rightRecognizer.direction = .Right
self.tableView.addGestureRecognizer(rightRecognizer)
}
Like this i need to add image.As i mention the box above the two labels
Seems you are used a custom library CAPSPageMenu, unfortunately what do you want to do is not available.
However with swift you can do extraordinary things such as expanding PageMenu directly in your code: (I've maded just an example but you can start from it):
Add this code at the bottom of your class or whatever you want:
extension UIView {
func fadeIn(duration: NSTimeInterval = 1.0, delay: NSTimeInterval = 0.0, completion: ((Bool) -> Void) = {(finished: Bool) -> Void in}) {
UIView.animateWithDuration(duration, delay: delay, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.alpha = 1.0
}, completion: completion) }
func fadeOut(duration: NSTimeInterval = 1.0, delay: NSTimeInterval = 0.0, completion: (Bool) -> Void = {(finished: Bool) -> Void in}) {
UIView.animateWithDuration(duration, delay: delay, options: UIViewAnimationOptions.CurveEaseIn, animations: {
self.alpha = 0.0
}, completion: completion)
}
}
extension CAPSPageMenu {
func addImageOverMyMenuItem(index:Int,imageName:String) {
if index>=0 && index<self.menuItems.count || self.menuItems[index].titleLabel!.frame.height>0 {
var labelFrame = self.menuItems[index].titleLabel!.frame
let image: UIImage = UIImage(named: imageName)!
let bgImage = UIImageView(image: image)
labelFrame.origin.y = self.menuItems[index].frame.origin.y
labelFrame.origin.x += (labelFrame.width/10)
labelFrame.size.width -= (labelFrame.width/10)*2
labelFrame.size.height = self.menuItems[index].titleLabel!.frame.size.height/3
bgImage.frame = labelFrame
bgImage.tag = 9999
bgImage.alpha = 0.0
self.menuItems[index].addSubview(bgImage)
bgImage.fadeIn(completion: {
(finished: Bool) -> Void in
if finished {
// image is showed, do whatever you want
}
})
} else {
print("Due to index out of range or titleLabel dont yet setted i cannot set image")
}
}
func removeImageOverMyMenuItem(index:Int) {
if index>=0 && index<self.menuItems.count {
if let bgImage = self.menuItems[index].viewWithTag(9999) {
bgImage.fadeOut(completion: {
(finished: Bool) -> Void in
if finished {
bgImage.removeFromSuperview()
}
})
}
}
}
}
You can easily use to add an image:
pageMenu?.addImageOverMyMenuItem(0, imageName: "search.png")
and to remove :
pageMenu?.removeImageOverMyMenuItem(0)
P.S. The result would be this:
So , finally following the official demo 1 project (PageMenuDemoStoryboard) in the ViewController at the method viewDidAppear i can add my images like this:
// Initialize scroll menu
pageMenu = CAPSPageMenu(viewControllers: controllerArray, frame: CGRectMake(0.0, 0.0, self.view.frame.width, self.view.frame.height), pageMenuOptions: parameters)
pageMenu?.addImageOverMyMenuItem(0, imageName: "search.png")
pageMenu?.addImageOverMyMenuItem(1, imageName: "search.png")
pageMenu?.addImageOverMyMenuItem(2, imageName: "search.png")
self.addChildViewController(pageMenu!)
self.view.addSubview(pageMenu!.view)
pageMenu!.didMoveToParentViewController(self)
...
And you obtain this result:
P.S.S. If it's not clear, I've made this example just using an image named "search.png" but you can use all images do you want to insert in your project.
You are using PageMenu, right?
Basically, PageMenu doesn't have the feature satisfy your needs.
If you want to use PageMenu, you have to modify this module.
Check MenuItemView Class in CAPSPageMenu.swift
I have also customised CAPSPageMenu , using the answer of #Alessandro Ornano that helped me to add the image above text, so anyone using can also set width,height of image and also can set the position of image as per device by using code below.Hope it will help someone.
extension UIView {
func fadeIn(duration: TimeInterval = 0.0, delay: TimeInterval = 0.0, completion: #escaping ((Bool) -> Void) = {(finished: Bool) -> Void in}) {
UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.alpha = 1.0
}, completion: completion) }
func fadeOut(duration: TimeInterval = 0.0, delay: TimeInterval = 0.0, completion: #escaping (Bool) -> Void = {(finished: Bool) -> Void in}) {
UIView.animate(withDuration: duration, delay: delay, options: UIViewAnimationOptions.curveEaseIn, animations: {
self.alpha = 0.0
}, completion: completion)
}
}
extension CAPSPageMenu {
func addImageOverMyMenuItem(index:Int,imageName:String) {
if index>=0 && index<self.menuItems.count || self.menuItems[index].titleLabel!.frame.height>0 {
var labelFrame = self.menuItems[index].titleLabel!.frame
let image: UIImage = UIImage(named: imageName)!
let bgImage = UIImageView(image: image)
labelFrame.origin.y = self.menuItems[index].frame.origin.y
labelFrame.origin.x += (labelFrame.width/9)
labelFrame.size.width -= (labelFrame.width/10)*5
labelFrame.size.height = self.menuItems[index].titleLabel!.frame.size.height/3
bgImage.frame = labelFrame
bgImage.tag = 9999
bgImage.alpha = 0.0
if UIDevice().userInterfaceIdiom == .phone {
switch UIScreen.main.nativeBounds.height {
case 480:
print("iPhone Classic")
case 960:
print("iPhone 4 or 4S")
bgImage.frame = CGRect(origin: CGPoint(x:20 ,y :0), size: CGSize(width:30, height: 30))
case 1136:
print("iPhone 5 or 5S or 5C")
bgImage.frame = CGRect(origin: CGPoint(x:20 ,y :0), size: CGSize(width:35, height: 35))
case 1334:
print("iPhone 6 or 6S")
bgImage.frame = CGRect(origin: CGPoint(x:30 ,y :0), size: CGSize(width:35, height: 35))
case 2208:
print("iPhone 6+ or 6S+")
bgImage.frame = CGRect(origin: CGPoint(x:35 ,y :0), size: CGSize(width:35, height: 35))
default:
print("unknown")
bgImage.frame = CGRect(origin: CGPoint(x:40 ,y :0), size: CGSize(width:30, height: 30))
}
}
self.menuItems[index].addSubview(bgImage)
bgImage.fadeIn(completion: {
(finished: Bool) -> Void in
if finished {
// image is showed, do whatever you want
}
})
} else {
print("Due to index out of range or titleLabel dont yet setted i cannot set image")
}
}
func removeImageOverMyMenuItem(index:Int) {
if index>=0 && index<self.menuItems.count {
if let bgImage = self.menuItems[index].viewWithTag(9999) {
bgImage.fadeOut(completion: {
(finished: Bool) -> Void in
if finished {
bgImage.removeFromSuperview()
}
})
}
}
}
}
extension UIDevice {
var iPhone: Bool {
return UIDevice().userInterfaceIdiom == .phone
}
enum ScreenType: String {
case iPhone4
case iPhone5
case iPhone6
case iPhone6Plus
case unknown
}
var screenType: ScreenType {
guard iPhone else { return .unknown }
switch UIScreen.main.nativeBounds.height {
case 960:
return .iPhone4
case 1136:
return .iPhone5
case 1334:
return .iPhone6
case 2208:
return .iPhone6Plus
default:
return .unknown
}
}
}
You can easily use to add an image in viewDidLoad():
pageMenu = CAPSPageMenu(viewControllers: controllerArray, frame: CGRect(origin: CGPoint(x: 0,y :70), size: CGSize(width: self.view.frame.width, height: self.view.frame.height)), pageMenuOptions: menuParam)
pageMenu?.addImageOverMyMenuItem(index: 0, imageName: "detail.png")
pageMenu?.addImageOverMyMenuItem(index: 1, imageName: "gallery.png")
pageMenu?.addImageOverMyMenuItem(index: 2, imageName: "time.png")
pageMenu?.addImageOverMyMenuItem(index: 3, imageName: "map.png")
Result :

How to flip a UIView horizontally using Facebook PoP ios framework?

I am using facebook Pop framework for iOS.
How do i make a view flip horizontal (on its Y axis) ?
Here is my code:
func buttonClicked(sender: UIButton) {
aBoolValue = !aBoolValue
sender.userInteractionEnabled = false
let layer = sender.layer
// First let's remove any existing animations
layer.pop_removeAllAnimations()
layer.removeAllAnimations()
let rotation: POPSpringAnimation = POPSpringAnimation(propertyNamed:kPOPLayerRotationY)
if aBoolValue == true {
rotation.toValue = M_PI
sender.setTitle("G", forState: UIControlState.Normal)
} else {
rotation.toValue = 0
sender.setTitle("P", forState: UIControlState.Normal)
}
rotation.completionBlock = {(anim: POPAnimation! , finished: Bool) -> Void in
layer.pop_removeAllAnimations()
sender.userInteractionEnabled = true
}
layer.pop_addAnimation(rotation, forKey: "rotation")
The results of this is that it flips as expected, but after about 5-6 flips, the animation begins to flip randomly and not normally. How do i flip a view horizontally the right way?
It is a bug in pop library. see https://github.com/facebook/pop/issues/71
People can create somewhat similar animation with UIView.animateWithDuration: method.
for example
private func get3DTransformation(angle: Double) -> CATransform3D {
var transform = CATransform3DIdentity
transform.m34 = -1.0 / 500.0
transform = CATransform3DRotate(transform, CGFloat(angle * M_PI / 180.0), 0, 1, 0.0)
return transform
}
private func flipAnimation(view: UIView, completion: (() -> Void) = {}) {
let angle = 180.0
view.layer.transform = get3DTransformation(angle)
UIView.animateWithDuration(1, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: .TransitionNone, animations: { () -> Void in
view.layer.transform = CATransform3DIdentity
}) { (finished) -> Void in
completion()
}
}
func buttonClicked(sender: UIButton) {
aBoolValue = !aBoolValue
if aBoolValue == true {
sender.setTitle("G", forState: UIControlState.Normal)
} else {
sender.setTitle("P", forState: UIControlState.Normal)
}
flipAnimation(sender)
}

Issue with Blur Image in Xcode

I am using Sketch 3 and I created a rectangle which I made the opacity 70% and gave it a background blur of 20%. When I add this image to my project and try to use it on Xcode the blur disappears from the image and I am left with something I did not create, a see through image with no blur. What can be possibly going on?
func createBlurImageView() {
guard let imageView: UIImageView = UIImageView(frame: self.window!.bounds) else {
return
}
imageView.tag = 1000
imageView.image = UIImage(named: "Rectangle136")
self.window!.addSubview(imageView)
self.window!.bringSubviewToFront(imageView)
UIView.animateWithDuration(0.4, animations: {() -> Void in
imageView.alpha = 0.8
})
}
func removeBlurImageView() {
guard let imageView = self.window?.viewWithTag(1000) as? UIImageView else {
print("No good")
return
}
UIView.animateWithDuration(0.4, animations: {() -> Void in
imageView.alpha = 0
}, completion: {(finished: Bool) -> Void in
// remove when finished fading
imageView.removeFromSuperview()
})
}

swift background color animation loop

I want the background color of my iOS app to change between four colors over x amount of seconds
This is what I have so far (it does exactly what I want when I specify just 2 colors)
I also need the animation to run in a loop infinitely.
ViewController.swift
UIView.animateWithDuration(X.0, animations: {
// Color 1
self.view.backgroundColor = UIColor(rgba)
// Color 2
self.view.backgroundColor = UIColor(rgba)
// Color 3
self.view.backgroundColor = UIColor(rgba)
// Color 4
self.view.backgroundColor = UIColor(rgba)
})
Try this out:
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.view.backgroundColor = UIColor.blackColor()
}) { (Bool) -> Void in
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.view.backgroundColor = UIColor.greenColor()
}, completion: { (Bool) -> Void in
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.view.backgroundColor = UIColor.grayColor()
}, completion: { (Bool) -> Void in
UIView.animateWithDuration(1.0, animations: { () -> Void in
self.view.backgroundColor = UIColor.redColor()
}, completion:nil)
})
})
}
In case you want a continuous repeating animation, try this out:
UIView.animate(withDuration: 2, delay: 0.0, options:[UIView.AnimationOptions.repeat, UIView.AnimationOptions.autoreverse], animations: {
self.view.backgroundColor = UIColor.black
self.view.backgroundColor = UIColor.green
self.view.backgroundColor = UIColor.darkGray
self.view.backgroundColor = UIColor.red
}, completion: nil)
Below code helps to keep enable user interaction with animated view. A random color generation (use in case its needed).
UIView.animateWithDuration(7, delay: 1, options:
[UIViewAnimationOptions.AllowUserInteraction,
UIViewAnimationOptions.Repeat,
UIViewAnimationOptions.Autoreverse],
animations: {
self.yourView.backgroundColor = self.randomColor()
self.yourView.backgroundColor = self.randomColor()
}, completion:nil )
func randomColor() -> UIColor {
let randomRed:CGFloat = CGFloat(drand48())
let randomGreen:CGFloat = CGFloat(drand48())
let randomBlue:CGFloat = CGFloat(drand48())
return UIColor(red: randomRed, green: randomGreen, blue: randomBlue,
alpha: 1.0)
}
You have to use NSTimer:
let timer = NSTimer.scheduledTimerWithTimeInterval(5, target: self, selector: "update", userInfo: nil, repeats: true)
func update() {
let nextCollor = getNextColor()
UIView.animateWithDuration(X.0, animations: {
self.view.backgroundColor = nextCollor
})
}
func getNextColor() -> UIColor {
let currentColor = self.view.backgroundColor
if currentColor == smaple1 {
return UIColor.redColor()
} else if currentColor == smaple2 {
return UIColor.grayColor()
} else {
return UIColor.whiteColor()
}
}
NSTimer.scheduledTimerWithTimeInterval runs your code every 5 seconds
PS: do not forget invalidate timer when you done with it. Just call timer.invalidate() for it. Otherwise you get a crash.
Swift 5
override func viewWillAppear(_ animated: Bool) {
self.customButton.backgroundColor = .red
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: [.repeat, .autoreverse], animations: {
self.customButton.backgroundColor = .none
}, completion: nil)
}
i had a dynamic / unknown amount of colors to cycle through, so i refactored the Swift 5 answer a bit:
Swift 5
UIView.animate(withDuration: 1.0, delay: 0.0, options: [.repeat, .autoreverse]) {
// colors is the dynamic [UIColor] array previously defined
for color in colors {
self.colorView.backgroundColor = color
}
} completion: { (finished) in
}
this will nicely cycle through the colors with a fade-style animation -- and with the .autoreverse option, it won't "snap" to the starting color.

Resources