I have created a subclass of UILabel called MyUILabel. The only thing changed is the font and font-size. It appears as expected when I run the app. However, the in the Storyboard, the default UILabel is showed. Is there any way to make Storyboards show the font and font-size from my subclass?
MyUILabel:
public class MyUILabel : UILabel {
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.font = UIFont(name: Constants.DefaultFont, size: 30)
}
}
You could make it #IBDesignable, and then implement prepareForInterfaceBuilder:
#IBDesignable
public class MyUILabel: UILabel {
public override func awakeFromNib() {
super.awakeFromNib()
configureLabel()
}
public override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
configureLabel()
}
func configureLabel() {
font = UIFont(name: Constants.DefaultFont, size: 40)
}
}
Note, IB didn't like it when I implemented init(coder:), so I moved it into awakeFromNib.
Also note that when you make an #IBDesignable class, Apple advises that you create a separate target (e.g. "File" - "New" - "Target..." - "Cocoa Touch Framework") for this designable class. For more information, see WWDC 2014 video What’s New in Interface Builder.
Related
I'm pretty new to coding. Im not sure if an IBOutlet (button, text field, etc) ctrl-dragged from a xib should go in the xib's NSView class or in the view controller which has the NSView added as a subview.
I've been playing around with this for a while, learning as I go. I'm stuck on wondering if I have the code structured correctly. This is for MacOS so resources are limited and often dated. I'd assume that an outlet added for a button, for example, would go in the controller as views should be "dumb". If I try that the actions always have "action" set automatically and type as Any as a default - not what I'm used to seeing. I suspect this may have something to do with the class set for the file's owner and the class set for the view in IB. If anyone can outline the best way to handle this that would be fantastic, thank you!
The view that loads the xib:
class View4: NSView {
#IBOutlet weak var view: View4!
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
Bundle.main.loadNibNamed("View4", owner: self, topLevelObjects: nil)
self.frame = self.bounds
self.wantsLayer = true
self.translatesAutoresizingMaskIntoConstraints = false
self.layer?.backgroundColor = NSColor.purple.cgColor
self.roundedCorners(on: self)
// add xib to custom NSView subclass
self.addSubview(self.view)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
// Drawing code here.
}
}
The corresponding ViewController:
class View4Controller: NSViewController {
override func loadView() {
print("View4Controller.loadView")
self.view = NSView()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
print("View4Controller.viewDidLoad")
self.view = View4()
}
}
The idea of an outlet is to have a reference to an object that is outside of your code created. The concept is great for prototyping, but tends to become hard to manage as a project grow.
If you class is the class, then it can refer to itself. („self“ in swift or „this“ in c++) You don't need an outlet in this case.
The outlet is normally used by controller that need to maintain the view. The concept is a alternative to creating and configuring the view manually.
I am setting some properties in the draw method of my UIImageView. However, these do not seem to be taking any affect at all. I see no rounded corners and no masking taking affect. The view is below:
//
// RoundImage.swift
//
//
import UIKit
class RoundImage: UIImageView {
//------------------
//MARK: - Setup and Initialization
//------------------
override init(frame: CGRect) {
super.init(frame: frame)
self.initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.initialize()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
self.layer.cornerRadius = 30
self.layer.masksToBounds = true
self.clipsToBounds = true
}
//Setups content, styles, and defaults for the view
private func initialize(){
self.initStyle()
}
//Sets static content for the view
private func staticContent() {
}
//Styles the view's colors, borders, etc at initialization
private func initStyle(){
}
//Styles the view for variables that must be set at runtime
private func runtimeStyle(){
//TODO: These values cannot be changed in interface builder, but they should be able to be
}
//------------------
//MARK: - Interface Builder Methods
//------------------
//Sets the view up for interface builder with runtime styling and temp display values
override func prepareForInterfaceBuilder() {
self.runtimeStyle()
self.staticContent()
}
//------------------
//MARK: - Lifecycle Methods
//------------------
//Sets the view up with runtime styling and values
override func awakeFromNib() {
super.awakeFromNib()
self.runtimeStyle()
self.staticContent()
}
}
UIImageView when subclassed does not call the draw method at all. It is not allowed, although minimally documented. In this case, it is recommended to subclass UIView and then draw the image on the view in your draw method yourself.
For further information:
drawRect not being called in my subclass of UIImageView
I'm trying to make a set of UILabels bold, and figured it could be done with something like a CSS class. The only way I thought of though is subclassing UILabel and adding it as a Custom Class to each label:
import UIKit
class BoldLabel: UILabel {
override internal var font: UIFont? {
get {
return UIFont.boldSystemFontOfSize(16.0)
}
}
}
But this gives an error "Getter for 'font' with Objective-C selector 'font' conflicts with getter for 'font' from superclass 'UILabel' with the same Objective-C selector". So is there a way to do it like this, or a different way to easily make a bunch of labels' fonts bold?
import UIKit
class BoldLabel: UILabel {
//This method is call when you affect this class to a UILabel in your STORYBOARD
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
self.setup()
}
//This method is call when you programatically create an instance of this UILabel class
override init(frame:CGRect) {
super.init(frame:frame)
self.setup()
}
func setup() {
//All custom properties of this label class here
self.font = UIFont.boldSystemFontOfSize(28)
}
}
Results with UILabels from storyboard :
I'm trying to create a reusable UIView in Swift that I can plug into my Storyboard view controllers. My key issue right now is that the reusable UIView "widget" doesn't fully fit into the UIView box in the storyboard. I followed this tutorial to set up the reusable UIView widget
Created a subclass of UIView and a corresponding .xib -- and connected these:
import UIKit
class MyWidgetView: UIView {
#IBOutlet var view: UIView!;
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder);
NSBundle.mainBundle().loadNibNamed("MyWidgetView", owner: self, options: nil);
self.addSubview(self.view);
}
}
In the XIB, which is the interface file corresponding to the code above, I used UIView with Freeform size under the Simulated Metrics, and Scale to Fill under View mode.
In the main storyboard, I added a UIView block (same rectangular shape) and changed the Class to MyWidgetView
It works, but the components I created in the XIB look squished in the actual app, despite the fact that I used layout constraints in both the XIB and also the main storyboard.
See the screenshot. The pink part isn't supposed to appear, since that is just a color of the UIVIew on the main storyboard that I added to test the sizing. That UIView is actually MyWidgetView (after I changed the class in step 3. So in theory, since MyWidgetView == the UIView on the main storyboard, and that UIView has constraints that make it rectangular in the superview, then why is my widget squished? The blue part below should extend all the way right.
The actual view hierarchy loaded from the nib file in your code is added via
self.addSubview(self.view). So, the frame of your self.view actually has no relationship with its parent, i.e. MyWidgetView.
You may choose either adding layout constraints through code or just setting its frame after being added as a subview. Personally, I prefer the latter. In my experiment, the following is what works for me. I am using Xcode 6.4, which I think is not the same one as yours.
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
if let nibsView = NSBundle.mainBundle().loadNibNamed("MyWidgetView", owner: self, options: nil) as? [UIView] {
let nibRoot = nibsView[0]
self.addSubview(nibRoot)
nibRoot.frame = self.bounds
}
}
Alternatively the variable frame can be overridden. This code worked for me when CardImgText was set to files owner for the view.
class CardImgTxt: NSView {
#IBOutlet var view: NSView!
override var frame: NSRect{
didSet{
view.frame = bounds
}
}
override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
// Drawing code here.
}
required init?(coder: NSCoder) {
super.init(coder: coder)
NSBundle.mainBundle().loadNibNamed("View", owner: self, topLevelObjects: nil)
addSubview(view)
}
}
if you are more interested in efficiency than real time updating. Then replace :
override var frame: NSRect{
didSet{
view.frame = bounds
}
}
with:
override func viewDidEndLiveResize() {
view.frame = bounds
}
I want to custom a 5-star UIView,also I want it to be render in storyboard. So I decide to use #IBDesignable and #IBInspectable.The following is my code.
import UIKit
#IBDesignable
class RatingView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUpView()
}
func setUpView() {
let imageView = UIImageView(frame:CGRectMake(0, 0, 50,50))
imageView.image = UIImage(named: "star")
addSubview(imageView)
}
}
And then in my storyboard , I pull a UIView into my canvas,and set Custom class to my custom view as RatingView.The compiler starts to compile storyboard file and I just wait for the custom view to be renderd in canvas.Here is the screenshot.
The state is "up to date",but the view has not been renderd.The view is just staying white,what I want to see is the image I add to the parent view.
When I use UILabel instead of UIImageView, the label is renderd in the canvas but not the UIImageView,how can I render my lovely star image in my canvas.(Images.xcassets has star.png file)
use UILabel instead of UIImageView
func setUpView() {
let label = UILabel(frame: CGRectMake(0, 0, 50, 50))
label.text = "text"
addSubview(label)
}
result:
I was trying to do the same exact thing. You need to put the view in a framework. As #Benson Tommy said in the comments take a look at WWDC 2014 session 411.
Here is a link to the session:https://developer.apple.com/videos/wwdc/2014/#411
Here is a link to the transcript of the session: http://asciiwwdc.com/2014/sessions/411