How to disambiguate global and local methods in Swift? - swift

I want to use kind of SwiftUI-like functional style of configuring the UI, for example by providing [a global] method hide(view: UIView) instead of writing view.isHidden = true.
Now this works fine but for reverse logic I want to have the global method show() but within context of an UIViewController it conflicts with the class method show(sender: Any).
Is there any language trick I can you in order not to have to write MyAppName.show() each time I use it?

Your title is a bit inaccurate. You already know how to disambiguate it. The question is about how to do so without repeating the module name. Unfortunately, I don't think there's any such way.

I found out that Swift compiler seems to be overreacting, as one method has 2 arguments and my method has 1 argument. What's the reason to write "nearly matches" and breaking compilation is unclear to me.
However, I found the solution:
extension UIViewController{
func show(_ view: UIView) { view.isHidden = false }
}

Related

Is there a reliable way of overriding CellClass in classes derived from NSControl?

This question seems to have been asked several times, but I have yet to find an definitive answer. I'm totally aware of the fact that NSCell is "being gradually phased out". This is great (and long overdue!), but it doesn't help in the here and now. Suppose I want to create a subclass of NSImageView which (e.g.) clips an image with an arbitraily shaped mask. I might define it like this:
class MaskedImageView : NSImageView {
public override class var cellClass: AnyClass? {
get { MaskedImageCell.self }
set {}
}
MaskedImageCell, of course, inherits from NSImageCell. This code compiles just fine under Swift 5.x, but the cell class is never changed. Adding the statement super.cellClass = newValue to the above property setter likewise makes no difference. (In fact, this isn't how the underlying AppKit machinery establishes the cell class. If I add a print statement inside the property getter, I can see that AppKit is interrogating the cell class, as I'd expect, but it never uses it.)
Like I said, I'm aware that NSCell is on the way out. But there are surely multiple use cases where it would be useful to change the cellclass to add specifics required by a particular application. If this isn't possible NOW, then is one supposed to try and replicate the entire functionality of (eg) NSImageCell. I surely wouldn't want to do that!
All constructive comments gratefully received. And please do bear in mind that I'm not actually interested in adding weird-shaped masks to NSImageViews. 😄 What I'm after is a definitive solution to the problem of introducing one's own cell class into the mix. We were told 7 years ago that NSCell was being deprecated, but while it's still with us, we should have a mechanism for introducing custom cell classes.

Why does Swift 5 optional chaining for safeAreaInserts require two question marks in window??.safeAreaInsets instead of one question mark? [duplicate]

When accessing UIapplication's main window it is returned as a UIWindow??
let view = UIApplication.sharedApplication().delegate?.window // view:UIWindow??
Why is it returning as a double optional and what does it mean and if put into a if let should I add one ! after it?
if let view = UIApplication.sharedApplication().delegate?.window!
My first though was to replace ? with a ! after delegate but that was not the solution.
#matt has the details, but there is a (somewhat horrible, somewhat awesome) workaround. (See edit below, though)
let window = app.delegate?.window??.`self`()
I will leave the understanding of this line of code as an exercise for the reader.
OK, I lie, let's break it down.
app.delegate?.window
OK, so far so good. At this point we have the UIWindow?? that is giving us a headache (and I believe is a bug in Swift disconnect between Swift and Cocoa). We want to collapse it twice. We can do that with optional chaining (?.), but that unwraps and rewraps, so we're back where we started from. You can double-optional-chain, though, with ??. which is bizarre, but works.
That's great, but ?? isn't a legal suffix operator. You have to actually chain to something. Well, we want to chain back to itself (i.e. "identity"). The NSObject protocol gives us an identity method: self.
self is a method on NSObject, but it's also a reserved word in Swift, so the syntax for it is `self`()
And so we get our madness above. Do with it as you will.
Note that since ??. works, you don't technically need this. You can just accept that view is UIWindow?? and use ??. on it like view??.frame. It's a little noisy, but probably doesn't create any real problems for the few places it should be needed.
(*) I used to think of this as a bug in Swift, but it's not fixable directly by optional chaining. The problem is that there is no optional chaining past window. So I'm not sure where the right place to fix it is. Swift could allow a postfix-? to mean "flatten" without requiring chaining, but that feels odd. I guess the right operator would be interrobang delegate?.window‽ :D I'm sure that wouldn't cause any confusion.
EDIT:
Joseph Lord pointed out the better solution (which is very similar to techniques I've been using to avoid trivial if-let, but hadn't thought of this way before):
let window = app.delegate?.window ?? nil // UIWindow?
I agree with him that this is the right answer.
It's because the window property is itself in doubt (it's optional). Thus, you need one question mark because there might or might not be a window property, and another question mark because the return value of that window property is itself an Optional. Thus we get a double-wrapped Optional (as I explain in my tutorial: scroll down to the Tip box where I talk about what happens when an optional property has an Optional value).
Thus, one way to express this would be in two stages — one to cast (and unwrap that Optional), and one to fetch the window (and unwrap that Optional):
if let del = UIApplication.sharedApplication().delegate as? AppDelegate {
if let view = del.window {
Now view is a UIWindow.
Of course, if you're sure of your ground (which you probably are), you can force the cast in the first line and the unwrapping in the second line. So, in Swift 1.2:
let del = UIApplication.sharedApplication().delegate as! AppDelegate
let view = del.window!
Oh the double optional! Sometimes you can use a double-bang (two exclamation marks) but you cannot cast that way with optional binding. So... my remix of all the other code gets you a UIWindow object called window of the non-optional kind:
guard let w = UIApplication.shared.delegate?.window, let window = w else { return }
But let's not waste time and just use
let window = UIApplication.shared.delegate!.window!!
and be done.
With advent of Swift2 for me a usual workaround in this kind of cases is
if let _window = UIApplication.sharedApplication().delegate?.window, window = _window {
// Some code... i.e.
let frame = window.frame
}

Swift: Referencing variables in one class from another function and/or class

trying a few things out in Swift. I’m trying to get some things that seem muddled to me straightened out - mostly to do with how I deal with variables and referencing them in a project.
What I am trying to do is keep a variable (based on a struct) defined in ViewController accessed and updated from various other functions within an application.
So, a brief outline of the code I have is here. I actually wrote a smaller app to test my ideas out before applying them to something more complex.
I started in XCode with a Swift document based application for Mac OSX.
In ViewController.swift I have:
import Cocoa
class ViewController: NSViewController {
var myText = "Hello, some text"
#IBOutlet weak var textView1: NSTextField!
#IBAction func Button1(_ sender: Any) {
myText = "This is button 1 clicked"
myText = setText( thisText: &myText )
textView1.stringValue = myText
}
#IBAction func Button2(_ sender: Any) {
print("Button 2")
myText = "This is button 2 clicked"
textView1.stringValue = myText
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
textView1.stringValue = myText
}
override func viewDidAppear() {
let document = self.view.window?.windowController?.document as! Document
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
In SetText.swift, I have this:
import Foundation
func setText( thisText: inout String) -> String {
thisText = "Function"
return thisText
}
I liked the idea of sending variables to the Set Text function and then returning it, but thinking about it more makes me think that actually this could end up like a proverbial bowl of spaghetti with functions calling functions and who know what else. So I was thinking that something like this would probably make more sense:
import Foundation
func setText( thisText: inout String) {
let vc = ViewController()
// Read the variable from View Controller
var myTextHere = vc.myText
myTextHere = myTextHere + " More text"
// Set the variable in ViewController here
vc.myText = myTextHere
}
From my reading around on this subject, if I call ViewController(), it will create anew instance of the view (is that right, or am I misreading?). That’s already happened, so what I need is to reference the ViewController that called the function setText, or rather that owns that particular instance of code. As I’m thinking about a document based application, I’d obviously want to keep all instances of myText with each document’s ViewController.
My aim is to create something a bit more complex, using a variable based on a Struct to keep everything together. So:
myCard.image // holds a bitmap image
myCard.size // holds the size of the image
And so on. being able to access it in the form of ViewController().myCard to both read and write to is what I am thinking I need to do.
What I don’t want to do is use global variables.
Thanks.
I'm having a hard time seeing much correlation between the title of your question and the body of your question. In fact, I'm not even entirely sure that there's a question being asked. That said, I'll try to address the questions you appear to be asking (re-written how I think they're intended):
When you initialize a new view controller, does it create a new view?
Yes. There is a view property for every view controller, and it's not a shared component or a singleton or anything else like that. There is one main view for every view controller. It's almost certainly composed of dozens of other subviews, but there is one view that is the view for every view controller.
Is there a way to get metadata about the state of a view controller from outside, preferably in the form of a struct?
Absolutely. First, you'd need to define that Card struct. I'd recommend doing it in the same file as the view controller itself. You can define it outside of the view controller, or if you want stricter coupling and namespacing, you can do it inside the view controller. Just be aware that doing that latter would mean that the type name, when referenced outside the view controller, would be ViewController.Card rather than just Card.
Then you'd want to create either a computed property (var card: Card) or a method (func card() -> Card) on your view controller which builds and returns one of those based on the state. It sounds like you're already leaning toward the property approach.
Note: I would absolutely advise against having one that is a normal get/set property because then you'd have to constantly update it and modify it. The best thing to do is have a computed property which builds it on the fly. So when the property is called, it reaches into all your components to get the info you want (like image sizes, strings, etc) and then packages up and hands off the Card metadata struct. Doing it on-demand like this eliminates unnecessary complexity and consolidates the metadata logic into one place.
Some dangerous things in your code example:
I can't think of a good reason to implement viewDidAppear() but not call super.viewDidAppear(). Unless you have an enormously compelling reason to leave that out (I honestly can't think of a single one), do not do so.
I don't see any good reason for your first implementation of your setText(thisText:) method. The way you use it in your Button1(_ sender: Any) IBAction functionally does absolutely nothing. That method in general is screwy for several reasons: it's got an upper-case method name, sets the textView text by trying to assign to stringValue for some super strange reason, and does in three lines what could be done in one:
textView1.text = "This is button 1 clicked"
The second implementation of setText(thisText:) makes even less sense than the previous. The two biggest problems being 1) that you don't even use the thisText argument passed in, and 2) your method, which is called 'set text' is creating a whole new view controller every single time it gets called? That's a huge violation of "doing what it says on the tin." Methods should have a single responsibility and shouldn't do anything beyond that responsibility. I'd never in a million years look at a method called setText and think "I'll bet this initializes a view controller." Never.
I see this question has already been downvoted a bit (not by me), and I'd like to take a moment to coach you in using Stack Overflow: Ask clear, concise, specific questions about clear, concise, specific topics. As I said at the top of my answer, there doesn't appear to be a question anywhere in your post. I had to kind of make some up that I inferred from what you wrote.
Remember: coding isn't just wiggling your fingers while you think about an app. If you're doing the hard work of good engineering, you'll likely spend a ratio of about 10:1 (or more!) of staring at your screen to actually typing any code. Every time you write a line of code, you should be asking yourself, "Why am I writing this line of code? Is this necessary? Am I reinventing the wheel?)
Good luck!

iOS 10 issue with storyboard custom view classes

Does anybody know why the ZoomingPDFViewer Apple Sample Code project no longer works? It was working prior to the iOS 10 release but now it keeps returning a unrecognized selector error when calling [PDFScrollView setPDFPage:].
It seems like the custom classes set in the storyboard are no longer being instantiated.
I had the same issue. It seems that the auto conversion to Swift 3 doesn't work well with IBs.
There are two options:
a) You can set the argument label--i.e.the parameter name to be shown externally--to _. For example,
#IBAction func setPDFPage(_ sender: AnyObject) {
// ...
}
b) You can remove the IBAction connection in the IB and re-set it. Notice instead of the previous setPDFPage(sender:) the connection will say setPDFPageWithSender(sender:) or something like that. (I didn't actually try it with the code in question, but writing from experience here.)
The same is true for segues.

Size classes - traitCollectionDidChange crashes with nil value

I have a swift project with a storyboard using Size Classes, a ViewController which conforms to the UITraitEnvironment protocol and have implemented the function traitCollectionDidChange.
When I first launch the app traitCollectionDidChange is called which seems reasonable - it allows developers to handle the first size class presented by the application, however the previousTraitCollection variable passed to it is nil. I can't check that this value is nil as its NOT optional, and accessing it in any way causes a crash. Changing the parameter to optional in the function declaration causes a build error, complaining that I have not implemented required methods in the protocol.
For now I have managed to work around this by creating a separate function which takes an optional variable of the same type that I can then check against, but I would like to know why this is happening.
If its any use to anyone at all, here is the function I am overriding
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection) {
// Accessing previousTraitCollection causes a crash
}
Can anyone shed any light on this?
You have to declare that the type of the argument is optional, because accessing it forces the compiler to try and unpack the address which causes the crash. Replace it with:
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
// Accessing previousTraitCollection causes a crash
}