How do I manage recurrent child UIViewControllers? - swift3

Scenario:
I instantiate a UIViewController and added it to a
containerViewController.
I dismiss (remove) this child
I select this same child to display again.
My Concern: I wish to create a single child UIViewController instance. But It appears that I would create an additional instance of the child view controller per 'case' iteration, which I do not want.
Question: Does Swift already handle this? ...or must I check for the current view controller's instance prior to making it a child?
If I have to check for its existance, then I'll have to make 'viewController' global for all cases.

No Swift doesn't handle this automatically. You must check the existing child view controllers to prevent adding duplicates.
You could use code like this:
if let controller = parent.childViewControllers.filter { $0 is CountriesViewController }.first {
// use existing child controller here
}
else {
// create new child controller and add it to parent here
}

Related

Downsides to NOT calling UIViewController.addChild, .didMove, etc when embedding views from other view controllers

What are the downsides to not following this process?
let parent = UIViewController()
let child = UIViewController()
parent.view.addSubview(child.view)
parent.addChild(child)
child.didMove(toParent: parent)
// and to remove
child.willMove(toParent: nil)
child.removeFromParent()
child.view.removeFromSuperview()
and instead just doing something more on the order of
let parent = UIViewController()
let child = UIViewController()
parent.view.addSubview(child.view)
// and to remove
child.view.removeFromSuperview()
My specific desire is to use SwiftUI views in place of UIViews sprinkled through my project, but officially you're supposed to use a UIHostingController and embed it as a child view controller of whatever parent view controller it belongs to.
I was previously under the impression that you have to call these methods, but then another developer suggested I just try not calling them with the assumption I'm only missing out on view controller lifecycle events (which I don't think matter to me in most cases). I've since tried it and it worked, but I'm worried about what I'm missing/why this might be a bad idea.
I recently came across an example of something you might lose if you don't add the UIHostingViewContoller as a child of the parent view controller in this article about using SwiftUI views in self-sizing table view cells. If you don't add it as a child, the height of the cell holding its view is not always calculated correctly.
https://noahgilmore.com/blog/swiftui-self-sizing-cells/#view-controller-containment

Make a custom class of UIPickerView able to send events in Swift

I've written a custom class based on UIPickerView. Within a class I implemented next method:
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let min = (selectedRow(inComponent: 0) % minutes.count)
let sec = (selectedRow(inComponent: 1) % seconds.count)
self.min = min
self.sec = sec
}
Every time user has selected value from "min" and "sec" using picker view, these class variables are changed accordingly. So, it's work ok.
Now, I would like this class to send event every time these variables are changed. Is it possible to adopt valueChanged event for this class, and how to implement it, or I have to make custom event for these purposes? I would like to do it in that way to be able making action outlets via IB. Please, correct me if I missed the pattern.
May be I put this question in other words: what makes the build-in UIDataPicker able to connect to view controller within interface builder with actions outlets - and what should I add to my custom class to achieve partially that functionality?
Apple stated that: "UIDatePicker class uses a custom subclass of UIPickerView to display dates and times." Correct me here if I'm wrong, but they also point out that UIDatePicker inherits from UIControl rather than from UIPickerView. As for UIPickerView - they say it inherits from the UIView. So, I see no connection in terms of inheritance of UIControl for UIPickerView and UIDatePicker. Also I can't inherit UIControl to obtain event sending functionality due to multiple inheritance of UIView class. Can't understand why things here so weird and I can't use UIControl functionality for UIPickerView.
create YourCustomControl inherit from UIControl
addSubview UIPickerView
set YourCustomControl as a delegate for this picker
To send event you need add this line
sendActions(for: .valueChanged)
in every place where these variables are changed
UIKit - sendActions

Why is QML deleting my C++ ListView model?

