Swift Programming UIButtons - swift

Im relatively new to the swift language and I am having a difficult time with creating a button. I have created a new file as a UIButton, so how would I change the button type using "self"?
for example..
import UIKit
class LoginButton: UIButton {
override init(frame: CGRect){
super.init(frame: frame)
self.buttonWithType(UIButtonType.System) as UIButton
// Above is where I need the button type as "System"
self.enabled = true
self.setTitle("Log in", forState: UIControlState.Normal)
self.addTarget(self, action: "logInPressed", forControlEvents: UIControlEvents.TouchUpInside)
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func logInPressed(){
println("log in")
}
}

Do you really need to subclass UIButton? In most use cases, one uses UIButton directly and does not subclass it.
The buttonWithType() method you reference is a class method (not an instance method). You call it as UIButton.buttonWithType(type) which returns an instance of a UIButton so configured. You can't change the type after you instantiate the button; however, several methods are available for modifying the look of the button after you instantiate.
To add a brown border of size 2.0 to your button, you could add the following code:
myButton.layer.borderColor = UIColor.brownColor().CGColor
myButton.layer.borderWidth = 2.0

Related

Where to ctrl-drag IBOutlets, view class or ViewController?

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.

Property Initialization with closures

I was looking into ARC and strong reference cycles and ran into this code of mine:
class TestClass: UIView {
let button: UIButton = {
let view = UIButton()
view.frame = CGRect(x: 50, y: 50, width: 200, height: 200)
view.backgroundColor = .blue
view.translatesAutoresizingMaskIntoConstraints = false
view.setTitle("Button", for: .normal)
view.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
return view
}()
#objc private func buttonClicked() {
print("Clicked")
}
override init(frame: CGRect) {
super.init(frame: frame)
print("Object of TestClass initialized")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
print("Object of TestClass deinitialized")
}
}
reference to self in the addTarget method inside the closure doesn't seem to create a strong reference cycle.
Can someone explain why?
Also, I noticed that if I remove inheritance from UIView the compiler starts complaining: Use of unresolved identifier 'self'.
Can someone explain this as well, why does it happen in this case and doesn't in the first one?
This is not a retain cycle because self is not what you think it is :)
Properties with initial value are "executed" even before any initializer runs, and for those properties self points to a higher order function of this type:
(TestClass) -> () -> TestClass
So you don't really access the instance, but rather you access a static-like method that does the initialization of all properties that have a default value. This is why you don't have a retain cycle.
addTarget accepts an Any? value for it's first argument, so this violates no type rules so the compiler doesn't complain that you don't pass a NSObject instance there.
Checking the later behaviour - e.g. what happens if the button is added to the UI hierarchy and is tapped, reveals something interesting: the runtime sees that you passed a non-object as a target and sets null values for target and action:

Custom View (XIB) and IBAction

I created a custom view (xib). This view is nothing more than an elaborate button (e.g. contains a label and icon). I want to handle click events exactly like the standard UIButton. That is, control-drag the button to the IBAction function that'll be called when button is clicked. From the storyboard, I want to be able to control-drag my custom UIView to the IBAction function that will handle the event someone taps my view. I don't want use UITapGestureRecognizer if I don't have to.
Just include a UIButton in your custom view. You can customize the button so that it has no words or design, and then you can place your label and icon, etc. on top of that button. Then you will be able to ctrl-drag from your button to an IBAction function as you want
Create a custom class for that Xib and then you use
class customButton: UIButton {
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
override init(frame: CGRect, style: UITableViewStyle) {
super.init(frame: frame, style: style)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
//Setup texts and images
}
}
Now you can use this class as customClass in your Xib and in turn use it as a customClass on the button you wish to use it on. Next, just drag an #IBAction as you wanted to. And you can reuse this button with other texts and images for other classes if you create an configure function inside that you can call whenever you want :).

UILabel subclass appearance in Storyboard

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.

swift IBDesignable view not show in storyboard

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