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

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)

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)

How to animate UIBezierPath inside of draw(_:)?

I have a task to draw rectangle in draw(_:) method and to animate height of it.
var height = 0
override func draw(_ rect: CGRect) {
let rect = CGRect(x: 0, y: 0, width: 100, height: height)
let rectanglePath = UIBezierPath(rect: rect)
UIColor.green.setFill()
rectanglePath.fill()
}
And I'm looking to animate something like this:
func animate() {
UIView.animate(withDuration: 2) {
self.height = 300
}
}
I also can use CAShapeLayer and CABasicAnimation, but I don't know how to draw it in draw(_:) method. Using draw(_:) method is required in this task :(
class View: UIView{
override func draw(_ rect: CGRect)
{
let rectanglePath = UIBezierPath(rect: self.frame)
UIColor.green.setFill()
rectanglePath.fill()
}}
class ViewController: UIViewController{
override func viewDidLoad()
{
super.viewDidLoad()
let subview = View(frame: CGRect(x: 0, y: 0, width: 100, height: 0))
self.view.addSubview(subview)
UIViewPropertyAnimator.runningPropertyAnimator(
withDuration: 2,
delay: 0,
options: .curveLinear,
animations: {self.view.subviews[0].frame = CGRect(x: 0, y: 0, width: 100, height: 100)}
)
}}

Magnifying glass UIView

Trying to add a magnifying-glass effect to a uiview, but the magnfifying glass is always black.
My code for the Magnifing glass view:
public override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()!
context.translateBy(x: self.frame.size.width/2, y: self.frame.size.height/2)
context.scaleBy(x: self.scale, y: self.scale)
self.viewToMagnify.layer.render(in: context)
}
And in the View-to-Magnify:
self.magnifiedView = MagnifiedView.init(frame: CGRect(x: 200, y: 100, width: 100, height: 100))
self.magnifiedView.viewToMagnify = self.view
self.view.addSubview(magnifiedView)
Edit:
I want to use it in an ARSession on the camera output. The code above works fine for a UIImageView. Updating the View of the camera output in real-time seems to make some problems.

Custom progress view

I want to do a custom progress view for my iOS app, with 2 dots. Here is my code:
import UIKit
#IBDesignable
class StepProgressView: UIView {
#IBInspectable var progress: Float = 0
var progressColor = UIColor.blackColor()
var bgColor = UIColor.whiteColor()
override func layoutSubviews() {
self.backgroundColor = UIColor.clearColor()
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
let height = frame.height-8
let circle1 = UIView(frame: CGRect(x: frame.width*(1/3), y: 0, width: frame.height, height: frame.height))
circle1.backgroundColor = bgColor
circle1.layer.cornerRadius = frame.height/2
addSubview(circle1)
let circle2 = UIView(frame: CGRect(x: frame.width*(2/3), y: 0, width: frame.height, height: frame.height))
circle2.backgroundColor = bgColor
circle2.layer.cornerRadius = frame.height/2
addSubview(circle2)
let bgView = UIView(frame: CGRect(x: height/2, y: 4, width: frame.width-height/2, height: height))
bgView.backgroundColor = bgColor
bgView.layer.cornerRadius = height/2
addSubview(bgView)
let progressView = UIView(frame: CGRect(x: 0, y: 4, width: frame.width*CGFloat(progress), height: height))
progressView.backgroundColor = progressColor
progressView.layer.cornerRadius = height/2
addSubview(progressView)
}
}
The result:
However, as you can see, the circles aren't "filled" when the progression pass over one of them, and I don't know how to do that. I could create another view but I don't know how to handle the corner radius.
Can you help me ?
Thanks

Does iOS Playgrounds supports UIView animations?

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

Resources