Swift: Update UIViewController insets via extension - swift

I wanted to update the UIEdgeInsets of all my view controllers on certain devices and I wanted to see if there's a way to do it globally as an extension, rather than creating a method and calling it in viewDidLoad for each of them. Is there a way to achieve this? I tried using awakeFromNib but this doesn't work.
extension UIViewController {
open override func awakeFromNib() {
self.additionalSafeAreaInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)
}
}
I also tried calling self.viewLayoutMarginsDidChange() after changing the edge insets, with no results.
Any suggestions would be appreciated. Am I just overriding the wrong method or is this just not possible, or as easy as I'm thinking.

extension UIViewController {
open override func awakeAfter(using: NSCoder) -> Any? {
self.additionalSafeAreaInsets = UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100)
return super.awakeAfter(using: using)
}
}

Related

How do you enlarge the search icon on an NSSearchField?

I'm making a search field with a larger height than the default. Although its text size can be increased to the proper size, the icon stays the same size:
I tried to fix this by overriding NSSearchField's rectForSearchButton(whenCentered:)'s default rectangle. However, not only did it not increase the size of the button, but it also duplicated the button image.
override func rectForSearchButton(whenCentered isCentered: Bool) -> NSRect {
return NSRect(x: 10, y: 10, width: 30, height: 30)
}
I then tried commenting out the above and subclassing NSSearchFieldCell, which had a similar result.
override func searchButtonRect(forBounds rect: NSRect) -> NSRect {
return NSRect(x: 10, y: 10, width: 30, height: 30)
}
How can I properly center and enlarge this icon? I want it to fill up the whole space and be centered, or at least look like a normal search field.
You need to change button image scaling. Assuming your custom class is instantiated from Storyboard/XIB and the height is modified via constraint, it could be like below:
class CustomSearchFiled: NSSearchField {
override func awakeFromNib() {
if let cell = self.cell as? NSSearchFieldCell {
cell.searchButtonCell?.imageScaling = .scaleProportionallyUpOrDown
}
}
...

How to adjust vertically single line string in NSTextField?

I am using custom font for non-editable NSTextField, which I created in StoryBoard:
generalDisplay.font = NSFont(name: "DS-Digital Bold", size: 25.0)
Then I am adjusting frame height:
generalDisplay.frame.size.height = 28
The result is not centered vertically:
I've tried to turn off single line mode, but the result is even worse.
If I should subclass it, could you give me an example what methods I have to override?
You can subclass UITextField and override this method:
override func textRect(forBounds bounds: CGRect) -> CGRect {
return bounds.inset(by: UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 10))
}
Looking at the Apple documentation for NSTextfield maybe you could initialize your NSTextField with a NSAttributedString to be able to change the baseline
NSTextfield(labelWithAttributedString: NSAttributedString)

UIEdgeInset custom initializer in extension throwing EXC_BAD_ACCESS

I have this extension:
extension UIEdgeInsets {
init(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
self.init(top: top, left: left, bottom: bottom, right: right)
}
}
The purpose of the extension is to not have to pass parameters to the UIEdgeInsets initializer when they're not different from the default values. But when the custom initializer runs, an error is thrown:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x16f997ff8)
Any idea why this code would cause a runtime error?
You are causing infinite recursion since you are calling the same init method from within itself.
And an extension really shouldn't try to override an existing init or other method simply by providing default parameters.
There is already an init(top:,left:,bottom:,right:). Providing another with the same signature is a bad idea.
But that aside, your primary issue is calling self.init from within the same init method.
To see the problem, add a print statement:
init(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
print("Uh-oh")
self.init(top: top, left: left, bottom: bottom, right: right)
}
As another example, create a class with an init that takes one parameter. Now add the same init again to the class but provide a default to the parameter. The code won't even compile. So trying to do the same through an extension is just going to lead to problems, even if you could solve the recursion problem.

Why doesn't overriding `intrinsicContentSize` in `NSView` work for `NSScrollView`?

I have placed an instance of custom class BigView that is a subclass of NSView inside a NSScrollView in IB. The content size of my BigView will be computed at runtime. What is the best practices for setting the content size?
Overriding intrinsicContentSize, as suggested in the The Big Nerd Ranch guide, does not seem to work -- the frame remains at its original size:
class BigView: NSView {
...
override var intrinsicContentSize: NSSize { // doesn't work?!
return NSSize(width: 10000, height: 10000)
}
...
}
Setting the frame programmatically (or in IB) does work:
class BigView: NSView {
...
override func awakeFromNib() {
...
self.frame = NSRect(x: 0, y: 0, width: 10000, height: 10000)
}
...
}I
or from the controller:
class ViewController: NSViewController {
#IBOutlet weak var bigView: BigView!
...
override func viewDidLoad() {
super.viewDidLoad()
bigView.frame = NSRect(x: 0, y: 0, width: 1000, height: 1000)
}
...
}
This can also be done throughout the scrollview's documentView property:
class ViewController: NSViewController {
#IBOutlet weak var scrollView: NSScrollView!
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.documentView?.frame = NSRect(x: 0, y: 0,
width: 1000, height: 1000)
}
...
}
I suppose one could also use AutoLayout constraints as well.
What doesn't overriding intrinsicContentSize work?
The problem is that when translatesAutoresizingMaskIntoConstraints is enabled in newer versions of AppKit default constraints are set without considering the intrinsic size.
To fix this you need to add the constraints manually. First pin the position of your BigView to the top left of the superview (Should be the NSClipView). Then go to the Size Inspector, and in the Content Compression Resistance Priority section set Intrinsic Size to Placeholder. That gives your view the constraints it needs and everything should work at runtime.

NSButtion action not called when target is different object

I've got an NSButton in a View Controller that, when clicked, should call a method in an instance of another class (I have that instance in the View Controller). However, the action method is never called.
My code is below (it's short and simple). Please can somebody explain why this is?
View Controller class with the button:
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let b:NSButton = NSButton(frame: NSRect(x: 150, y: 200, width: 30, height: 30))
self.view.addSubview(b)
let g = Global()
b.target = g
b.action = #selector(g.s)
}
}
Class called 'Global' that I create an instance of, that the button should then call a method within:
class Global:NSObject {
override init() {
super.init()
}
#objc dynamic func s() {
Swift.print("S ran")
}
}
Thanks
Update: For easy reproduction, I've created a GitHub repo showing the issue in its simplest form here.
The problem is that by the time you click the button, target has been set to nil. This is because g is stored as a local variable and target is a weak property, so after viewDidLoad is finished, g is released and the target becomes nil. So, by the time you click the button, there is no object on which to call the action.
You need to store a strong reference to the target somewhere. One way would be to store it as an instance variable on your view controller:
class ViewController: NSViewController {
let g = Global()
override func viewDidLoad() {
super.viewDidLoad()
let b:NSButton = NSButton(frame: NSRect(x: 150, y: 200, width: 30, height: 30))
self.view.addSubview(b)
b.target = g
b.action = #selector(g.s)
}
}