Issues with animation in swift - ios

I am trying to make some buttons look like they are growing. Could anybody tell my why this code only makes the buttons appear after the while loop completes?
var size = CGFloat(5)
let b1 = UIButton()
b1.backgroundColor = UIColor(red: 200, green: 0, blue: 0, alpha: 100)
b1.setTitle("", forState: .Normal)
b1.setTitleColor(UIColor(red: 255, green: 255, blue: 255, alpha: 100), forState: .Normal)
b1.frame = CGRectMake((view.frame.width/2)-105, (view.frame.height/2-105), CGFloat(size), CGFloat(size))
b1.titleLabel?.font = UIFont(name: "Helvetica", size: 24)
let b2 = UIButton()
b2.backgroundColor = UIColor(red: 0, green: 0, blue: 200, alpha: 100)
b2.setTitle("", forState: .Normal)
b2.setTitleColor(UIColor(red: 255, green: 255, blue: 255, alpha: 100), forState: .Normal)
b2.frame = CGRectMake((view.frame.width/2)+5, (view.frame.height/2)-105, CGFloat(size), CGFloat(size))
b2.titleLabel?.font = UIFont(name: "Helvetica", size: 24)
let b3 = UIButton()
b3.backgroundColor = UIColor(red: 0, green: 200, blue: 0, alpha: 100)
b3.setTitle("", forState: .Normal)
b3.setTitleColor(UIColor(red: 255, green: 255, blue: 255, alpha: 100), forState: .Normal)
b3.frame = CGRectMake((view.frame.width/2)-105, view.frame.height/2+5, CGFloat(size), CGFloat(size))
b3.titleLabel?.font = UIFont(name: "Helvetica", size: 24)
let b4 = UIButton()
b4.backgroundColor = UIColor(red: 200, green: 200, blue: 0, alpha: 100)
b4.setTitle("", forState: .Normal)
b4.setTitleColor(UIColor(red: 255, green: 255, blue: 255, alpha: 100), forState: .Normal)
b4.frame = CGRectMake((view.frame.width/2)+5, view.frame.height/2+5, CGFloat(size), CGFloat(size))
b4.titleLabel?.font = UIFont(name: "Helvetica", size: 24)
while (size < 100) {
size+=1
b1.frame = CGRectMake(CGFloat(view.frame.width/2)-105/*+CGFloat(100-size)*/, CGFloat(view.frame.height/2-105)+CGFloat(100-size), CGFloat(size), CGFloat(size))
b2.frame = CGRectMake(CGFloat(view.frame.width/2)+5/*+CGFloat(100-size)*/, (view.frame.height/2)-105+(100-size), CGFloat(size), CGFloat(size))
b3.frame = CGRectMake(CGFloat(view.frame.width/2)-105/*+CGFloat(100-size)*/, view.frame.height/2+5+(100-size), CGFloat(size), CGFloat(size))
b4.frame = CGRectMake(CGFloat(view.frame.width/2)+5/*+CGFloat(100-size)*/, view.frame.height/2+5+(100-size), CGFloat(size), CGFloat(size))
view.addSubview(b1)
view.addSubview(b2)
view.addSubview(b3)
view.addSubview(b4)
}'

UI Changes don't take effect until after your code returns and the program visits the event loop. All the UI changes you make in a function call get saved up and then applied on the next pass through the event loop.
Your while loop runs all the way to the end, and THEN the view changes are rendered to the screen, all at once.
As JYeh says in his answer, you should use UIView animation methods in the family animateWithDuration.

I suggest reading up on how to use a UIView Animation: https://www.raywenderlich.com/76200/basic-uiview-animation-swift-tutorial
Instead of the while loop, use a UIView.animateWithDuration function call. The reason the buttons are just appearing is because the while loop finishes so fast that you can't see anything happening, and while loops shouldn't be used to do animation.

