Swift callback function for draw()? - ios

I have a customView where I overwrite drawRect:. I also have some cases, where I want to display a UILabel ontop of my customView:
if self.ticks.count < 50 {
self.label.frame = self.bounds
self.label.text = "This chart requires 50 or more ticks.\nKeep Ticking."
self.addSubview(label)
}
However if I call this code before the drawRect is called, then my label doesn't show up. Is there a callback method like viewDidDraw or something?
EDIT
Someone asked for it, so here is all the code that is now doing what I need it to do. I think the answer to the question is to override layoutSubviews.
#IBDesignable open class CustomView: UIView {
#objc open var ticks:[Tick] = []
let label = UILabel()
required public init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
required public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
func setup(){
self.backgroundColor = UIColor.clear
self.label.frame = self.bounds
self.label.text = "This chart requires 50 or more ticks.\nKeep Ticking."
self.label.numberOfLines = 0
self.label.textAlignment = .center
self.addSubview(label)
}
override open func layoutSubviews() {
self.label.frame = self.bounds
}
#objc open func setSessions(sessions:[Session]){
self.ticks = []
for session in sessions.reversed(){
if self.ticks.count < 250{
self.ticks = self.ticks + (session.getShots() as! [Shot])
}else{
break
}
}
if self.ticks.count < 50 {
self.label.isHidden = false
}else{
self.label.isHidden = true
}
self.bringSubview(toFront: self.label)
}
override open func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()!
let width = rect.width - 10;
let height = rect.height - 10;
let center = CGPoint(x: width/2+5, y: height/2+5)
let big_r = min(width, height)/2
context.setLineWidth(1.5)
UIColor.white.setStroke()
//draw the rings
for i in stride(from: 1, to: 7, by: 1){
let r = big_r * CGFloat(i) / 6.0;
context.addArc(center:center, radius:r, startAngle: 0, endAngle: CGFloat(2*Double.pi), clockwise: false)
context.strokePath()
}
//draw the ticks
if self.ticks.count > 49 {
UIColor.red().setFill()
for (i, tick) in self.ticks.prefix(250).enumerated(){
let radius = min((100.0-CGFloat(shot.score))/30.0 * big_r, big_r);
let x = Utilities.calculateX(tick, radius)
let y = Utilities.calculateY(tick, radius)
let point = CGPoint(x: x+center.x,y: y+center.y)
context.addArc(center:point, radius:CGFloat(DOT_WIDTH/2), startAngle: 0, endAngle: CGFloat(2*Double.pi), clockwise: false)
context.fillPath()
}
}
}
}

You can make your custom view redraw by calling setNeedsDisplay() when needed. After that, you can add the label

You can set a delegation for the View class and call the delegate method at the end of the draw function. However, it seems that if you call that directly, the draw function will only end until the delegate function get processed. Which means that the code will be executed before the new drawing refresh on the screen. In that case, you may need to call it asynchronously.
// at end of draw function
DispatchQueue.main.async {
self.delegate?.drawEnd()
}

Related

Animating a AVPlayerLayer using UIPropertyAnimator [duplicate]

