Does iOS Playgrounds supports UIView animations? - ios

Is it possible to preview animation done for UIView with animateWithDuration in Playground? I am adding XCPShowView right after initialization of UIView and its shows everything it in a final state no matter what position on a timeline I will choose.

Yes, it is (checked on Xcode 6.1.1, but may apply to earlier versions).
You should:
Select the "Run in Full Simulator" option (see this answer for a step-by-step).
Add the view you wish to animate within a container view.
For example, this shows a black square moving in a down-left motion:
import UIKit
import XCPlayground
let container = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
XCPShowView("container", container)
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0))
view.backgroundColor = UIColor.blackColor()
container.addSubview(view)
UIView.animateWithDuration(5.0, animations: { () -> Void in
view.center = CGPoint(x: 75.0, y: 75.0)
})

Xcode 7 update : XCPShowView is deprecated but you can see the animation in the playground liveView. And there is more coming soon: Interactive Playgrounds
Here is an update to Joseph Chen sample code. I also changed the color to green as the container default background is black :
import UIKit
import XCPlayground
let container = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0))
// XCPShowView("container", view: container) // -> deprecated
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0))
view.backgroundColor = UIColor.greenColor()
container.addSubview(view)
UIView.animateWithDuration(5) {
view.center = CGPoint(x: 75.0, y: 75.0)
}
XCPlaygroundPage.currentPage.liveView=container

Related

how to programmatically show a colored disk behind a UIImageView, in swift on an iOS device

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)

IOS - How to create Facebook reaction bar with blur background?

Although it may not be the week to replicate some design of Facebook, I would like to be able to design my own version of the reaction indicator view below.
I have three UIImageViews lined in the same positions as above. The problem is that, unlike Facebook, the background color may change (i.e is on top of a UIBlurEffect) and therefore I am unable to set the border color to white.
I thought it would make sense to set the borderColor like so:
imageViewOne.layer.borderColor = UIColor.clear.cgColor
imageViewOne.layer.borderWidth = 2
However, the underlying imageViewTwo is displayed in the border instead of the background color.
So far, I have this:
Would appreciate some help/ideas on how to make this work - I'm thinking of masks but not sure whether a. this is the correct solution and b. how to achieve the desired effect. To clarify, I am not able to set the border color as a constant as it will change with the UIBlurEffect.
In my opinion, there are 2 way to resolve your problem.
Create and use clipped image for Wow and Love like below Love image.
Another way is using mask property of UIView. Creating a mask image and apply it for mask property.
Mask image looks like.
Code for applying mask.
let imvLoveMask = UIImageView.init(image: UIImage.init(named: "MASK_IMAGE_NAME"));
imvLoveMask.frame = CGRect.init(x: 0, y: 0, width: imvLove.frame.size.width, height: imvLove.frame.size.height);
imvLove.mask = imvLoveMask;
Both of 2 above way can help you achieve what you want in the question. Background of icons in below image is an UIVisualEffectView.
In my opinion, the first way with clipped image is better and faster because you don't need to apply mask for your imageView. But if you don't want to create a clipped image for some reason, you can use the second way.
For more detail, you can take a look at my demo repo
You need to clip part of the image in order to let underlying content be visible in the gaps between images. See playground sample.
Add smile_1, smile_2, smile_3 images to playground resources. I took emoji images from https://emojipedia.org/facebook/.
import UIKit
import PlaygroundSupport
class EmojiView: UIView {
var imageView = UIImageView()
var imageInset = UIEdgeInsets(top: 3.0, left: 3.0, bottom: 3.0, right: 3.0)
var shapeLayer = CAShapeLayer()
var overlap: CGFloat = 0.0 {
didSet {
self.updateShape()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
// MARK: UIView
override func awakeFromNib() {
super.awakeFromNib()
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
self.imageView.frame = UIEdgeInsetsInsetRect(self.bounds, self.imageInset)
self.shapeLayer.frame = self.bounds
self.updateShape()
}
// MARK: Private
private func setup() {
self.addSubview(self.imageView)
self.layer.mask = self.shapeLayer
}
private func updateShape() {
let frame = self.bounds
let path = UIBezierPath(rect: frame)
// Cut off part of the image if overlap more then > 0
if 0 < self.overlap {
path.usesEvenOddFillRule = true
path.append(UIBezierPath(ovalIn: CGRect(x: -frame.width + self.overlap, y: 0.0, width: frame.width, height: frame.height)).reversing())
}
self.shapeLayer.path = path.cgPath
}
}
let overlap: CGFloat = 10 // Amount of pixels emojis overlap each other
// Create emoji views
let emojiView_1 = EmojiView(frame: CGRect(x: 5.0, y: 5.0, width: 40.0, height: 40.0))
emojiView_1.imageView.image = UIImage(named: "smile_1")
let emojiView_2 = EmojiView(frame: CGRect(x: emojiView_1.frame.maxX - overlap, y: 5.0, width: 40.0, height: 40.0))
emojiView_2.imageView.image = UIImage(named: "smile_2")
emojiView_2.overlap = overlap
let emojiView_3 = EmojiView(frame: CGRect(x: emojiView_2.frame.maxX - overlap, y: 5.0, width: 40.0, height: 40.0))
emojiView_3.imageView.image = UIImage(named: "smile_3")
emojiView_3.overlap = overlap
let holderView = UIView(frame: CGRect(x: 0.0, y: 0.0, width: emojiView_3.frame.maxX + 5, height: 50.0))
// Add gradient layer
let gradientLayer = CAGradientLayer()
gradientLayer.frame = holderView.bounds
gradientLayer.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
holderView.layer.addSublayer(gradientLayer)
// Add emoji views
holderView.addSubview(emojiView_1)
holderView.addSubview(emojiView_2)
holderView.addSubview(emojiView_3)
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = holderView
use this :
self.imageViewOne.layer.cornerRadius = self.imageViewOne.layer.bounds.width/2
self.imageViewOne.layer.masksToBounds = true
Simple suggestion: As you are setting border color programatically, you have a control to change it, according to background color (if background color is solid (not a gradient)).
imageViewOne.layer.borderColor = imageViewOne.superview?.backgroundColor ?? UIColor.white
imageViewOne.layer.borderWidth = 2.0
Actually instead of masking, you can put your images in a view which has white background and round(set corner radius). Then you can put these views (which has white background and images in it) via settings their zPosition or on storyboard with view hierarchy.
I've prepared a little playground for you. You can see the result in the screenshot. I've put a view inside the containerViews instead you can use uiimageview etc. It's a bit ugly but solves your issue I guess it's up to you to decide how use it.
Here is the code, you can just copy and paste it to a new playground and test it.
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
var mainContainerView = UIView(frame: CGRect(x: 0, y: 0, width: 140, height: 80))
mainContainerView.backgroundColor = UIColor.white
var containerView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
containerView.backgroundColor = UIColor.white
containerView.layer.cornerRadius = containerView.frame.width / 2
var innerView = UIView(frame: CGRect(x: 10, y: 10, width: 60, height: 60))
innerView.backgroundColor = UIColor.red
innerView.layer.cornerRadius = innerView.frame.width / 2
containerView.addSubview(innerView)
var containerView2 = UIView(frame: CGRect(x: 60, y: 0, width: 80, height: 80))
containerView2.backgroundColor = UIColor.yellow
containerView2.layer.cornerRadius = containerView2.frame.width / 2
var innerView2 = UIView(frame: CGRect(x: 10, y: 10, width: 60, height: 60))
innerView2.backgroundColor = UIColor.green
innerView2.layer.cornerRadius = innerView2.frame.width / 2
containerView2.addSubview(innerView2)
containerView.layer.zPosition = 2
containerView2.layer.zPosition = 1
mainContainerView.addSubview(containerView)
mainContainerView.addSubview(containerView2)
PlaygroundPage.current.liveView = mainContainerView

SWIFT slide one Object "in and out"

Is there a way to animate an object in swift (UIView, UIButton, etc.) so that it slides out on the left side of the screen and slides in at the same time on the right side of the screen, that the parts of f.e. an UIView that already slided out on the left side are already sliding in again from the right side.
I hope you can understand my question, its a bit tricky to explain this problem.
I'm using Xcode + Swift 3.0.
Thank you.
that the parts of f.e. an UIView that already slided out on the left side are already sliding in again from the right side
This almost slipped by me. The only way you have one "view" appear in two places on a screen is by really having two views, one on the left and one on the right.
Then, you could set the widthAnchors (properly), hide the subviews (properly), and animate. If the animation is fast enough (my one-sided slide out is set for 0.3 seconds) the user would think it's all one view.
You can use UIView.animate(). Trigger a second animation after the first has completed.
Possible implementation to get the idea:
var container = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0))
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0))
view.center = CGPoint(x: 100, y: 100)
view.backgroundColor = UIColor.green
container.addSubview(view)
let screenWidth = CGFloat(356.0)
// animate to left
UIView.animate(withDuration: 2.0, delay: 0.0, options: [], animations: {
view.center = CGPoint(x: -view.frame.size.width, y: view.frame.origin.y)
}, completion: { (finished: Bool) in
//set view to right of screen
view.center = CGPoint(x: view.frame.size.width + screenWidth, y: view.frame.origin.y)
// animate back to center
UIView.animate(withDuration: 2.0, delay: 0.0, options: [], animations: {
view.center = CGPoint(x: 100, y: 100)
})
})

