PFLoginViewController´s Twitter, Facebook buttons not working when subclassing - parse-platform

When I subclass PFLoginViewController from Parse so that I can customise UI, both Twitter and Facebook buttons stop working as they used to if I were not subclassing.
This is the class I instantiate my subclass from:
class SettingsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, PFLogInViewControllerDelegate, PFSignUpViewControllerDelegate, MFMailComposeViewControllerDelegate
{
#IBOutlet weak var loadingIndicator: UIActivityIndicatorView!
#IBOutlet weak var tableView: UITableView!
// THIS DOES WORK (without subclassing)
//var loginVC: PFLogInViewController = PFLogInViewController()
var loginVC:LogInViewController {
get{
let livc = LogInViewController()
livc.delegate = self
let suvc = SignUpViewController()
suvc.delegate = self
livc.signUpController? = suvc
return livc
}}
And this is my subclass of PFLogInViewController
class LogInViewController: PFLogInViewController {
static let forgotPassword = "Trouble Signing In?"
override func viewDidLoad() {
super.viewDidLoad()
let lv = self.logInView
self.facebookPermissions = ["public_profile", "email"]
self.fields = [PFLogInFields.UsernameAndPassword, .Facebook, .Twitter, .SignUpButton, .LogInButton, .PasswordForgotten, .DismissButton]
lv?.logo = UIImageView(image: UIImage(named: "myImage"))
lv?.logo?.contentMode = UIViewContentMode.ScaleAspectFit
lv?.emailAsUsername = true
self.view.backgroundColor = UIColor.whiteColor()
}
It might be a bug in Parse´s side, though. Any help would be appreciated!

This is a known issue - it looks like this bug was patched in the GitHub repo (see this commit) but not in the Cocoapod, if that's what you're using. I'm hoping they'll push the changes over soon :)
The reason for the bug in the old code is that the superclass is not updating the target/actions for the buttons when you set which fields you want in viewDidLoad. PFLoginViewController configures the target/actions in its viewDidLoad, but since you're setting the self.fields after calling super.viewDidLoad (which is what you should be doing, since you always call super first :) ), the field target/actions aren't re-updated. In other words, it looks like, the way PFLogInViewController was written, it only works if the fields don't change after its viewDidLoad is called :/. And since the default fields don't include a Facebook button, the Facebook button will never be set up. A simple solution is just to move self.fields =... before super.viewDidLoad().

Related

How to hide the shortcut bar in iOS9

I have a textfield with a hidden keyboard (since I'm using it with bluetooth). However, in iOS9 the shortcut bar keeps appearing.
Is there a way to hide it too?
Thank you so much!
You can pass your textfield name in place of userNameTextField for which you want to remove shortcut bar.
UITextInputAssistantItem* item = [userNameTextField inputAssistantItem];
item.leadingBarButtonGroups = #[];
item.trailingBarButtonGroups = #[];
In Swift 2.0
if #available(iOS 9.0, *) {
let item : UITextInputAssistantItem = yourTextView.inputAssistantItem
item.leadingBarButtonGroups = []
item.trailingBarButtonGroups = []
} else {
// Fallback on earlier versions
}
I had the same issue. And so starts a search of SO. So the above helped me out, but the whole, "if iOS9 thing" might be best framed like this:
if ([self respondsToSelector:#selector(inputAssistantItem)]) {
// iOS9.
UITextInputAssistantItem* item = [self inputAssistantItem];
item.leadingBarButtonGroups = #[];
item.trailingBarButtonGroups = #[];
}
Happily, I'd created a sub-class of a UITextField, (CHTextField) and was in use everywhere. So it was a very easy fix to whack this in the over-ridden "init" method.
Hope it helps.
Alternatively, just create an extension for UITextField in Swift 2.0 like this.
extension UITextField
{
public func hideAssistantBar()
{
if #available(iOS 9.0, *) {
let assistant = self.inputAssistantItem;
assistant.leadingBarButtonGroups = [];
assistant.trailingBarButtonGroups = [];
}
}
}
Then you can just call hideAssistantBar() on any text field you like.
#IBOutlet weak var myTextField: UITextField?;
override public func viewDidLoad() {
super.viewDidLoad();
myTextField?.hideAssistantbar();
}
In Swift 3.0 and 4.0
self.textField.inputAssistantItem.leadingBarButtonGroups.removeAll()
self.textField.inputAssistantItem.trailingBarButtonGroups.removeAll()
An easy way to do this for all text fields in your app is to create a category on UITextInputAssistantItem and override the getters for leadingBarButtonGroups and trailingBarButtonGroups like this:
#implementation UITextInputAssistantItem (RemoveBars)
- (NSArray<UIBarButtonItemGroup *> *)leadingBarButtonGroups
{
return #[];
}
- (NSArray<UIBarButtonItemGroup *> *)trailingBarButtonGroups
{
return #[];
}
#end
This worked for me on iOS 9.x and 8.x, no need for any conditional code.
Be careful with this though, this overrides those properties for EVERYTHING that uses UITextInputAssistantItem
Just to expand on the other answers here. I cobbled together some Swift 2.0 code that will loop through all subviews of a given view and disable the UITextInputAssistantItems for all UITextFields and UISearchBars.
func hideTheAssistantBar(view:UIView) {
//Check this view
for case let textField as UITextField in view.subviews {
let item : UITextInputAssistantItem = textField.inputAssistantItem
item.leadingBarButtonGroups = []
item.trailingBarButtonGroups = []
}
for case let searchBar as UISearchBar in view.subviews {
let item : UITextInputAssistantItem = searchBar.inputAssistantItem
item.leadingBarButtonGroups = []
item.trailingBarButtonGroups = []
}
//Now find this views subviews
let subviews = view.subviews
for subview : AnyObject in subviews {
if subview.isKindOfClass(UIView) {
hideTheAssistantBar(subview as! UIView)
}
}
}
You can then call this function passing in whatever view you would like to start at. I call this inside of my ViewDidLoad() method and pass in self.view like hideTheAssistantBar(self.view).
I actually went a step further for my needs and added this function to a helper class I use for common code. Therefore inside of my viewDidLoad() function I actually just call helper.hideTheAssistantBar(self.view) and then I don't have to put that function in every file.
Hope this helps someone coming along looking for an easy way to remove the assistant bar from all UITextFields and UISearchBars in one fail swoop.
Thanks to #Arkader for the swift code to recursively find all subviews. Swift List Subviews
Just to build on what Pranavan posted because setting the bar button groups to an empty array doesn't seem to work in iOS 12 or 13 using Xcode 11.
let inputAssistantItem = textFieldForTypingText.inputAssistantItem
inputAssistantItem.leadingBarButtonGroups.removeAll()
inputAssistantItem.trailingBarButtonGroups.removeAll()
I placed the above code in the viewDidLoad() function.
You can also give the option to the user:
inputAssistantItem.allowsHidingShortcuts = true
In the case letting the user hide it, if the text field becomes first responder again, they'll have to hide it again.

Xcode_OSX/Swift_NSPopUpButton.

I am incredibly new to this, so please keep that in mind!
I've been at this all night, watched countless videos/haunted
countless forums...I can't find one single answer!
I am trying to make a basic popup menu in Swift/OSX What I need to figure out is:
How can I add more than the 'three items' to this menu
Whatever is selected in the popup, for that info to send an integer
value to another number.
I very much would appreciate your help, Thanks.
A NSPopupButton is a container for a bunch of NSMenuItem objects so to add an item you can use
func addItemWithTitle(_ title: String!)
The NSMenuItem gets constructed for you by the call.
and as you may wish to start from scratch you can use
func removeAllItems()
To clean existing items from the button.
There are also other methods around moving and removing menu items from the button.
A NSPopupButton is-a NSControl so you can use var action: Selector to set the action sent when an item is selected and var target: AnyObject! to control which object receives the message. Or just wire it up in Interface Builder.
protocol FooViewDelegate{
func itemWithIndexWasSelected(value:Int)
}
class FooViewController: NSViewController {
#IBOutlet weak var myPopupButton: NSPopUpButton!
var delegate: FooViewDelegate?
let allTheThings = ["Mother", "Custard", "Axe", "Cactus"]
override func viewDidLoad() {
super.viewDidLoad()
buildMyButton()
}
func buildMyButton() {
myPopupButton.removeAllItems()
myPopupButton.addItemsWithTitles(allTheThings)
myPopupButton.target = self
myPopupButton.action = "myPopUpButtonWasSelected:"
}
#IBAction func myPopUpButtonWasSelected(sender:AnyObject) {
if let menuItem = sender as? NSMenuItem, mindex = find(allTheThings, menuItem.title) {
self.delegate?.itemWithIndexWasSelected(mindex)
}
}
}
All the button construction can be done in Interface Builder rather than code too. Remember that you can duplicate items with CMD-D or you can drag new NSMenuItem objects into the button.