I have implemented a custom view with adding CALayer as sublayer for UIView. When I animate the view with the following:UIView.animateWithDuration(2.0) { self.slider.bounds.size *= 2.0}, the scaling animation is kind of wrong. The CALayer start at the wrong position with scaled size and move to the final position instead of scaling with the view.
The CustomeView Code :
import UIKit
class GridMaskView: UIView {
private let cornerLayer: CAShapeLayer
private let borderLayer: CAShapeLayer
private let gridLayer: CAShapeLayer
private let gridSize: (horizontal: UInt, vertical: UInt) = (3, 3)
private let cornerThickness: CGFloat = 3.0
private let cornerLength: CGFloat = 20.0
private let borderThickness: CGFloat = 2.0
private let gridThickness: CGFloat = 1.0
private let lineColor: UIColor = UIColor(r: 120, g: 179, b: 193, a: 1)
var showGridLines: Bool = true {
didSet {
gridLayer.hidden = !showGridLines
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
cornerLayer = CAShapeLayer()
cornerLayer.fillColor = lineColor.CGColor
borderLayer = CAShapeLayer()
borderLayer.fillColor = UIColor.clearColor().CGColor
borderLayer.strokeColor = lineColor.CGColor
borderLayer.lineWidth = borderThickness
gridLayer = CAShapeLayer()
gridLayer.strokeColor = lineColor.CGColor
gridLayer.lineWidth = gridThickness
super.init(frame: frame)
layer.addSublayer(cornerLayer)
layer.addSublayer(borderLayer)
layer.addSublayer(gridLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
layoutLayers()
}
private func layoutLayers() {
drawCorner()
drawBorder()
drawGrid()
}
private func drawCorner() {
cornerLayer.frame = bounds.insetBy(dx: -cornerThickness, dy: -cornerThickness)
cornerLayer.path = cornerPath(forBounds: cornerLayer.bounds)
}
private func cornerPath(forBounds bounds: CGRect) -> CGPathRef {
let horizontalSize = CGSize(width: cornerLength, height: cornerThickness)
let verticalSize = CGSize(width: cornerThickness, height: cornerLength)
let corners: [(CGRectEdge, CGRectEdge)] = [(.MinXEdge, .MinYEdge), (.MinXEdge, .MaxYEdge), (.MaxXEdge, .MinYEdge), (.MaxXEdge, .MaxYEdge)]
var cornerRects = [CGRect]()
for corner in corners {
cornerRects.append(bounds.align(horizontalSize, corner: corner.0, corner.1))
cornerRects.append(bounds.align(verticalSize, corner: corner.0, corner.1))
}
let cornerPath = CGPathCreateMutable()
CGPathAddRects(cornerPath, nil, cornerRects, cornerRects.count)
return cornerPath
}
private func drawBorder() {
borderLayer.frame = bounds
borderLayer.path = borderPath(forBounds: borderLayer.bounds)
}
private func borderPath(forBounds bounds: CGRect) -> CGPathRef {
let borderPath = CGPathCreateMutable()
let borderCornerPoints = [bounds.topLeft, bounds.topRight, bounds.bottomRight, bounds.bottomLeft, bounds.topLeft]
CGPathAddLines(borderPath, nil, borderCornerPoints, borderCornerPoints.count)
return borderPath
}
private func drawGrid() {
gridLayer.frame = bounds
gridLayer.path = gridPath(forBounds: gridLayer.bounds)
}
private func gridPath(forBounds bounds: CGRect) -> CGPathRef {
let stepSize = bounds.size / (CGFloat(gridSize.horizontal), CGFloat(gridSize.vertical))
let gridPath = CGPathCreateMutable()
for i in (1...gridSize.vertical) {
let x = CGFloat(i) * stepSize.width
CGPathMoveToPoint(gridPath, nil, x, 0)
CGPathAddLineToPoint(gridPath, nil, x, bounds.size.height)
}
for i in (1...gridSize.horizontal) {
let y = CGFloat(i) * stepSize.height
CGPathMoveToPoint(gridPath, nil, 0, y)
CGPathAddLineToPoint(gridPath, nil, bounds.size.width, y)
}
return gridPath
}
override func intrinsicContentSize() -> CGSize {
return CGSize(width: cornerLength * 2, height: cornerLength * 2)
}
}
Anyone know how to fit this?
The problem is that when you do view animation you don't get any automatic animation of sublayers. You'd be better off using a subview of your original UIView, because view animation will animate that together with the original view, according to its autolayout constraints.

How to make waved progress bar in swift?

I am beginner IOS developer and often see in design patterns that people use Heart Beat or Waves as the progression bar (i.e. song progress). Sometimes these progress bars go around theAlbum Art etc.
How can i achieve such thing? I am aware of UISlider combined with AVAudioPlayer but couldn't find anything to achieve such as following for song slider.
You could make a custom View, and draw the vertical lines manually. The main procedure for reference:
import UIKit
class WavedProgressView: UIView {
var lineMargin:CGFloat = 2.0
var volumes:[CGFloat] = [0.5,0.3,0.2,0.6,0.4,0.5,0.8,0.6,0.4]
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.backgroundColor = UIColor.darkGray
}
override var frame: CGRect {
didSet{
self.drawVerticalLines()
}
}
var lineWidth:CGFloat = 3.0{
didSet{
self.drawVerticalLines()
}
}
func drawVerticalLines() {
let linePath = CGMutablePath()
for i in 0..<self.volumes.count {
let height = self.frame.height * volumes[i]
let y = (self.frame.height - height) / 2.0
linePath.addRect(CGRect(x: lineMargin + (lineMargin + lineWidth) * CGFloat(i), y: y, width: lineWidth, height: height))
}
let lineLayer = CAShapeLayer()
lineLayer.path = linePath
lineLayer.lineWidth = 0.5
lineLayer.strokeColor = UIColor.white.cgColor
lineLayer.fillColor = UIColor.white.cgColor
self.layer.sublayers?.removeAll()
self.layer.addSublayer(lineLayer)
}
}
The effect is:
And please make it more perfect by yourself, like: handling event, applying default and highlighted color etc.

Custom view with CALayer wired effect when animating bounds change

I have implemented a custom view with adding CALayer as sublayer for UIView. When I animate the view with the following:UIView.animateWithDuration(2.0) { self.slider.bounds.size *= 2.0}, the scaling animation is kind of wrong. The CALayer start at the wrong position with scaled size and move to the final position instead of scaling with the view.
The CustomeView Code :
import UIKit
class GridMaskView: UIView {
private let cornerLayer: CAShapeLayer
private let borderLayer: CAShapeLayer
private let gridLayer: CAShapeLayer
private let gridSize: (horizontal: UInt, vertical: UInt) = (3, 3)
private let cornerThickness: CGFloat = 3.0
private let cornerLength: CGFloat = 20.0
private let borderThickness: CGFloat = 2.0
private let gridThickness: CGFloat = 1.0
private let lineColor: UIColor = UIColor(r: 120, g: 179, b: 193, a: 1)
var showGridLines: Bool = true {
didSet {
gridLayer.hidden = !showGridLines
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
cornerLayer = CAShapeLayer()
cornerLayer.fillColor = lineColor.CGColor
borderLayer = CAShapeLayer()
borderLayer.fillColor = UIColor.clearColor().CGColor
borderLayer.strokeColor = lineColor.CGColor
borderLayer.lineWidth = borderThickness
gridLayer = CAShapeLayer()
gridLayer.strokeColor = lineColor.CGColor
gridLayer.lineWidth = gridThickness
super.init(frame: frame)
layer.addSublayer(cornerLayer)
layer.addSublayer(borderLayer)
layer.addSublayer(gridLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
layoutLayers()
}
private func layoutLayers() {
drawCorner()
drawBorder()
drawGrid()
}
private func drawCorner() {
cornerLayer.frame = bounds.insetBy(dx: -cornerThickness, dy: -cornerThickness)
cornerLayer.path = cornerPath(forBounds: cornerLayer.bounds)
}
private func cornerPath(forBounds bounds: CGRect) -> CGPathRef {
let horizontalSize = CGSize(width: cornerLength, height: cornerThickness)
let verticalSize = CGSize(width: cornerThickness, height: cornerLength)
let corners: [(CGRectEdge, CGRectEdge)] = [(.MinXEdge, .MinYEdge), (.MinXEdge, .MaxYEdge), (.MaxXEdge, .MinYEdge), (.MaxXEdge, .MaxYEdge)]
var cornerRects = [CGRect]()
for corner in corners {
cornerRects.append(bounds.align(horizontalSize, corner: corner.0, corner.1))
cornerRects.append(bounds.align(verticalSize, corner: corner.0, corner.1))
}
let cornerPath = CGPathCreateMutable()
CGPathAddRects(cornerPath, nil, cornerRects, cornerRects.count)
return cornerPath
}
private func drawBorder() {
borderLayer.frame = bounds
borderLayer.path = borderPath(forBounds: borderLayer.bounds)
}
private func borderPath(forBounds bounds: CGRect) -> CGPathRef {
let borderPath = CGPathCreateMutable()
let borderCornerPoints = [bounds.topLeft, bounds.topRight, bounds.bottomRight, bounds.bottomLeft, bounds.topLeft]
CGPathAddLines(borderPath, nil, borderCornerPoints, borderCornerPoints.count)
return borderPath
}
private func drawGrid() {
gridLayer.frame = bounds
gridLayer.path = gridPath(forBounds: gridLayer.bounds)
}
private func gridPath(forBounds bounds: CGRect) -> CGPathRef {
let stepSize = bounds.size / (CGFloat(gridSize.horizontal), CGFloat(gridSize.vertical))
let gridPath = CGPathCreateMutable()
for i in (1...gridSize.vertical) {
let x = CGFloat(i) * stepSize.width
CGPathMoveToPoint(gridPath, nil, x, 0)
CGPathAddLineToPoint(gridPath, nil, x, bounds.size.height)
}
for i in (1...gridSize.horizontal) {
let y = CGFloat(i) * stepSize.height
CGPathMoveToPoint(gridPath, nil, 0, y)
CGPathAddLineToPoint(gridPath, nil, bounds.size.width, y)
}
return gridPath
}
override func intrinsicContentSize() -> CGSize {
return CGSize(width: cornerLength * 2, height: cornerLength * 2)
}
}
Anyone know how to fit this?
The problem is that when you do view animation you don't get any automatic animation of sublayers. You'd be better off using a subview of your original UIView, because view animation will animate that together with the original view, according to its autolayout constraints.

iOS custom view doesn't show image immediately

I made a simple custom view in Swift which has a background image view with a photo and some labels in front. I then added the custom view to my storyboard and it displays well, I can see the background image and labels.
But when I run my app on my device the image view doesn't show immediately, if I navigate to another view then back, it is displayed. I only have a timer which scheduled some tasks in background in my scene. Did I miss anything here?
Here is the code of my custom view
import UIKit
#IBDesignable class GaugeView: UIImageView {
#IBOutlet weak var speedLabel: UILabel!
let circlePathLayer = CAShapeLayer()
var circleRadius: CGFloat = 50.0
var speed: CGFloat {
get {
return circlePathLayer.strokeEnd
}
set {
speedLabel.text = String(format: "%.2f", newValue)
if newValue < 20.0 {
circlePathLayer.strokeColor = UIColor.blueColor().CGColor
} else if newValue >= 25.0 {
circlePathLayer.strokeColor = UIColor.redColor().CGColor
} else {
circlePathLayer.strokeColor = UIColor.yellowColor().CGColor
}
if (newValue > 50) {
circlePathLayer.strokeEnd = 1
} else if (newValue < 0) {
circlePathLayer.strokeEnd = 0
} else {
circlePathLayer.strokeEnd = newValue / 49.9
}
}
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
override func layoutSubviews() {
super.layoutSubviews()
circleRadius = bounds.width / 2 - 2.6
circlePathLayer.frame = bounds
circlePathLayer.path = circlePath().CGPath
}
// Our custom view from the XIB file
var view: UIView!
let nibName = "GaugeView"
func xibSetup() {
view = loadViewFromNib()
// use bounds not frame or it'll be offset
view.frame = bounds
// Make the view stretch with containing view
view.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(view)
// Configure speed circle layer
configure()
}
func configure() {
speed = 0.0
circlePathLayer.frame = bounds
circlePathLayer.lineWidth = 6
circlePathLayer.fillColor = UIColor.clearColor().CGColor
circlePathLayer.strokeColor = UIColor.blueColor().CGColor
layer.addSublayer(circlePathLayer)
backgroundColor = UIColor.whiteColor()
}
func circleFrame() -> CGRect {
var circleFrame = CGRect(x: 0, y: 0, width: 2*circleRadius, height: 2*circleRadius)
circleFrame.origin.x = CGRectGetMidX(circlePathLayer.bounds) - CGRectGetMidX(circleFrame)
circleFrame.origin.y = CGRectGetMidY(circlePathLayer.bounds) - CGRectGetMidY(circleFrame)
return circleFrame
}
func circlePath() -> UIBezierPath {
let center: CGPoint = CGPointMake(CGRectGetMidX(circlePathLayer.bounds), CGRectGetMidY(circlePathLayer.bounds))
let start = CGFloat(1.33 * M_PI_2)
let end = CGFloat( 1.64 * M_2_PI)
return UIBezierPath(arcCenter: center, radius: circleRadius, startAngle: start, endAngle: end, clockwise: true)
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
Then I added the custom view to my storyboard and navigate to the scene programatically with performSegueWithIdentifier("dashboardSegue", sender: nil). I just noticed not only the custome view, but two progress bar on the scene are not displayed as well.
It's better show your code, but i am guessing your timer stuck the main UI thread. When you navigate to another view, your timer.invalidate() was called. Try to remove the NSTimer to see if it works well.

Changing bounds of a child view centers it in its parent

I have been following a tutorial explaining a custom ios control. There is no problem with the tutorial but I got really confused about the frame/bounds clipping (not part of the tutorial).
I have a UIView instance in the scene in the storyboard. This UIView is sized at 120 x 120. I am adding a custom control (extending UIControl) to this container view with addSubview. I began to experiment with setting different widths and height to the frame and bounds of the custom control, this is the initializer of the control:
public override init(frame: CGRect) {
super.init(frame: frame)
createSublayers()
}
...and produces this result (red is the parent, blue circle is the child):
Now I change the init to this:
public override init(frame: CGRect) {
super.init(frame: frame)
createSublayers()
self.frame.size.width = 40
self.frame.size.height = 40
println("frame: \(self.frame)")
println("bounds: \(self.bounds)")
self.clipsToBounds = true
}
And that produces this result:
and prints:
frame: (0.0,0.0,40.0,40.0)
bounds: (0.0,0.0,40.0,40.0)
But when I change the initliazer to this:
public override init(frame: CGRect) {
super.init(frame: frame)
createSublayers()
self.bounds.size.width = 40
self.bounds.size.height = 40
println("frame: \(self.frame)")
println("bounds: \(self.bounds)")
self.clipsToBounds = true
}
I get this:
and prints: frame: (40.0,40.0,40.0,40.0)
bounds: (0.0,0.0,40.0,40.0)
I cannot seem to comprehend why this view is centered in its parent view when I change its bounds. What exactly is causing it? Is the 'frame' clipped always towards its center? Or is this view centered in its parent after the bounds have been modified and that causes the update of the 'frame'? Is it some property that can be changed? How could I manage to put it to top-left corner, for example (exactly in the same way as when I modify the 'frame')? Thanks a lot!
EDIT:
class ViewController: UIViewController {
#IBOutlet var knobPlaceholder: UIView!
#IBOutlet var valueLabel: UILabel!
#IBOutlet var valueSlider: UISlider!
#IBOutlet var animateSwitch: UISwitch!
var knob: Knob!
override func viewDidLoad() {
super.viewDidLoad()
knob = Knob(frame: knobPlaceholder.bounds)
knobPlaceholder.addSubview(knob)
knobPlaceholder.backgroundColor = UIColor.redColor();
}
#IBAction func sliderValueChanged(slider: UISlider) {
}
#IBAction func randomButtonTouched(button: UIButton) {
}
}
And the knob:
public class Knob: UIControl {
private let knobRenderer = KnobRenderer()
private var backingValue: Float = 0.0
/** Contains the receiver’s current value. */
public var value: Float {
get {
return backingValue
}
set {
setValue(newValue, animated: false)
}
}
/** Sets the receiver’s current value, allowing you to animate the change visually. */
public func setValue(value: Float, animated: Bool) {
if value != backingValue {
backingValue = min(maximumValue, max(minimumValue, value))
}
}
/** Contains the minimum value of the receiver. */
public var minimumValue: Float = 0.0
/** Contains the maximum value of the receiver. */
public var maximumValue: Float = 1.0
public override init(frame: CGRect) {
super.init(frame: frame)
createSublayers()
self.bounds.size.width = 40
self.bounds.size.height = 40
println("frame: \(self.frame)")
println("bounds: \(self.bounds)")
self.clipsToBounds = true
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createSublayers() {
knobRenderer.update(bounds)
knobRenderer.strokeColor = tintColor
knobRenderer.startAngle = -CGFloat(M_PI * 11.0 / 8.0);
knobRenderer.endAngle = CGFloat(M_PI * 3.0 / 8.0);
knobRenderer.pointerAngle = knobRenderer.startAngle;
knobRenderer.lineWidth = 2.0
knobRenderer.pointerLength = 6.0
layer.addSublayer(knobRenderer.trackLayer)
layer.addSublayer(knobRenderer.pointerLayer)
}
}
private class KnobRenderer {
let trackLayer = CAShapeLayer()
let pointerLayer = CAShapeLayer()
var strokeColor: UIColor {
get {
return UIColor(CGColor: trackLayer.strokeColor)
}
set(strokeColor) {
trackLayer.strokeColor = strokeColor.CGColor
pointerLayer.strokeColor = strokeColor.CGColor
}
}
var lineWidth: CGFloat = 1.0 {
didSet {
update();
}
}
var startAngle: CGFloat = 0.0 {
didSet {
update();
}
}
var endAngle: CGFloat = 0.0 {
didSet {
update()
}
}
var backingPointerAngle: CGFloat = 0.0
var pointerAngle: CGFloat {
get { return backingPointerAngle }
set { setPointerAngle(newValue, animated: false) }
}
func setPointerAngle(pointerAngle: CGFloat, animated: Bool) {
backingPointerAngle = pointerAngle
}
var pointerLength: CGFloat = 0.0 {
didSet {
update()
}
}
init() {
trackLayer.fillColor = UIColor.clearColor().CGColor
pointerLayer.fillColor = UIColor.clearColor().CGColor
}
func updateTrackLayerPath() {
let arcCenter = CGPoint(x: trackLayer.bounds.width / 2.0, y: trackLayer.bounds.height / 2.0)
let offset = max(pointerLength, trackLayer.lineWidth / 2.0)
let radius = min(trackLayer.bounds.height, trackLayer.bounds.width) / 2.0 - offset;
trackLayer.path = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true).CGPath
}
func updatePointerLayerPath() {
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: pointerLayer.bounds.width - pointerLength - pointerLayer.lineWidth / 2.0, y: pointerLayer.bounds.height / 2.0))
path.addLineToPoint(CGPoint(x: pointerLayer.bounds.width, y: pointerLayer.bounds.height / 2.0))
pointerLayer.path = path.CGPath
}
func update(bounds: CGRect) {
let position = CGPoint(x: bounds.width / 2.0, y: bounds.height / 2.0)
trackLayer.bounds = bounds
trackLayer.position = position
pointerLayer.bounds = bounds
pointerLayer.position = position
update()
}
func update() {
trackLayer.lineWidth = lineWidth
pointerLayer.lineWidth = lineWidth
updateTrackLayerPath()
updatePointerLayerPath()
}
}
In your update function you are centering the view.
func update(bounds: CGRect) {
**let position = CGPoint(x: bounds.width / 2.0, y: bounds.height / 2.0)**
trackLayer.bounds = bounds
trackLayer.position = position
pointerLayer.bounds = bounds
pointerLayer.position = position
update()
}
If you take that out, then you're view won't be centered anymore. The reason setting the frame would leave it in the upper left hand corner while setting the bounds resulted in it being in the center is because setting the bounds x/y does not override the frame x/y. When you set the frame then later on your code only sets the bounds, so the frame x/y is never overwritten so the view stays in the upper left hand corner. However, in the second there is no x/y set for the frame, so I guess it's taken what you set for the bounds, so it get's centered.
I would recommend not setting the bounds x/y for the view as that should always be 0, 0. If you want to reposition it then use the frame. Remember the frame is relative the parent while the bounds is relative to it's self.

Resources