Transform UIView within bounds

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)

Updating UIView makes a hash of subview's alignment / Swift

I'm new in Swift. I want to make an app with a moving subview. I'm testing the app, and when i touch down the button, the subview moves with the correct value, but when the parent UIView updates because of changing label, the subview jump back to the origin coordinate what i set up in the storyboard. I use core graphics to draw circle. I want to move the circle on the screen by tap a button. How can i solved this problem?
class ViewController: UIViewController {
#IBOutlet weak var movingItem: MovingItem! /* inheritance from UIView */
#IBAction func move() {
self.movingItem.frame = CGRect(x: movingItem.frame.origin.x + 5.0,
y: movingItem.frame.origin.y,
width: movingItem.frame.width,
height: movingItem.frame.height)
}
override func viewDidLoad() {
self.movingItem.frame = CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0)
self.movingItem.setNeedsDisplay()
}
//more code...
}
#IBDesignable
class MovingItem: UIView {
override func drawRect(rect: CGRect) {
var context = UIGraphicsGetCurrentContext()
CGContextBeginPath(context)
var rectForCircle = CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0)
CGContextAddEllipseInRect(context, rectForCircle)
UIColor.redColor().setFill()
CGContextDrawPath(context, kCGPathFill)
}
}
Try instead of
var rectForCircle = CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0)
CGContextAddEllipseInRect(context, rectForCircle)
like this:
CGContextAddEllipseInRect(context, self.frame)
and insert line
self.movingItem.setNeedsDisplay()
after
self.movingItem.frame = CGRect(x: movingItem.frame.origin.x + 5.0,
y: movingItem.frame.origin.y,
width: movingItem.frame.width,
height: movingItem.frame.height)

Resources