Trouble while declaring an element of view in its class file - ios

I've a view controller which contains a view element like the following (the view is the orange element) :
The view is associated to a custom view class that I called "Quickie.swift". This view is an object with a textfield and a button so I want to declare both in the Quickie.swift.
However I can drag/drop to create declaration in the swift file :
And here nothing happen.
I can't find out how to declare this variable in my view object.

Did you tried to write it by your own and drag it to the code?
#IBOutlet var view: UIView!
You Need to release r-Click on view: UIView!

Related

Add a custom view to the UIViewController from the custom view class

I have a custom view. I named AnimatingView. I have customized the view in the AnimatingView.swift. In the ViewController, I am initiating the view with
let customAnimatingView = AnimatingView(overView: self.view)
and then I add the subView with -
self.view.addSubview(customAnimatingView)
However, I don't want the user add it as a subView like above, rather. I want to call a func from AnimatingView and the custom view should add it the controller like -
customAnimatingView.preset()
Can anyone give me some hint on how can I achieve such behavior.
Inside the custom view do
// add a property that refers to the the vc container of the view
// init it when you create an instance of the view
weak var parentController:UIViewController?
func present() {
parentController?.view.addSubview(self)
}
OR if you need to send the main view instead
var parentView:UIView?
func present() {
parentView?.addSubview(self)
}

Deallocate view controllers in navigation controller that have a reference to self

Say I have view controllers A, B, C, D & E all embedded in a navigation controller. In view controller B, I have a custom UIImageView object. In C, I have a custom UITextfield object. Both custom classes have a reference to the view controller for various reasons such as I have to perform things like segue when a user taps the image view. To accomplish this, I have this inside each custom class file:
var controller: UIViewController?
And then inside each view controller, inside viewDidLoad I set that variable to self and everything works as expected (segues on tap etc..)
I have an unwind segue from E back to A. However, I noticed that due to these custom objects in view controllers B & C, both were not being deallocated due to a retain cycle caused by having this reference to the view controller. I fixed the issue by setting the controller variable to nil upon segue, however this creates a problem such that if the user goes back (pops the current view controller), because I set the controller variable to nil upon segue, nothing works (it wont segue again because controller var = nil). I thought I might fix this by adding viewWillAppear code as follows:
override func viewWillAppear(_ animated: Bool) {
usernameTextField.controller = self
passwordTextField.controller = self
}
Because I read that viewWillAppear will be called each time the viewcontroller comes into view. This did not fix the problem.
Any ideas on how to go about this? How can I set the controllers to nil during the unwind maybe...?
As the other answers have said you need to make it a weak reference like this:
weak var controller: UIViewControler?
However I would go further and say that you should not be keeping a reference to to a UIViewController inside any UIView based object (UIImageView, UITextField, etc). The UIViews should not need to know anything about their UIViewControllers.
Instead you should be using a delegation pattern. This is a basic example:
1) Create a protocol for the custom UIImageField like this:
protocol MyImageFieldProtocol: class {
func imageTapped()
}
2) Then add a delegate like this:
weak var delegate: MyImageFieldProtocol?
3) Your UIViewController then conforms to the protocol like this:
class MyViewController: UIViewController, MyImageFieldProtocol {
}
4) Somewhere inside the view controller (viewDidLoad is usually a good place you assign the view controller to the image views delegate like this:
func viewDidLoad {
super.viewDidLoad()
myImageView.delegate = self
}
5) Then add the function to respond to the protocol action to the view controller like this:
func imageTapped {
self.performSegue(withIdentifier: "MySegue", sender: nil)
}
var controller: UIViewController? should be a weak reference. Like this:
weak var controller: UIViewController?
To know more about that read about Resolving Strong Reference Cycles Between Class Instances in Swift's documentation.
You should use weak references when you keep some ViewControllers
weak var controller: UIviewControler?
You should check everything link to retain cycle, and referencing in swift :
https://krakendev.io/blog/weak-and-unowned-references-in-swift
https://medium.com/#chris_dus/strong-weak-unowned-reference-counting-in-swift-5813fa454f30
I had similar issues, I advice you to look at those link : How can I manage and free memory through ViewControllers

Disable buttons of 1 view controller in another view controller