How about this?
// make an array containing 4 CGRect values:
var frames = Array<CGRect>(count: 4, repeatedValue: CGRectNull)
// divide view into 4 parts: left and right, then divide left and right into 2 parts each, top and bottom
let (left, right) = view.bounds.divide( view.bounds.width * 0.5, fromEdge: .MinXEdge )
(frames[0], frames[2]) = left.divide( view.bounds.height * 0.5, fromEdge:.MinYEdge)
(frames[1], frames[3]) = right.divide( view.bounds.height * 0.5, fromEdge:.MinYEdge)
// iterate over the range 0 to 3
(0...3).forEach { // this block (function) has the current number as its input
// I didn't name the input, so Swift names it $0
let b = UIButton( type: .System ) // make a standard button
b.frame = frames[ $0 ] // give the button a frame
/*
...Add code to customize your button `b` here
*/
view.addSubview( b ) // add button to our view
b.layer.addAnimation({ // add an animation to the button's layer.
// We will scale to current size from 5% size:
let anim = CABasicAnimation( keyPath: "transform" )
anim.fromValue = NSValue( CATransform3D: CATransform3DMakeScale( 0.05, 0.05, 1.0))
anim.duration = 5.0 // animation takes 5.0 s, just for demo purposes
return anim // the created animation, to be added
}(), forKey: nil)
}

Related

I’m trying to make two buttons that print two different things in swift playground