(NSMenuItem): missing setter or instance variable

I am encountering a strange error:
2015-04-02 12:20:14.642 test[21167:257788] Failed to connect
(testApp) outlet from (test.AppDelegate) to (NSMenuItem): missing
setter or instance variable
inserted id: 122
I occured when a added a menuItem to a menu and connected a function to it.
I do not know what the Problem is. The app works fine but i don't think it is a smart idea to ignore the error.
What is meant by setter or instance variable? Why is it needed?
UPDATE: Here is the relevant code:
import Cocoa
import Foundation
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var statusMenu: NSMenu!
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
func applicationDidFinishLaunching(aNotification: NSNotification) {
let icon = NSImage(named: "statusIcon")
statusItem.image = icon
statusItem.menu = statusMenu
// Time for constant repeat
NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "timerRepeat", userInfo: nil, repeats: true)
}
// Method to call the tracking core
func timerRepeat() {
//....
}
#IBAction func frontEnd(sender: NSMenuItem) {
var targetURL : String = NSBundle.mainBundle().resourcePath!
targetURL = targetURL + "/" + "front.app"
let workspace = NSWorkspace()
workspace.launchApplication(targetURL)
}
#IBAction func menuClicked(sender: NSMenuItem) {
NSApplication.sharedApplication().terminate(self)
}
}
You have a broken outlet in your xib file. Usually it happens when you set up an outlet to ivar or property which is later deleted or renamed not using Xcode's rename feature.
Also make sure that your custom view or view controller class is added to your target. (Project => Target Name => Build Phases => Compile Sources). It's possible that a file is in your project but not your target.
This happens because you at one point created an #IBOutlet for a storyboard element. You then later removed the code (reference) from your swift file.
I created an example where I create two extra #IBOutlets (I named them 'correctField' and 'incorrectField'- both are incorrect though) and connected them from my storyboard to my swift file.
I then removed the code from my swift file. This generates the log as shown in the following figure :
To remove this kind of log message, you can do the following:
Go to the 'storyboard' and select the storyboard elements you created connections (#IBOutlets) from.
Open the 'connection inspector' as showed in the figure below
Remove the Referencing Outlets which are incorrect (in my case it is the 'correctField' and 'incorrectField')
Done
This was done in xCode 11

OSX Swift webview load local file

I know this has been asked many times and I have read quite a few questions
and googled for days with no success so far.
I just want to load a local html file in a desktop app, truth is for this project I need a JS library and most of it is already done as a webpage (css, js and html, no server side processing needed). I dont want to force the app to load the webpage from an internet server so as to not force the users to have an internet connection. Needless to say I am completely inexperienced in Swift and apple development.
Right now this is the problem I am having:
the ide complaints about the params and I cant seem to get them right.
For reference here is a snippet of my latest code:
class AppDelegate: NSObject, NSApplicationDelegate
{
#IBOutlet weak var window: NSWindow!
#IBOutlet weak var webView: WebView!
typealias NSSize = CGSize
func applicationDidFinishLaunching(aNotification: NSNotification?)
{
self.window.title = "Chess Study Room"
var try1 = "main.html";
println(try1);
var try2 = NSURL(string: try1)!;
println(try2);
var try3 =
NSBundle.URLForResource("main", withExtension: "html", subdirectory: "web", inBundleWithURL: try2);
println(try3);
var try4 = NSBundle.pathForResource("main", ofType: "html", inDirectory: "web");
println(try4);
var try5 = NSString.stringByAppendingPathComponent("main.html");
println(try5);
// var myInternalUrl = NSURL(string: myInternalHtml)!;
//NSLog("%s", myInternalHtml!);
var request = NSURLRequest(try1,
NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData,
60
);
self.webView.frameLoadDelegate = self;
self.webView.mainFrame.loadRequest(
request!
);
}
But as you can see from my gif I've tried other things as well. The full errors are /path/TestWebView/AppDelegate.swift:32:35: Extra argument in call and /path/TestWebView/AppDelegate.swift:32:36: Missing argument for parameter 'cachePolicy' in call
At this point try1 and try2 output "main.html", try3 and try4 output nil and try5 outputs"(Function)"
The structure of folders is this:
I added the folder "web" as a reference (as advised in another question) but I doubt this would work to ship just one package...
I dont know if there's any difference but I'm not targetting iOS, I want this to be a desktop app.
Any advise will be greatly appreciated
After reading this post in the apple forums and putting it together with this other question I was able to come up with the following working code:
func applicationDidFinishLaunching(aNotification: NSNotification?)
{
self.window.title = "My App Title"
var try6 = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("main", ofType:"html")!)
var request = NSURLRequest(URL: try6!);
self.webView.frameLoadDelegate = self;
self.webView.mainFrame.loadRequest(request);
}

Swift NSViewController

I'm new to cococa and swift, and I'm tring to create a custom ViewController.
class StatusUpdate : NSViewController {
#IBOutlet var StatusView: NSView!
#IBOutlet var eventsFoundCell: NSTextFieldCell!
#IBAction func update(sender: AnyObject) {
StatusView.hidden = false
eventsFoundCell.stringValue = "A"
}
}
The code as shown above is working as you would expect it.
But what I tring to do is to add an other function to that class like :
func otherUpdate() {
eventsFoundCell.stringValue = "B"
}
In order to update the stringValue of the eventsFoundCell variable.
So I could call it in an other class :
var update = StatusUpdate()
update.otherUpadte()
When calling update.otherUpadte() with in n other class,
I'm always getting an error like :
Thread1: EXC_BAD_INSTRUCTION(code=EXC_1386_INVOP, subcode=0x0)
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
Any idea on show I could do this ?
Thank you !
It is because in this line
var update = StatusUpdate()
You are creating a new instance of StatusUpdate. The variable StatusView is not bound to any NSView
You already got the answer, so this is recommendation only. It will make your Cocoa life way easier if you follow well established conventions. So, instead of:
#IBOutlet var StatusView: NSView!
you should write
#IBOutlet var statusView: NSView!
There are many cases in Cocoa where proper capitalisation (and style in general) are assumed in order for the frameworks to work.

Resources