I am subclassing an UIView that is being loaded from a xib file. When I load the xib into the Viewcontroller using NSBundle.mainBundle().loadNibName() I want the UIView's width, height and background color to be set. This is what I have in the initializer, but when I run the simulator the UIView does not have a width, height or background color set.
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.backgroundColor = UIColor.blueColor()
self.bounds = CGRect(x: 0, y: 0, width: 150, height: 150)
}
Override awakeFromNib instead. See this question for a broader explanation.
Related
I have a UITableViewCell in which I add my custom view, say
class MyView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
convenience required init(size: CGFloat) {
self.init(frame: CGRect.zero)
height = size
// init my other sub contents [1]
}
}
From my UITableViewCell:
let myView = MyView(size: 35)
contentView.addSubView(myView)
My problem: my other UI components in MyView depend on the width and height of MyView and it's too early to set their position in [1] because MyView's frame is now (x: 0, y: 0, width: 0, height: height)
Where should I put my other UI components in MyView? Is there a delegate in UIView that tells me: "Hey, the frame of the view is updated with its actual size."? (like viewDidAppear in a UIViewController)
Regards,
You can do
override var frame: NSRect {
didSet {
//update your subviews
}
}
In your MyView class. But really, you should just look into setting up proper constraints:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/
I have made a custom subclass of UIView in my iOS application, and I am trying to get the computed sizes of the view in the view's init method, so I can use them when creating subviews to put inside the custom view.
The custom view is inside a stack view, which assigns my view 1/3 of the total (main view) height.
My init looks like this:
var mySubView: UIImageView
required init?(coder aDecoder: NSCoder) {
mySubView = UIImageView()
super.init(coder: aDecoder)
let viewWidth = Int(self.frame.size.width)
let viewHeight = Int(self.frame.size.height)
mySubView.frame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight)
mySubView.backgroundColor = UIColor.cyan
self.addSubview(mySubView)
}
However, the heights and widths are not reported correctly. For instance, mySubView above only ends up filling about half of the total space of the custom view.
Any help would be greatly appreciated!
The initializer is called too early in the lifecycle of the view to accurately do layout unless you know the exact dimensions in advance. Even so, it is idiomatically the wrong place to do it.
Try using the layoutSubviews method as such:
class SubView: UIImageView {
var mySubView: UIImageView
required init?(coder aDecoder: NSCoder) {
mySubView = UIImageView()
mySubView.backgroundColor = UIColor.cyan
super.init(coder: aDecoder)
self.addSubview(mySubView)
}
override func layoutSubviews() {
mySubView.frame = self.bounds
super.layoutSubviews()
}
}
Now the subview bounds will be set properly at the start of each layout pass. It’s a cheap operation.
Also, the bounds property of a UIView is the frame translated to the view’s internal coordinate space. This means that normally this is true: bounds = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height). I suggest reading the documentation on view layout.
Alternatively, you can ditch manual layout entirely and use AutoLayout to do this for you.
class SubView: UIImageView {
var mySubView: UIImageView
required init?(coder aDecoder: NSCoder) {
mySubView = UIImageView()
mySubView.backgroundColor = UIColor.cyan
super.init(coder: aDecoder)
self.addSubview(mySubView)
mySubView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
mySubView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
mySubView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
mySubView.heightAnchor.constraint(equalTo: heightAnchor).isActive = true
}
}
Hi I am trying to create a custom loading bar view by subclassing UIView. I want to create one UIView with a fixed frame, and another UIView that is inside of it. When I initialize the inner UIView, with the frame passed in by this method override init(frame: CGRect), the two views have different origins. I want the two views to be directly on top of each other to start out. I also want to be able to update the innerBar by calling this uploadBar.setLoadingPercentage(percent: 53.5)
Here is the code:
Creating the UploadBar
let uploadBar = UploadBar(frame: CGRect(x: 40, y: 40, width: 400, height: 40))
view.addSubview(uploadBar)
Subclassing UploadBar
import UIKit
class UploadBar: UIView {
var innerBar: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.white
innerBar = UIView(frame: frame)
innerBar.backgroundColor = UI.customBlue()
addSubview(innerBar)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setLoadingPercentage(percent: Double) {
// change innerBar's frame and redraw
}
}
For your inner view, you only require the width and height from the parent rect. The x and y should be zero relative to the parent view:
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.white
let innerRect = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
innerBar = UIView(frame: innerRect)
innerBar.backgroundColor = UI.customBlue()
addSubview(innerBar)
}
I would like to know if its possible to create something like the image below.
I guess I have to create a custom view but any clue on how to do it?
Thanks
If you have a UITableView on the screen, you can achieve this by creating a custom table header view:
Create a subclass of UIView -> CustomTableHeaderView
Add a button inside it:
class CustomTableHeaderView: UIView {
var rightButton: UIButton
override init(frame: CGRect) {
rightButton = UIButton()
let buttonWidth: CGFloat = 100
// this is hardcoded for cimplicity, it's better to setup auto layout constraints here
rightButton.frame = CGRect(x: 0, y: frame.width - buttonWidth, width: buttonWidth, height: 50)
rightButton.setTitle("My button", forState: .Normal)
// other configuration
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In your view controller (or any other place where you initialise a table view, set your custom view as a tableHeaderView:
tableView.tableHeaderView = CustomTableHeaderView(frame: frame:CGRect(x: 0, y: 0, width: view.frame.width, height: 10))
I'm trying to understand how layer-backed NSViews work. I have created the following code inside a playground.
import Cocoa
import XCPlayground
class MyView: NSView {
override init(frame: NSRect) {
super.init(frame: frame)
wantsLayer = true //if commented change centerX & centerY accordingly
layerContentsRedrawPolicy = .OnSetNeedsDisplay
layer!.backgroundColor = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 0.5)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
let containerView: NSView = NSView(frame: CGRectMake(0, 0, 150, 165))
let myView: MyView = MyView(frame: containerView.frame)
let label: NSTextField = NSTextField(frame: NSRect(x: 0.0, y: 0.0, width: 100, height: 22))
label.stringValue = "Please wait..."
label.drawsBackground = false
label.bordered = false
label.selectable = false
label.textColor = NSColor.whiteColor()
//containerView.addSubview(myView)
containerView.addSubview(myView)
containerView.addSubview(label, positioned: .Above, relativeTo: myView)
XCPShowView("view", containerView)
Essentially I've created a layer-backed NSView subclass. Then I'm adding this subclass into the non layer-backed containerView. I'm also adding an NSTextField into containerView, by explicitly positioning it above the layer-backed NSView subclass.
My aim is to have the NSTextField show above the layer-backed view. However, in the assistant editor the NSTextField view appears below the layer-backed view. Could someone please help understand why this happens?
After battling with the issue for quite a bit I was able to find out that I had to set
label.wantsLayer = true
label.layerContentsUpdatePolicy = .OnSetNeedsDisplay
for the z-index to be correct. I'm still not sure as to why this is the case, since label isn't part of the MyView view hierarchy but rather part of the broader containerView one, which isn't layer-backed.