I am trying to make a view transform and kind of have a circle effect until it reaches certain points then it just fills a rectangle. This is for a material design project I am working on. All the code is in Swift 2.0 on an iOS 8 or above device.
func helloWorld(sender: UIButton) {
let point = sender.frame.origin
let rippleViewInitFrame: CGRect = CGRect(x: point.x, y: point.y, width: 4, height: 4)
let rippleView: UIView = UIView(frame: rippleViewInitFrame)
rippleView.backgroundColor = UIColor.MDColor.blue
let bounds: CGRect = CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)
rippleView.layer.masksToBounds = true
rippleView.layer.cornerRadius = 2
self.view.addSubview(rippleView)
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseInOut, animations: {
rippleView.transform = CGAffineTransformMakeScale(200.0, 200.0)
rippleView.bounds = bounds
}, completion: {
finished in
print(rippleView.frame.origin.x)
})
}
Currently the view just grows beyond the size of the screen.
The print statement returns -37176 instead of say 0. I want it to fill the screen and nothing more.
If you want it to fill a rectangle.
1) create a rectangular container UIView.
2) add your rippleView as a subview to this rectangular uiview.
set the clipsToBounds property to yes of your container view.
and just do the animation.
let rectView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100));
rectView.backgroundColor = UIColor.redColor()
// initialRippleView
let rippleView = UIView(frame: CGRect(x: 15, y: 15, width: 2, height: 2));
rippleView.backgroundColor = UIColor.whiteColor()
rippleView.cornerRadius = rippleView.width / 2;
rectView.addSubview(rippleView);
rectView.clipsToBounds = true;
UIView.animateWithDuration(5) { () -> Void in
rippleView.transform = CGAffineTransformMakeScale(100, 100);
}
Try in playground.
import UIKit
import XCPlayground
// the main View
let iPhone = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 568));
iPhone.backgroundColor = UIColor.greenColor();
// the rect View that will be the bounds of the animation
let rectView = UIView(frame: CGRect(x: 0, y: 0, width: 150, height: 150));
rectView.backgroundColor = UIColor.redColor()
rectView.center = iPhone.center;
// initialRippleView
let rippleView = UIView(frame: CGRect(x: 15, y: 15, width: 2, height: 2));
rippleView.layer.cornerRadius = 1;
rippleView.backgroundColor = UIColor.whiteColor()
iPhone.addSubview(rectView);
rectView.addSubview(rippleView);
// this property clips the drawings of subview to be clipped to the bounds of rectView.
rectView.clipsToBounds = true;
UIView.animateWithDuration(5) { () -> Void in
// you may need to calculate the right scale factor
rippleView.transform = CGAffineTransformMakeScale(200, 200);
}
// Playground stuff
XCPShowView("Container View", view: iPhone);
Copy this code in a playground File
Show the Assistant editor
Press play (in the left bottom corner)
Related
I want to make it appear as if my image is slowly getting filtered from top to bottom. I am adding two image views. My processed image is in the background and non-processed in the front. I am making the height of non-processed image 0. Here is my code.
imageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.size.width, height: 400)
imageView.image = processedImage
let nonProcessedImageView = UIImageView()
nonProcessedImageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.size.width, height: 400)
nonProcessedImageView.image = nonProcessedImage
view.addSubview(nonProcessedImageView)
UIView.transition(with: nonProcessedImageView,
duration: 5.0,
animations: {
nonProcessedImageView.frame = CGRect(x: 0, y: 500, width: self.view.bounds.size.width, height: 0)
},
completion: {_ in
})
The non processed image does not even appear on top of the processed.
The issue seems to be that changing the y coordinate of the frame in the animation block leads to issues when using the UIView.Animate function.
See this answer
To quote the most essential part:
You should change the center property (to move the view) and bounds
property (to change its size) instead. Those properties behave as
expected.
Since you want to just reduce the height, you don't need to do anything to the y coordinate
let nonProcessedImageView = UIImageView()
var newImageFrame = imageView.frame
nonProcessedImageView.frame = newImageFrame
nonProcessedImageView.clipsToBounds = true
nonProcessedImageView.contentMode = .scaleAspectFit
nonProcessedImageView.image = imageView.image
view.addSubview(nonProcessedImageView)
// I set this to 1 instead of 0 as 0 does not show the image
// for some reason
newImageFrame.size.height = 1
// Update your processed image
imageView.image = processedImage
UIView.animate(withDuration: 5.0) {
nonProcessedImageView.frame = newImageFrame
}
completion: { (isComplete) in
if isComplete {
nonProcessedImageView.removeFromSuperview()
}
}
This gives some results but as you can see, the animation is not so good because as you reduce the height, the image view's contentMode kicks in and gives the seen effect.
For best results, add the new image view to a UIView and reduce the height of the UIView instead of the UIImageView
var newImageFrame = imageView.frame
let containerView = UIView(frame: newImageFrame)
containerView.clipsToBounds = true
let nonProcessedImageView = UIImageView()
nonProcessedImageView.frame = containerView.bounds
nonProcessedImageView.clipsToBounds = true
nonProcessedImageView.contentMode = .scaleAspectFit // same as original
nonProcessedImageView.image = imageView.image
containerView.addSubview(nonProcessedImageView)
view.addSubview(containerView)
newImageFrame.size.height = 1
imageView.image = processedImage
UIView.animate(withDuration: 3.0) {
containerView.frame = newImageFrame
}
completion: { (isComplete) in
if isComplete {
containerView.removeFromSuperview()
}
}
This should give you what you want:
This works for top to bottom transition.
imageView.frame = CGRect(x: 0, y: 100, width: self.view.bounds.width, height: 400)
imageView.image = nonProcessedImage
view.addSubview(imageView)
let frontView = UIView(frame: CGRect(x: 0, y: 100, width: self.view.frame.width, height: 0))
frontView.clipsToBounds = true
view.addSubview(frontView)
let nonProcessedImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 400))
nonProcessedImageView.image = processedImage
nonProcessedImageView.clipsToBounds = true
frontView.addSubview(nonProcessedImageView)
UIView.transition(with: frontView, duration: 5, options: [.allowAnimatedContent], animations: {
frontView.frame = CGRect(x: 0, y: 100, width: self.view.frame.width, height: 400)
}, completion: nil)
I have an icon that I programmatically display.
How can I display a solid, colored disk behind the icon, at the same screen position?
Icon is opaque.
I will sometimes programmatically change screen position and disk's diameter and color.
Here's how I programmatically display the icon now.............
DispatchQueue.main.async
{
ViewController.wrist_band_UIImageView.frame = CGRect( x: screen_position_x,
y: screen_position_y,
width: App_class.screen_width,
height: App_class.screen_height)
}
UPDATE #1 for D.Mika below...
UPDATE #2 for D.Mika below...
import UIKit
import Foundation
class ViewController: UIViewController
{
static var circleView: UIView!
static let wrist_band_UIImageView: UIImageView = {
let theImageView = UIImageView()
theImageView.image = UIImage( systemName: "applewatch" )
theImageView.translatesAutoresizingMaskIntoConstraints = false
return theImageView
}()
override func viewDidLoad()
{
super.viewDidLoad()
view.addSubview( ViewController.wrist_band_UIImageView )
// Init disk image:
ViewController.circleView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
ViewController.circleView.backgroundColor = .red
ViewController.circleView.layer.cornerRadius = view.bounds.size.height / 2.0
ViewController.circleView.clipsToBounds = true
// Add view to view hierarchy below image view
ViewController.circleView.insertSubview(view, belowSubview: ViewController.wrist_band_UIImageView)
App_class.display_single_wearable()
}
}
class App_class
{
static var is_communication_established = false
static var total_packets = 0
static func display_single_wearable()
{
ViewController.wrist_band_UIImageView.frame = CGRect(x: 0, y: 0,
width: 100, height: 100)
ViewController.circleView.frame = CGRect( x: 0,
y: 0,
width: 100,
height: 100)
ViewController.circleView.layer.cornerRadius = 100
}
static func process_location_xy(text_location_xyz: String)
{}
} // App_class
You can display a colored circle using the following view:
circleView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
circleView.backgroundColor = .red
circleView.layer.cornerRadius = view.bounds.size.height / 2.0
circleView.clipsToBounds = true
// Add view to view hierarchy below image view
view.insertSubview(circleView, belowSubview: wrist_band_UIImageView)
You can change it's position and size by applying a new frame. But you have to change the layer's cornerRadius accordingly.
This is a simple example in a playground:
Edit:
I've modified the sample code to add the view the view hierarchy.
BUT, you still need to adjust the circle's frame, when you modify the image view's frame. (and the cornerRadius accordingly). For example
DispatchQueue.main.async
{
ViewController.wrist_band_UIImageView.frame = CGRect( x: screen_position_x,
y: screen_position_y,
width: App_class.screen_width,
height: App_class.screen_height)
circleView.frame = CGRect( x: screen_position_x,
y: screen_position_y,
width: App_class.screen_width,
height: App_class.screen_height)
circleView.layer.cornerRadius = App_class.screen_width
}
And you need to add a variable to the viewController holding the reference to the circle view, like:
var circleView: UIView!
The following answer works.
The answers from d.mika above did not work. Plus, he was insulting. ;-)
// Show red circle
let circlePath = UIBezierPath(arcCenter: CGPoint(x: 200, y: 400), radius: CGFloat(40), startAngle: CGFloat(0), endAngle: CGFloat(Double.pi * 2), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
// Change the fill color
shapeLayer.fillColor = UIColor.red.cgColor
// You can change the stroke color
shapeLayer.strokeColor = UIColor.red.cgColor
// You can change the line width
shapeLayer.lineWidth = 3.0
view.layer.addSublayer(shapeLayer)
I'm trying to create a popup label over the imageview. But in my application it doesn't work. I've made a test application with the same screen: UIImageView and below a UIView with a UIButton on the view.
So, where are two questions.
What could be difference in a code for such a different behaviour?
Why in my application the UILabel doesn't conforms to the initial frame?
The code of the function inside my viewController is the same:
private func showBanner(startY: CGFloat, targetView: UIView) {
let height: CGFloat = 42
let finishY = startY - height
let bannerLabel = UILabel(frame: CGRect(x: 0, y: startY, width: self.view.frame.width, height: height))
bannerLabel.translatesAutoresizingMaskIntoConstraints = false
bannerLabel.font = UIFont.systemFont(ofSize: 13, weight: .regular)
bannerLabel.textColor = .lightGray
bannerLabel.backgroundColor = .black
bannerLabel.textAlignment = .center
bannerLabel.numberOfLines = 1
bannerLabel.text = "You've added the item to the favorites"
targetView.addSubview(bannerLabel)
UIView.animate(withDuration: 0.5, animations: {
bannerLabel.frame = CGRect(x: 0,
y: finishY,
width: self.view.frame.width,
height: height
)
}) {
_ in
UIView.animate(withDuration: 0.5, delay: 1.0, options: .curveLinear, animations: {
bannerLabel.frame = CGRect(x: 0,
y: startY,
width: self.view.frame.width,
height: height
)
}, completion: {
_ in
bannerLabel.removeFromSuperview()
})
}
}
The function is being called so:
showBanner(startY: itemsImageView.frame.maxY, targetView: itemsImageView)
The problem was in the line:
bannerLabel.translatesAutoresizingMaskIntoConstraints = false
As soon as this line was removed, the problem has dissapeared.
When using a UIView as an SCNMaterialProperty in a SceneKit scene, can that view be part of the responder chain?
To clarify how:
omg_you_can_use_an_actual_uiview_now = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
material_plane.diffuse.contents = omg_you_can_use_an_actual_uiview_now
let geom_plane = SCNPlane(width: 1, height: 1)
geom_plane.materials = [material_plane]
My issue is that when I press the button that runs the dealToPlayer function(), everything acts as if a card has been dealt, updates total value of cards dealt, but it does not show any animation of the card being dealt or placed. Why might this be? I am not using auto layout for this scene at all.
func dealToPlayer() {
let index = Int(arc4random_uniform(UInt32(deckCards.count)))
let drawnCard = deckCards.remove(at: index)
randomCards.append(drawnCard)
randomCard = randomCards[3 + cardSelect]
image = UIImageView(frame: CGRect(x: self.deckLocation.x, y: self.deckLocation.y, width: self.width, height: self.height))
image.image = mapping[randomCard]
cardsOut = cardsOut + 1
print("cards out is currently: \(cardsOut)")
cardSelect = cardSelect + 1
UIView.animate(withDuration: 0.5, delay: 1.5, options: [], animations: {
self.image.frame = CGRect(x: self.cardTwoLocation.x - (self.cardsOut * self.thirdWidth), y: self.cardTwoLocation.y, width: self.width, height: self.height)
}, completion: nil)
checkPlayerValue()
}
First, make sure your image view is added to your view controller.
Second, make sure your animation is called by putting a break point.
Third, print out the location and make sure the new x and y are valid, and they are different than the previous x and y.
Here is a sample duplicate of your code an it works fine
image = UIImageView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
image.backgroundColor = .red
self.view.addSubview(image)
UIView.animate(withDuration: 0.5, delay: 1.5, options: [], animations: {
self.image.frame = CGRect(x: 200, y: 200, width: 100, height: 100)
}, completion: nil)