I want to create an login template in a normal class,but in the class,the view can't call the event,no "sss" be printed when the Event triggered
import UIKit
class ViewController: UIViewController ,UITextFieldDelegate{
override func viewDidLoad() {
super.viewDidLoad()
let loginView=login(view: self.view,controller:self)
loginView.setView();
}
}
import UIKit
class login{
let PassWord=UITextField()
let view:UIView!
let controller:ViewController
init(view:UIView,controller:ViewController){
self.view=view
self.controller=controller
}
func setView(){
initPassWord()
print("1")
}
func initPassWord(){
PassWord.placeholder="PassWord"
PassWord.addTarget(self, action: #selector(login.exits(_:)), for:.allEvents)
//PassWord.addTarget(controller, action: #selector(login.exits(_:)), for:.allEvents)
}
#IBAction func exits(_ sender:UITextField){
print("sss")
}
You need
var loginView:Login! // hold a strong reference here , start class names with capital letter
override func viewDidLoad() {
super.viewDidLoad()
loginView = Login(controller:self)
loginView.setView();
}
Also make it weak
weak var controller:ViewController
as not to cause retain cycles
BTW you only need to send the controller , and there you can access it's view
Related
I have a singleton to store some global data for my macOS app, one of my ViewController keeps modifying data. I want to simultaneously show the changes in a View, which is related to another ViewController. what 's the best way to do this?
Global Data:
final class AppData {
static var logs: [LogData] = []
}
ViewController 1:
class FirstViewController: NSViewController {
AppData.logs.append(newLogData)
}
ViewController 2:
class SecondViewController: NSViewController {
// what's the best way to simultaneously watch the change of AppData.logs?
}
If your App is planned to be macOS only you can use a NSObjectController. This is definitively the easiest approach and you can do most of the configuration in Interface builder. It works internally with bindings. In case of an array you want to observe, you would use a NSArrayController.
One way is to use the notificationcenter
In viewcontroller2 add:
override func viewDidLoad() {
super.viewDidLoad()
notificationCenter.default.addObserver(self,
selector: #selector(view1DidChange),
name: "view1DidChange",
object: nil
)
}
#objc private func view1DidChange(_ notification: Notification) {
// Do something
}
In viewcontroller1 add
notificationCenter.default.post(name: "view1DidChange", object: self)
This can be repeated in every class, that should listen.
Here i am sharing the Delegate & Protocol approach to achieve this functionality.
final class AppData {
static var logs: [LogData] = []
}
protocol FirstViewControllerDelegate {
func ViewControllerDelegate(appData:[LogData])
}
class FirstViewController: NSViewController {
var delegate:FirstViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
AppData.logs.append(newLogData)
self. delegate?.ViewControllerDelegate(appData: AppData.logs)
}
}
class SecondViewController: NSViewController,FirstViewControllerDelegate {
var firstViewController:FirstViewController = FirstViewController()
override func viewDidLoad() {
self.firstViewController.delegate = self
}
func ViewControllerDelegate(appData:[LogData]){
//Do Update the UI
}
}
I am trying to use Parse PFUser in a software for OSX desktop. When I try to use it PFUser.query() it gives a message: Failed to set (contentViewController) user defined inspected property on (NSWindow): The class PFUser must be registered with registerSubclass before using Parse.
It is happening without registering the class.
I tried it registering the class this way: PFUser.registerSubclass() but it still doesn't work.
I will use the default PFUser without adding any fields to it, so I don't need to create a custom class to be my PFUser.
I tried to use PFUser.enableAutomaticUser() without success
Code below:
AppDelegate.swift
import Cocoa
import Parse
import Bolts
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let APP_ID = "app_id"
let CLIENT_KEY = "client_key"
let SERVER = "https://parseserver.com/"
func applicationDidFinishLaunching(_ aNotification: Notification) {
PFUser.registerSubclass()
let configuracaoParse = ParseClientConfiguration {
$0.applicationId = self.APP_ID
$0.clientKey = self.CLIENT_KEY
$0.server = self.SERVER
}
Parse.initialize(with: configuracaoParse)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
ViewController.swift
import Cocoa
import Parse
class ViewController: NSViewController {
#IBOutlet weak var emailTextField: NSTextField!
#IBOutlet weak var senhaSecureTextField: NSSecureTextField!
override func viewDidLoad() {
super.viewDidLoad()
contaUsuarios()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func entrarButtonClicked(_ sender: NSButton) {
}
func contaUsuarios() {
let query = PFUser.query()
query?.countObjectsInBackground(block: {
(count, error) -> Void in
let numeroUsers = Int(UInt32(count))
if numeroUsers > 0 {
}
print(numeroUsers)
})
}
}
Reading some content on the internet I discovered that in OSX the ViewController is launched before the AppDelegate finishes loading, so I initialized the Parse connection and subclassing in the ViewController's viewDidLoad instead of AppDelegate and it is working just fine.
I am really struggling to pass the contents of one array from a view controller to another to set up the contents of a nscombobox. I have tried everything I can think of, prepare for segue, init; but nothing seems to work.
the program flow is as follows: the user enter a number into a text field and based on it an array with the size of the number is created. Once the user presses a button the next VC appears that has a combo box and inside that combo box those numbers need to appear. All my attempts result in an empty array being passed. Could someone please take a bit of time and help me out. Im sure I'm doing a silly mistake but cannot figure out what.
Code listing below:
Class that take the user input. At this stage I'm trying to pass the contents of the array in the next class as I gave up on prepare for segue because that one crashes because of nil error. Please note that prepare for segue is uncommented in the code listing just for formatting purposes here. Im my program it is commented out as I am using perform segue at the moment.
Any solution would be nice please. Thank you.
import Cocoa
class SetNumberOfFloorsVC: NSViewController {
//MARK: - Properties
#IBOutlet internal weak var declaredNumber: NSTextField!
internal var declaredFloorsArray = [String]()
private var floorValue: Int {
get {
return Int(declaredNumber.stringValue)!
}
}
//MARK: - Actions
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction private func setNumberOfFloors(_ sender: NSButton) {
if declaredNumber.stringValue.isEmpty {
let screenAlert = NSAlert.init()
screenAlert.messageText = "Please specify the number of floors!"
screenAlert.addButton(withTitle: "Got it!")
screenAlert.runModal()
} else if floorValue == 0 || floorValue < 0 {
let screenAlert = NSAlert.init()
screenAlert.messageText = "Please input a correct number of floors!"
screenAlert.addButton(withTitle: "Got it!")
screenAlert.runModal()
} else {
for i in 0...floorValue - 1 {
declaredFloorsArray.append(String(i))
}
print("\(declaredFloorsArray)")
let declareNumberOfRoomsVC = SetNumberOfRoomsForFloorVC(boxData: declaredFloorsArray)
declareNumberOfRoomsVC.boxData = declaredFloorsArray
performSegue(withIdentifier: "set number of rooms", sender: self)
}
}
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
if segue.identifier == "set number of rooms" {
if let addRoomsVC = segue.destinationController as? SetNumberOfRoomsForFloorVC {
addRoomsVC.floorBox.addItems(withObjectValues: declaredFloorsArray)
}
}
}
}
this is the class for the next VC with the combo box:
import Cocoa
class SetNumberOfRoomsForFloorVC: NSViewController, NSComboBoxDelegate, NSComboBoxDataSource {
//MARK: - Properties
#IBOutlet internal weak var floorBox: NSComboBox!
#IBOutlet private weak var numberOfRoomsTxtField: NSTextField!
internal var boxData = [String]()
//MARK: - Init
convenience init(boxData: [String]) {
self.init()
self.boxData = boxData
}
//MARK: - Actions
override func viewDidLoad() {
super.viewDidLoad()
floorBox.usesDataSource = true
floorBox.dataSource = self
floorBox.delegate = self
print("\(boxData)")
}
#IBAction private func setRoomsForFloor(_ sender: NSButton) {
}
//MARK: - Delegates
func numberOfItems(in comboBox: NSComboBox) -> Int {
return boxData.count
}
func comboBox(_ comboBox: NSComboBox, objectValueForItemAt index: Int) -> Any? {
return boxData[index]
}
}
First you should remove the following code.
let declareNumberOfRoomsVC = SetNumberOfRoomsForFloorVC(boxData: declaredFloorsArray)
declareNumberOfRoomsVC.boxData = declaredFloorsArray
I assume you think that the viewController you created here is passed to prepareForSegue. However the storyboard instantiates a new viewController for you.
After that you need to set your declaredFloorsArray as the the boxData of the new viewController in prepareForSegue and you should be good to go.
By using the delegation protocol I have tried to pass a string (inputFromUser.string) from NSViewController - mainController to custom subclass of NSView of NSPopover - PlasmidMapView, to drawRect function, see code below. But, it didn’t work. I don’t know where a mistake is. Maybe there is another way to pass this string.
Update
File 1.
protocol PlasmidMapDelegate {
func giveDataForPLasmidMap(dna: String)
}
class MainController: NSViewController {
#IBOutlet var inputFromUser: NSTextView!
var delegate: plasmidMapDelegate?
#IBAction func actionPopoverPlasmidMap(sender: AnyObject) {
popoverPlasmidMap.showRelativeToRect(sender.bounds,
ofView: sender as! NSView, preferredEdge: NSRectEdge.MinY)
let dna = inputDnaFromUser.string
delegate?.giveDataForPLasmidMap(dna!)
}
}
File 2
class PlasmidMapView: NSView, PlasmidMapDelegate {
var dnaForMap = String()
func giveDataForPLasmidMap(dna: String) {
dnaForMap = dna
}
override func drawRect(dirtyRect: NSRect) {
let objectOfMainController = MainController()
objectOfMainController.delegate = self
//here I have checked if the string dnaForMap is passed
let lengthOfString = CGFloat(dnaForMap.characters.count / 10)
let pathRect = NSInsetRect(self.bounds, 10, 45)
let path = NSBezierPath(roundedRect: pathRect,
xRadius: 5, yRadius: 5)
path.lineWidth = lengthOfString //the thickness of the line should vary in dependence on the number of typed letter in the NSTextView window - inputDnaFromUser
NSColor.lightGrayColor().setStroke()
path.stroke()
}
}
Ok, there's some architecture mistakes. You don't need delegate method and protocol at all. All you just need is well defined setter method:
I. Place your PlasmidMapView into NSViewController-subclass. This view controller must be set as contentViewController-property of your NSPopover-control. Don't forget to set it the way you need in viewDidLoad-method or another.
class PlasmidMapController : NSViewController {
weak var mapView: PlacmidMapView!
}
II. In your PlacmidMapView don't forget to call needsDisplay-method on dna did set:
class PlasmidMapView: NSView {
//...
var dnaForMap = String() {
didSet {
needsDisplay()
}
//...
}
III. Set dna-string whenever you need from your MainController-class.
#IBAction func actionPopoverPlasmidMap(sender: AnyObject) {
popoverPlasmidMap.showRelativeToRect(sender.bounds,
ofView: sender as! NSView, preferredEdge: NSRectEdge.MinY)
let dna = inputDnaFromUser.string
if let controller = popoverPlasmidMap.contentViewController as? PlasmidMapController {
controller.mapView.dna = dna
} else {
fatalError("Invalid popover content view controller")
}
}
In order to use delegation your class PlasmidMapView needs to have an instance of the MainController (btw name convention is Class, not class) and conform to the PlasmidMapDelegate (once again name convention dictates that it should be PlasmidMapDelegate). With that instance you then can:
mainController.delegate = self
So, after several days I have found a solution without any protocols and delegation as Astoria has mentioned. All what I needed to do was to make #IBOutlet var plasmidMapIBOutlet: PlasmidMapView!for my custom NSView in MainController class and then to use it to set the value for the dnaForMap in #IBAction func actionPopoverPlasmidMap(sender: AnyObject).
class PlasmidMapView: NSView
{
var dnaForMap = String()
}
class MainController: NSViewController
{
#IBOutlet var inputFromUser: NSTextView!
#IBOutlet var plasmidMapIBOutlet: PlasmidMapView!
#IBAction func actionPopoverPlasmidMap(sender: AnyObject)
{
plasmidMapIBOutlet.dnaForMap = inputDnaFromUser.string!
popoverPlasmidMap.showRelativeToRect(sender.bounds,
ofView: sender as! NSView, preferredEdge: NSRectEdge.MinY)
}
}
I want to touch a button in ViewController and run a simple function in a custom class. No parameters needed. Code is basically:
class MyClass: UIView {
//init function is here
//other setup stuff
...
func SetFocus() {
//I want to set the camera focus to infinity.
var error: NSErrorPointer = nil
var pos: CFloat = 1.0
captureDevice!.lockForConfiguration(error)
captureDevice!.setFocusModeLockedWithLensPosition(pos, completionHandler: nil)
captureDevice!.unlockForConfiguration()
}
}
and in ViewController
import UIKit
class ViewController: UIViewController {
#IBOutlet var myClass: MyClass!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func myButton(sender: UIButton) {
myClass.SetFocus()
}
}
I've tried everything I can think of (new at Swift) and the app either crashes or the function I'm calling doesn't run. Everything else in MyClass works OK.
Try to delete the #IBOutlet from before MyClass.
var myClass : MyClass!
You can also try this:
var myClass : MyClass! = MyClass()
From the looks of it, however, everything you are doing in setFocus() in MyClass, can easily be recreated in the ViewController.
Try this
#IBAction func myButton(sender: UIButton) {
var ClassViewController = self.storyboard.instantiateViewControllerWithIdentifier("StoryboardID") as! UIViewController
ClassViewController.MyFunction()
}