I am having a problem when trying to disable button's user interaction of 1 view controller in another view controller.
I have searched similar questions here, but some seems outdated or does not work for me:
How to access an IBOutlet from another class.
My scenario is as follows:
class ViewControllerA() {
#IBOutlet weak var btnFirst: UIButton!
#IBOutlet weak var btnSecond: UIButton!
#IBOutlet weak var btnThird: UIButton!
override func viewDidLoad() {
var vcB = ViewControllerB()
vcB.closure = {
// Meet some condition, want to disable buttons of ViewControllerA here
}
}
}
class ViewControllerB() {
var closure: () -> Void = {}
// Do something with closure here
}
My problem is that i set the breakpoint in the closure and try to use directly IBOulet in closure to disable buttons like:
btnFirst.isUserInteractionEnabled = false
Or try to set a property of ViewControllerA in closure of ViewControllerB and use property observer, whenever this property changes, enable or disable buttons of ViewControllerA.
My problem is that, i can still click the buttons as if it's enable. Sorry, i cannot post the code, please help me!
Thanks
You can post notification from second view controller and add observer for that particular posted notification in first view controller.
In that observer method, you can do your stuff like disabling user interaction for first view controllers' button.
Since you did not post any relevant code, I can only guess what might have happened:
I assume that you are not accessing the btnFirst of the correct viewcontroller. In ViewControllerA.viewDidLoad you are creating an new Instance of ViewControllerB and set the closure. Are you also showing exactly this view controller's view? Or how will the user navigate to B? If you are using storyboard segues, those will create a new B instances and show its view. Now when you execute the closure in A, this will disable the button of the first B, not of the B that is displayed.
But this is still just a guess...

How do you insert content to a custom status cel which contains a UITextField?

I have two view controller that for adding my "Routines". One is to display a list of all routines and one to edit/create routines.
In the first view controller I have two way to enter to the second controller
by selecting an already created routine
by pressing the "+" button on the navigation bar
If the press option is pressed the data should be recalled from CoreData entity and the fields in the second view controller shall be populated.
But I get the error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Would anyone know how to insert content to a custom status cell named "NameCell" which contains a UITextField?
Thanks in advance,
Ace
If you are using static cells in your table view in the storyboard, but want to populate data in one of them, one does not implement any of the UITableViewDataSource methods, but rather just create IBOutlet from the UILabel in question, and then you can populate it as you see fit.
class ViewController: UITableViewController {
#IBOutlet weak var foo: UILabel!
var bar: String! // this is populated by `prepareForSegue` of presenting view controller
override func viewDidLoad() {
super.viewDidLoad()
foo.text = bar
}
}
As the Table View Programming Guide for iOS says:
Note: If a table view in a storyboard is static, the custom subclass of UITableViewController that contains the table view should not implement the data source protocol. Instead, the table view controller should use its viewDidLoad method to populate the table view’s data. For more information, see Populating a Static Table View With Data.

Swift put multiple IBOutlets in an Array

I made these (marked with red border) IBOutlets using ctrl + drag
But i don't like to have the exact same line 9 times (DRY)
How do i put these IBOutlets in an Array?
you can define a generic outlet collection in Swift like this:
#IBOutlet var collectionOfViews: Array<UIView>? // = [UIView]?
or for e.g. UIButton objects:
#IBOutlet var collectionOfButtons: Array<UIButton>? // = [UIButton]?
you can find your collections under the Outlet Collections group as usually are in the File's Owner:
it would look on my console after connecting 5 random buttons:
Follow these steps to create an array of outlets an connect it with IB Elements:
Create an array of IBOutlets
Add multiple UIElements (Views) in your Storyboard ViewController interface
Select ViewController (In storyboard) and open connection inspector
There is option 'Outlet Collections' in connection inspector (You will see an array of outlets there)
Connect if with your interface elements
-
class ViewController2: UIViewController {
#IBOutlet var collection:[UIView]!
override func viewDidLoad() {
super.viewDidLoad()
}
}
Solution here Swift - IBOutletCollection equivalent
#IBOutlet var objectCollection: [Object]
This is for macOS (should be similar for iOS) and I do not find an "Outlet Collections" in my storyboard (looks like they took that option out). So I put all my buttons in an NSStackView and linked the stack from storyboard
#IBOutlet weak var buttons: NSStackView!
and then I looped over them to make changes accordingly
for case let (index, button as NSButton) in buttons.arrangedSubviews.enumerated() {
if(index + 1 != someButtonIndex) {button.state = .off}
else {button.state = .on}
}
you can also use tag instead of index
Start with the two view pane where you see both your code and the storyboard. When you make your first IBOutlet connection from the UI to your code, just look carefully at the Connection drop down field and select the option called "Outlet Collection". This will automatically create an array of IBOutlets. Next just look for the little black circle within a circle that is placed in your code where the array is created. Just drag from this circle to all the other UI objects you want to connect to that same collection (not sure if you can mix types). Similarly you can connect all the objects to one Action by dragging from the first black dot created to all the other objects you want to wire up to that action. Also consider EnumerateSequence() to help in working with this Collection. Sweet right?

Resources