Using Qt 5.5.1 on iOS 9 I'm trying to assign a dynamically created QAbstractListModel to the model property of a ListView:
Window {
ListView {
model: api.model()
delegate: delegate
}
Component {
id: delegate
Text { text: "Test" }
}
}
api is a C++ object assigned to the QML context with setContextProperty. The model method is a Q_INVOKABLE which returns a QAbstractListModel *. This all works, my ListView is populated with data.
The problem is when I start scrolling. Usually after the second full scroll (to the bottom, back up to the top and down again) my ListView starts to clear itself out. The debugger is telling me the QAbstractListModel is being destroyed.
I don't want to set CppOwnership on the model. Is there another way to prevent the ListView from destroying its model?
QML seems kind of broken in this regard, I've experienced completely arbitrary deletions of objects still in use in multiple scenarios. Objects with parents and referenced by the JS engine are being deleted for no apparent reason while JS garbage still takes hundreds of megabytes of memory instead of being freed. This applies to both objects returned from C++ and objects created in QML. When an object is returned from a C++ function to QML, ownership is passed to the QML engine, which makes the object vulnerable to such arbitrary deletions.
The solution is to force CPP ownership and manage the object's lifetime manually - keep in mind destroy() won't work on such objects, so you have to use a C++ function from QML to delete it.
qmlEngine.setObjectOwnership(obj, QQmlEngine::CppOwnership);
Also, as BaCaRoZzo mentioned, exposing the model as a property to api might be the appropriate form. It depends on whether the function is just an accessor to an existing object or it creates the object itself.
At any rate, keep in mind that QML object lifetime management at this point cannot and should not be trusted.
Even though I've accepted ddriver's answer I've found a solution that seems to better match what I wanted.
By dynamically loading my components and storing the model as a variable, I'm able to get QML to keep my C++ models alive and to destroy them when required, for example:
MyComponent {
property var model: api.createModel()
ListView {
model: model
delegate: delegate
[...]
}
Component { id: delegate [...] }
Component.onDestruction: model.destroy()
}
Unfortunately the model.destroy() call seems to be required. I was expecting the garbage collector to pick this up, but it doesn't seem to.
I've only tested this is toy examples so far, caveat lector.
Just to say - I can confirm the same issue on both Linux x86_64 and Android ARMv7.
MyComponent {
property var model: api.createModel()
ListView {
model: model
delegate: delegate
[...]
}
Component { id: delegate [...] }
}
Seems to be enough if you don't mind the model being destroyed later in time.
I want to add something to ddriver's answer, which is more than a comment.
This same problem came up for me. basically, i wanted to create a dynamic list view model (QAbstractListModel, in fact). The usual way is to put your models up front in main (or somewhere) like this:
QQmlContext* ctxt = engine.rootContext();
ctxt->setContextProperty("myModel", &model);
I have one model per object in this case, so i needed a dynamic solution.
I have a QObject which creates my model for a list. The model created derives from QAbstractListModel. The model is created and given out by my QObject host with a Q_INVOKABLE.
First problem is that the type of the model so generated is not known and must be registered. The usual qmlRegisterType does not work because QAbstractListModels cannot be copied. so you must register with qmlRegisterUncreatableType.
That's the first bit. Now the model works BUT who destroys it?
Turns out both my C++ code and QML both try to destroy the object since ownership was implicitly given to QML as part of the Q_INVOKABLE accessor.
BUT just letting QML clean up was bad. I tracked when this happened and it didn't happen at all in a timely manner. Basically it wouldn't clean up unless I did quite radial things like resize the window. presumably, it would eventually clean up (garbage etc.) but i really wanted these dynamic models to be cleaned up when their host QObject goes out.
So ddriver's idea is the way. but also remember to register with qmlRegisterUncreatableType.
eg,
inline MyModel* MyHostObject::getModel()
{
if (!_model)
{
_model = new MyModel(this);
// retain ownership of this object.
QQmlEngine::setObjectOwnership(_model, QQmlEngine::CppOwnership);
}
return _model;
}

Change C++ model with QML

I want to extend the example called "Object ListModel Example" from Qt documentation
(you can get it on http://qt-project.org/doc/qt-4.8/declarative-modelviews-objectlistmodel.html).
I am trying to add a simple GUI functionality: a menu item that changes the content
(i.e. name) of the first data item in the model. Something like this:
MenuItem {
text: "Item 123"
onClicked: {
myModel.setProperty(0,"name","Item 123") //this gives me error
}
}
I am able to create a menu in QML but I cannot find the correct way to make changes in the model.
Btw, what is a difference between setContextProperty and qmlRegisterType (only the first one is used in this example but many other examples include the second one).
That kind of model is really not suitable for modification. There is no way for the view to be notified of changes. A better option is to use a QAbstractItemModel: http://qt-project.org/doc/qt-4.8/declarative-modelviews-abstractitemmodel.html
A simpler way to use a QAbstractItemModel is via QStandardItemModel: http://qt-project.org/doc/qt-4.8/qstandarditemmodel.html
setContextProperty() adds a single named property to the context. qmlRegisterType() registers a QObject-derived type with the QML engine, allowing it to instantiate that type. For example, the QDeclarativeItem type is registered with the engine as "Item", which is how the engine knows what to create when Item {} appears in QML code.

Configuring new document in MFC

When the user creates a new document in my SDI-application, I need to present a dialog specifying details on the document to be created (think: resolution, bit-depth, etc.) I initially put the code for displaying this dialog in OnNewDocument() (I don't need it when opening an existing document), but putting user-interface code in the document-class just doesn't feel right (also, I don't have any CWnd* to use as a parent for the dialog).
Is there a better place to do this in MFC?
You're right, the document class is no good place for UI.
CDocTemplate::[OpenDocumentFile][1](pszPath) looks like a better candidate:
pszPath==NULL means 'create a new document'.
The method is virtual -> Just derive CMySingleDocTemplate from CSingleDocTemplate and use an instance of this class in CMyWinApp::InitInstance().
This class is responsible for creating docs, frames and views, hence I think it's a good place to put a UI operation.
BOOL CMyWinApp::InitInstance()
{
...
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CMySingleDocTemplate( // <--Derives from CSingleDocTemplate
IDR_MAINFRAME,
RUNTIME_CLASS(CMyDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CMyView));
AddDocTemplate(pDocTemplate);
...
}
CDocument* CMySingleDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName,
BOOL bMakeVisible)
{
CDocument *pDoc =
CSingleDocTemplate::OpenDocumentFile(lpszPathName, bMakeVisible);
if (lpszPathName==NULL)
{
// GUI to get user info
// update doc
m_pOnlyDoc->Blah(input);
// update view
m_pOnlyDoc->UpdateAllViews(NULL,...,...);
}
}
This might not be ideal though: In SDI, there is one and only doc object. It's re-used accross File/Load and File/New operation.
This function will then be called a first time before the initial mainframe is created. You may not want to have a dialog presented to user before the frame is created. Ouch! It's a little more complicated:
Instead of popping up a GUI in in OpenDocumentFile(NULL) as above, just post a custom message/command to the main frame. Then add a handler that will react by the sequence pop up GUI/update doc/update views. That way, the main frame will be displayed before the GUI is popped up and your user will be happier.
This also solves your problem where you don't have a CWnd parent: the main frame is already created and your dialog will use it byt default.
BTW, another solution consists in adding a command handler for ID_FILE_NEW in your CMyWinApp's message map and add your own override of OnFileNew(). But when you write OnFileNew(), I believe you'll quickly find out that it's an ugly solution :-(