In Rubymotion how to get callback from dismissed view controller - rubymotion

On a table row click I reference a cell to present a view controller (to select from a list of images)
def open_selector
view_b = ImagesController.new #using rmq hence .new
##cell.superview.superview.controller.presentViewController view_b, animated:true, completion:nil
end
Inside the images controller - I dismiss when finished selecting - but how do I let cell know it was closed?
def collectionView(view, didSelectItemAtIndexPath: index_path)
self.dismissViewControllerAnimated(true, completion: lambda{})
end

I would suggest providing your UICollectionViewController a delegate so it can call back itself. So:
class MyCollectionViewController < UICollectionViewController
attr_writer :parent_controller
# ...
def collectionView(view, didSelectItemAtIndexPath: index_path)
self.dismissViewControllerAnimated(true,
completion: lambda{
#parent_controller.collection_did_close(self)
})
end
Assuming, you have a method called collection_did_close in the parent controller, it will be called with a reference to the collection view controller. Using that you can grab whatever information you need out of there before it gets garbage collected.

Related

How does the pattern of assigning `#State` in `onReceive` ensure that view state is in sync?

I'm having a little trouble with the following pattern which integrates Combine publishers into SwiftUI so that view state is updated when publishers emit:
struct ItemList: View {
var publisher: AnyPublisher<[Item], Never>
#State private var items = [Item]()
var body: some View {
List(items) { item in
ItemRow(item: item)
}
.onReceive(publisher) {
items = $0
}
}
}
Above example from Swift by Sundell
I feel like I'm missing something when I read it.
Let's assume you initialize items to the correct (at that time) value. What ensures that the published value won't change between the creation of ItemList and the first call to body, where it first starts listening to changes? Or if there is no such guarantee, then what else is preventing the view from ending up in the wrong initial state because of this?
Consider a NavigationLink:
NavigationLink(
destination: { ItemList(publisher: myPub) },
label: { Text("Show List") }
)
Here we have a case where SwiftUI creates the ItemList immediately, but doesn't ask the ItemList for its body until the user taps the link.
(How do we know it creates the ItemList immediately? The destination argument is not declared #escaping, so SwiftUI has to call it inside the NavigationLink initializer.)
So in fact there is a real risk in this case that items should change between when the ItemList is created and when it appears on screen.
We solve this by using a publisher like CurrentValueSubject that publishes its current value immediately to each new subscriber. That way, it doesn't matter how much later SwiftUI decides to use the view. As soon as SwiftUI uses the view, it subscribes to the publisher and immediately gets the current value. SwiftUI can handle that update before updating the framebuffer, so the user doesn't see a flash of incorrect data.
We need to read it in sequence:
State is initiailzed, supposing items = [Item1, Item2, Item3]
body is called to render view
List is constructed with current items, ie. List([Item1, Item2, Item3])
onReceive is called on constructed List of 3) and creates view around that list with subscriber to publisher
subscriber requests current events from publisher
if there are events in publisher then onReceive's closure handler is called (see below) otherwise no changes and List of 3) is shown on screen
6.1. if handler gets same initial [Item1, Item2, Item3] (subscriber extracts all available items) then state is not changed and List of 3) is shown on screen
6.2. if handler gets different items [ItemX, ItemY] then state change invalidates view and List is rebuilt with [ItemX, ItemY] which are shown on screen (there is no cycling because refresh is synchronous and we get into 6.1 at second pass).
That's simplified logic of provided code snapshot.

How to detect changes to a property of a core data object