I’m new to swift and coding in general and I’m trying to make a truth or dare game but when I run the code using step through my code and press the red button it is running the code from Responder and not from responder1. I think the problem might be that I maid the 2nd button just by copying the for the 1st button and adding 1, and then swift thinks that the button 1 is the same as button and it runs the code for the 1st button.
import PlaygroundSupport
import UIKit
import SpriteKit
//
let view = UIView()
view.backgroundColor = #colorLiteral(red: 0.0, green: 0.7522757053375244, blue: 0.780606746673584, alpha: 1.0)
view.frame = CGRect(x: 0, y: 0, width: 400, height: 300)
let lbl = UILabel(frame: CGRect(x: 50, y: 0, width: 200, height: 50))
lbl.text = "Hello, World!"
view.addSubview(lbl)
lbl.textColor = .blue
let button = UIButton(frame: CGRect(x: 50, y: 100, width: 100, height: 50))
button.backgroundColor = #colorLiteral(red: 0.721568644046783, green: 0.886274516582489, blue: 0.592156887054443, alpha: 1.0)
//button.setTitleColor(#colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0), for: UIControlState.selected)
button.setTitle("button", for: UIControl.State.selected)
button.layer.cornerRadius = 10
view.addSubview(button)
let button1 = UIButton(frame: CGRect(x: 250, y: 100, width: 100, height: 50))
button1.backgroundColor = #colorLiteral(red: 1.0, green: 0.1893465518951416, blue: 0.1893465518951416, alpha: 1.0)
//button1.setTitleColor(#colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0), for: UIControlState.selected)
button1.setTitle("button1", for: UIControl.State.selected)
button1.layer.cornerRadius = 10
view.addSubview(button1)
class Responser: NSObject
{
//Method to be called
#objc func printname()
{
lbl.text = "aha"
}
}
let responder = Responser()
button.addTarget(responder, action: #selector(Responser.printname), for:.touchUpInside)
class Responser1: NSObject
{
//Method to be called
#objc func printname()
{
lbl.text = "UAh"
}
}
let responder1 = Responser()
button1.addTarget(responder1, action: #selector(Responser1.printname), for:.touchUpInside)
PlaygroundPage.current.liveView = view

How to add gradients on custom class buttons?

I am beginner in Swift/Xcode. I made a custom class in my Xcode project to handle buttons appearance (color, shape etc) that works find.
However, I can't succeed to add gradients to these buttons.
I tried the below code, but it adds a new squared gradient onto my rounded buttons instead of applying the intended gradients to those buttons. Here is what I already gave many tries:
- add this code to my custom button class (inheriting from UIButton)
- add this code to my custom button class (inheriting from UIView)
- above 2 approaches and removing the backgroungColor settings
- Using a separate function called from viewController
func ConfigMenuButtons() {
// Set button shape color
layer.cornerRadius = 37
// Set button size
translatesAutoresizingMaskIntoConstraints = false
widthAnchor.constraint(equalToConstant: 74).isActive = true
heightAnchor.constraint(equalToConstant: 74).isActive = true
// Set button text
setTitleColor(.white, for: .normal)
titleLabel?.font = UIFont.boldSystemFont(ofSize: 15)
// Set button shadow
layer.shadowRadius = 2
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.8
// Set button gradient colors
let lightBlue = UIColor(red: 122/255, green: 127/255, blue: 249/255, alpha: 1)
let darkBlue = UIColor(red: 74/255, green: 88/255, blue: 205/255, alpha: 1)
// Set gradients
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [lightBlue.cgColor, darkBlue.cgColor]
gradientLayer.locations = [0.0,1.0]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
clipsToBounds = true
// insert gradients to button
layer.insertSublayer(gradientLayer, at: 0)
}
But I always end up with this "square shaped gradient layer" appearing onto the targeted buttons, instead of having these gradients applied directly to these buttons.
If someone had any piece of advice, it would be fantastic.
Thank you!
Here is the screen shot of the button with its partial expected effect
Here is a screenshot of fixed issue
I finally fixed the issue as below adding a CGRect size.
func ConfigMenuButtons() {
// Set button shape
layer.cornerRadius = 37
// Set button size
translatesAutoresizingMaskIntoConstraints = false
widthAnchor.constraint(equalToConstant: 74).isActive = true
heightAnchor.constraint(equalToConstant: 74).isActive = true
// Set button text
setTitleColor(.white, for: .normal)
titleLabel?.font = UIFont.boldSystemFont(ofSize: 15)
// Set button shadow
layer.shadowRadius = 2
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 2)
layer.shadowOpacity = 0.8
// Set button gradient colors
let lightBlue = UIColor(red: 112/255, green: 117/255, blue: 239/255, alpha: 1)
let darkBlue = UIColor(red: 70/255, green: 84/255, blue: 201/255, alpha: 1)
// Set gradients
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [lightBlue.cgColor, darkBlue.cgColor]
gradientLayer.locations = [0.0,1.0]
gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
// THAT IS THE LINE THAT COULD FIX THE ISSUE
gradientLayer.frame = CGRect(x: 0, y: 0, width: 74, height: 74)
// Set rounded shape
gradientLayer.cornerRadius = 37
// insert gradients to button
layer.insertSublayer(gradientLayer, at: 0)
}

Swift text attribute issue: Letter Spacing + Highlighted

I have a button
buttonSign
created and tested with the highlighted change of colour.
I need to create also -0.5 letter spacing with:
let attributedString = NSMutableAttributedString(string: (buttonSign.titleLabel?.text!)!)
attributedString.addAttribute(NSAttributedString.Key.kern, value: -0.5, range: NSRange(location: 0, length: (buttonSign.titleLabel?.text!.count)!))
buttonSign.setAttributedTitle(attributedString, for: .normal)
The button code:
let buttonSign = UIButton(frame: CGRect(x: UIScreen.main.bounds.width/2 - 170, y: UIScreen.main.bounds.height-41-56-16-56, width: 340, height: 56))
buttonSign.setTitleColor(UIColor.white, for: UIControl.State.normal)
buttonSign.setTitleColor(UIColor.blue, for: UIControl.State.highlighted)
buttonSign.backgroundColor = UIColor(red: 95.0 / 255.0, green: 84.0 / 255.0, blue: 216.0 / 255.0, alpha: 1.0)
buttonSign.addTarget(self, action: #selector(signUpAction), for: .touchUpInside)
buttonSign.layer.cornerRadius = buttonSign.bounds.size.height / 2
buttonSign.clipsToBounds = true
buttonSign.setTitle("SIGN UP",for: .normal)
buttonSign.titleLabel?.font = UIFont(name: "SFProDisplay-Bold", size: 20)
buttonSign.titleLabel?.textColor = UIColor(red: 111.0 / 255.0, green: 107.0 / 255.0, blue: 244.0 / 255.0, alpha: 1.0)
view.addSubview(buttonSign)
This works but when I apply
attributedString
It doesn't change the colour of text when highlighted.
How is that possible?

UIButton sizeToFit not working

This is my code :
let button_video = UIButton(type: .custom)
button_video.frame = CGRect.zero
if subsession.VideoUrl != nil
{
button_video.frame = CGRect(x: 10, y: yAxis + 15, width: 300, height: 30)
button_video.contentEdgeInsets = UIEdgeInsets(top: 3, left: 28, bottom: 7, right: 5)
if iVideoNumber == 0
{
button_video.setTitle(String(format: "Video #%i", 1), for: .normal)
}
else
{
button_video.setTitle(String(format: "Video #%i", iVideoNumber+1), for: .normal)
}
button_video.titleLabel?.font = UIFont(name: "OpenSans-Regular", size: 15)
button_video.titleLabel?.textColor = UIColor.white
button_video.setTitleColor(UIColor.white, for: .normal)
button_video.titleLabel?.numberOfLines = 0
button_video.titleLabel?.lineBreakMode = .byWordWrapping
button_video.titleLabel?.textAlignment = .center
button_video.tag = iCount
button_video.addTarget(self, action: #selector(CourseDetailViewController.loadVideo(sender:)), for: .touchUpInside)
let btnGradient = CAGradientLayer()
btnGradient.frame = button_video.bounds
btnGradient.cornerRadius = 6
btnGradient.colors = [(UIColor(red: 174.0 / 255.0, green: 127.0 / 255.0, blue: 183.0 / 255.0, alpha: 1.0).cgColor as CGColor), (UIColor(red: 78.0 / 255.0, green: 57.0 / 255.0, blue: 96.0 / 255.0, alpha: 1.0).cgColor as CGColor)]
button_video.layer.insertSublayer(btnGradient, at: 0)
button_video.sizeToFit()
view_scrollView.addSubview(button_video)
}
}
The button however occupies the whole width of 300
You are inserting sublayer with defined frame, and inserting it in your button. So sizetofit is working its size is equal to largest layes in button which is 300 due to size of gradient layer.
If you are using multiple titles for different UIControlState, make sure to change the UIControlState before asking for sizeToFit.
In my UIButton I had to use multiple titles, one for UIControlState.normal and another for .selected state, I was expecting the result of .normal state, but the button was on .selected state.

Creating a button in the Playground app on iPad

I am working with the new beta version of the Playground app in iOS 10.
I want to get some UIKit elements working in a playground.
Swift 3 is just different enough from Swift 2, I am having some problems.
I got a label and text box working, but I cannot get a button working.
I have the button showing up but cannot get text in it or an action working on touch-up.
Here is my code I have.
import PlaygroundSupport
import UIKit
//
let view = UIView()
view.backgroundColor = #colorLiteral(red: 0.909803926944733, green: 0.47843137383461, blue: 0.643137276172638, alpha: 1.0)
PlaygroundPage.current.liveView = view
let lbl = UILabel(frame: CGRect(x: 50, y: 0, width: 200, height: 50))
lbl.text = "Hello, World!"
view.addSubview(lbl)
let txt = UITextField(frame: CGRect(x: 150, y: 200, width: 200, height: 50))
//txt.placeholder = "Enter text here"
//txt.font = UIFont.systemFont(ofSize: 15)
txt.borderStyle = UITextBorderStyle.roundedRect
view.addSubview(txt)
func buttonPressed(sender: UIButton)
{
//sender.backgroundColor = #colorLiteral(red: 0.725490212440491, green: 0.47843137383461, blue: 0.0980392172932625, alpha: 1.0)
}
let button = UIButton(frame: CGRect(x: 50, y: 100, width: 100, height: 50))
button.backgroundColor = #colorLiteral(red: 0.721568644046783, green: 0.886274516582489, blue: 0.592156887054443, alpha: 1.0)
button.setTitleColor(#colorLiteral(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0), for: UIControlState.focused)
button.setTitle("button", for: UIControlState.focused)
button.layer.cornerRadius = 10
button.addTarget(button, action: "buttonPressed", for: UIControlEvents.touchUpInside)
view.addSubview(button)
I've been trying the same and the following code works for me:
import UIKit
import PlaygroundSupport
class Receiver {
#objc func buttonClicked() {
print("click")
}
}
let view = UIView()
view.backgroundColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
let receiver = Receiver()
let button = UIButton(frame: CGRect(x: 10, y: 10, width: 100, height: 50))
button.setTitle("TestButton", for: .normal)
button.setTitleColor(#colorLiteral(red: 0.474509805440903, green: 0.839215695858002, blue: 0.976470589637756, alpha: 1.0), for: .normal)
button.addTarget(receiver, action: #selector(Receiver.buttonClicked), for: .touchUpInside)
PlaygroundPage.current.liveView = view
view.addSubview(button)
I think the important difference is that you add the button as target of the .touchUpInside event. Unfortunately, the button does not implement your buttonPressed method (it's just defined in the Playground, not as part of the UIButton class). As there is no self in a general playground (to refer to the function you wrote), I wrapped it into a new class and used an instance of this class to be the receiver of the .touchUpInside event.

Resources