I have this core data entity called Cake, that has properties like:
numberOfIngredientsBeingUsed
numberOfIngredientsTotal
color
weight
I read one object from the entity and now I have something like this
let orangeCake = Cake(type:"orange", context:coreDataContext)
Now I want to show a ProgressView. I want to pass the progress view, a binding numberOfIngredientsBeingUsed property, representing the current number of ingredients so far being used and a regular property numberOfIngredientsTotal, representing the total number of ingredients required to make the cake.
The idea is that when numberOfIngredientsBeingUsed changes on the main view, the progress view updates, because it is a binding property.
How do I do that? I cannot wrap my brain around, because numberOfIngredientsBeingUsed is a property of the cake object orangeCake.
CoreData managed objects are type of ObservableObject, so instead of passing one property you need to inject entire instance of Cake as ObservedObject and use its properties inside ProgressView, which will update (and refresh view) whenever corresponding object updated somewhere in other part of code.
So it should look like
struct ProgressView: View {
#ObservedObject var model: Cake
var body: some View {
Text("Progress: \(model.numberOfIngredientsBeingUsed) of \(model.numberOfIngredientsTotal)")
}
}

Creating Hashtags in swift

I am new to swift. I am trying to create a view where you can create hashtags. There is a UITextfield in which you type the word to be converted. On press of 'enter' or '#', it should automatically convert to hashtags and display in labels which are further stored in an array format.
I tried many tutorials but none of them seem to work.
UITextField's have a delegate that is pretty handy.
A really simple implementation would be to use the textFieldShouldReturn delegate method, you can use this to detect when the return button is pressed.
Tell your view controller that it is going to adopt the protocol like this:
class ViewController:UIViewController, UITextFieldDelegate {
...
Then tell your textfield where to delegate it's events, if you are making the textfield inside the view controller then use self to reference the view controller like this:
let textField = UITextField()
textField.delegate = self // IMPORTANT
self.view.addSubView(textField)
Then inside your view controller implement the textFieldShouldReturn method like so:
class ViewController:UIViewController, UITextFieldDelegate {
...
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let text = textField.text {
let hashtag = "#\(text)"
print("New hashtag is \(hashtag)")
}
return true // allows the default behaviour of return button, you can return false if you want the keyboard to remain on screen.
}
}
This solution does not account for the user entering more than one hashtag, nor does it remove white space etc.. you may need to think about formatting/validating this string before it is useable.
You may also want to consider looking at other existing questions that cover things like splitting strings and creating arrays.

in rubymotion how to manually invoke table method

I'm using a UICollectionViewController and I'm trying to set an item as selected automatically (ie. previously selected options). If I attempt to set when I'm setting the cells data
def collectionView(view, cellForItemAtIndexPath: index_path)
//create the cell etc.. then
cell.selected = true
cell.update(index_path.row)
end
I get NoMethodError for selected. However this works from the normal select/deselect methods.
def collectionView(view, didSelectItemAtIndexPath: index_path)
cell = view.cellForItemAtIndexPath(index_path)
cell.selected = true
end
Any ideas on how can I automatically preselect a cell?
Thanks,
To preselect a cell in a Collection View named myColView, call:
indexPath = NSIndexPath.indexPathForItem(1, inSection: 0)
myColView.selectItemAtIndexPath(indexPath, animated: false, scrollPosition: 0)
This method does not cause any selection-related delegate methods to be called.

Empty space between navigationbar and view

I'm experiencing this weird behaviour once I initiate an application root controller with a UINavigationController
On first launch, there is an empty space between the navigationbar and first viewcontroller controller.
but the full content is displayed after I swtiched to another view and back to the first one.
Is something wrong with this?
tab_bar_controller = RootViewController.alloc.initWithNibName(nil, bundle:nil)
#window.rootViewController = UINavigationController.alloc.initWithRootViewController(tab_bar_controller)
Thanks for your help.
It is not considered "proper" to put a UITabBarController inside a UINavigationController:
UINavigationController#initWithRootViewController ... rootViewController:
The view controller that resides at the bottom of the navigation stack.
This object cannot be an instance of the UITabBarController class.
The opposite - a UINavigationController as one of the UITabBarController child view controllers - is allowed.
nav_controller = RootViewController.alloc.initWithNibName(nil, bundle:nil)
#window.rootViewController = UITabBarController.alloc.init
#window.rootViewController.viewControllers = [nav_controller]
Even if you did get this figured out, your app would ultimately be rejected.
Try placing the content in the viewWillAppear callback.