SwiftUI how to get reference to editing Textfield inside a List? - uitextfield

Is it possible to find a particular view in SwiftUI based on its tag ? Or is there another solution to my problem below ?
Based on string in a Textfield in a Row in List A, am populating search results in List B.
When a row on a List B is tapped, I want the textField on List A to be updated.
But the problem is I dont know how to get hold of the active textField or the index of the row in the List A.
Please refer this image for clarity
Im trying to emulate this behaviour, which is common in desktop. You enter text in a Textfield and either using mouse or up down arrow keys to select an option and the Textfield gets updated with that option.
Here instead of PopOver am using a separate List view.

In the code below, you can see how to accomplish most of the things you require.
You mention you want to know which field you are typing on. You can do that by using the onEdintingChanged closure. This closure receives a boolean value that indicates if the field became active or inactive. You can use it to set a variable like in the example: activeField.
Another of your requests, was being able to refresh the List with every keystroke. The onReceive modifier subscribes to the binding of the TextField and executes your closure for each. There is however, what I think it is a bug: When there is text in both fields, the closure executes for both fields at every keystroke. The if statement comparing self.activeField is there to prevent that.
So, now, from the closure you can trigger an update on your external model. Since your List should be bound to the same model, it will refresh automatically.
Another of your requirements was that tapping on the list should update your text field. That is simple. If your textfield is bound to the same model, you just update the corresponding model variable and the field will update.
I hope I've been clear.
struct ContentView : View {
#State private var field1 = ""
#State private var field2 = ""
#State private var activeField: Int = 0
var body: some View {
VStack {
TextField("", text: $field1, onEditingChanged: { if $0 { self.activeField = 0 } })
.textFieldStyle(.roundedBorder)
.onReceive(field1.publisher().last()) { if self.activeField == 0 { print("FIELD 1 -> \(self.field1): \($0)") } }
TextField("", text: $field2, onEditingChanged: { if $0 { self.activeField = 1 } })
.textFieldStyle(.roundedBorder)
.onReceive(field2.publisher().last()) { if self.activeField == 1 { print("FIELD 2 -> \(self.field2): \($0)") } }
}
}
}

Related

Dismissed view empties before animation starts when using SwiftUI navigation in combination with TCA

We are struggling with SwiftUI navigation in combination with TCA and I am wondering is someone else did encounter similar issue.
The problem is that when we set parameters isPresented or isActive on .sheet or NavigationLink to false to dismiss it, then all content seems to be replaced with empty view before the animation starts (see attached gif).
In the code we store state (boolean value) indicating if child view is presented in parent view. When button to go back on child view is tapped, then we catch this action in parent view and change the boolean value to false to dismiss child view. It works like a charm instead of navigation animation.
I would be endlessly happy for any help or suggestions.
I have a similar problem. My example is a bit more involved but arguably more generic. Hope it helps.
My TCA setup looks like this:
struct State: Equatable {
var child: ChildState?
// ...
}
One can get the child store by scoping the parent store:
let childStore = parentStore.scope { $0.child ?? ChildState() }
And the child view’s body looks like this:
var body: some View {
WithViewStore(childStore) { viewStore in
// ...
}
}
Here is what happened:
Some reducer sets parent.child = nil.
This notifies every scoped store that the root state changes.
Each scoped store then applies the scope function to get a new child state, and compares it with the previous one. More likely than not, we get a different child state. Some objectWillChange is called.
The corresponding WithViewStore is marked dirty.
The content closure in WithViewStore is called, and generates a new child view tree.
SwiftUI then generates an animation from the old child view tree to the new child view tree, in addition to the dismissal animation for the whole child view tree.
My solution is to kill step 3. First, let's extend the child state with another property:
struct ChildState: Equatable {
var isBeingDismissed: Bool = false
}
Second, when scoping, let's do:
let childStore = parentStore.scope { x -> ChildState in
if let child = x.child {
return child
}
var child = ChildState()
child.isBeingDismissed = true
return true
}
Lastly, we can modify the WithViewStore with a custom removeDuplicates closure:
var body: some View {
WithViewStore(childStore) { old, new in
if old == new {
return true
}
// Key
if new.isBeingDismissed {
return true
}
return false
} content: { viewStore in
// ...
}
}
Here is how it works. The scoped child state can be nil in two cases, when the view is first created and the view is being dismissed. In the first case, the old is nil. In the second case, the new is nil. The custom removeDuplicates essentially does this: when a child state is being dismissed, we skip any update. So the only animation being played is the transition animation. The content stays the same.

How to stop AttributeGraph being generated from Picker

I understand the cause here, but cannot find a straightforward solution. Can anyone see a simple solution that I am just not seeing ?
The cascade of AttributeGraph errors occurs when the focus is on one TextField and the picker is changed to the alternate field. The objective is to have a picker selectively enable certain fields within a form. This is just a simplistic case to demonstrate the issue.
I understand that removing the ".disable" will prevent the cascade of errors and that a TextField having focus is the root cause. I have not been able to find a way to remove focus from the TextField when the picker changes.
This on on MacOS 10.15 using Xcode 12.4. If this does not happen in later versions, then I will just have to live with it since I am stuck on 10.15 due to hardware limitation.
import SwiftUI
struct AttributeGraphIssueView: View {
private var choices = ["Choice 1","Choice 2"]
#State private var selection:String = "Choice 1"
#State private var entryForChoice1: String = "Hello"
#State private var entryForChoice2: String = "World"
var body: some View {
Picker( selection: $selection, label: EmptyView() ) {
ForEach (choices, id:\.self) { entry in
Text(entry)
}
}
TextField("for choice 1", text: $entryForChoice1)
.disabled(selection == choices[1])
.background((selection == choices[1]) ? Color.red: Color.green)
TextField("for choice 2", text: $entryForChoice2)
.disabled(selection == choices[0])
.background((selection == choices[0]) ? Color.red: Color.green)
}
}
So I have arrived at an ugly, but functional solution, which is to not render the TextField rather than disable them. The code for the TextField becomes
if (selection != choices[1]) {
TextField("for choice 1", text: $entryForChoice1)
.foregroundColor((selection == choices[1]) ? Color.red: Color.green)
}
Why ugly? Because this means that the static form has become dynamic. Perhaps a personal preference but I prefer fields in a form to stay put once rendered. I'd prefer a cleaner solution if anyone can see one.

In SwiftUI how can I add scroll to refresh functionality to List?

If I have a SwiftUI List that gets its values from my server via API call, such as:
var body: some View {
List {
ForEach(viewModel.items) { item in
ListItemsView(item: item)
}
}
.onAppear {
self.viewModel.getAllItems()
}
}
The array of items is stored in the #Published var items, in viewModel
The func .getAllItems() calls a server API which returns an array of items and stores them under the #Published var items
How can I add the functionality to swipe down on this List and call .getAllItems() again, in order to update items with new values, and theoretically re-render the List?
Or is there a much better way to do this that I'm not seeing?
In Xcode 13 and iOS 15+ you can use the new refreshable() modifier
List(events) { event in
Text(event.name)
}
.refreshable {
print("Get New Events")
}
Here's an implementation that adds a proper UIRefreshControl to a SwiftUI List's table view: https://github.com/timbersoftware/SwiftUIRefresh

ExtJS: Clear a data binding from a viewController

I am using the admin-dashboard template and made the following modification to the MainController:
routes: {
':node': {
before : 'checkSession',
action : 'onRouteChange'
}
},
Thus, when I change a route the before route will happen first, so the following method will first get called and then the routing will proceed onward:
checkSession : function() {
var args = Ext.Array.slice(arguments),
action = args[args.length - 1],
hash = window.location.hash;
// TODO: use hash to clear patient header when appropriate
if (hash.indexOf('#new-patient') > -1) {
console.log(hash);
}
action.resume();
},
This works splendidly. However, when I get the condition of hash.indexOf('#new-patient') > -1 I would like to clear a comboBox that I have defined in a view as:
bind: {
store: '{patients}'
},
reference: 'patientCombo',
This is from a view in the same scope as the above method from the viewController is, that is they are both in the hierarchy of the same viewModel. I am just not sure exactly how I can clear this from the above method. I just want to set the combo box back to no selected value (if one had been selected) and then update the bindings. Merci!
When I tried using Ext.ComponentQuery, I initially referenced the itemId, but the xtype was wrong (the combobox and displaytext, among other sundries are in a container). Since this is the only combobox in my app now, I just tried it as:
combo = Ext.ComponentQuery.query('combobox')[0]
if (combo){
combo.setValue('')
}
and, lo' and behold, it worked!

jqgrid change cell value and stay in edit mode

I'm using inline editing in my grid , I have some cases which i want to change the value of a cell inside a column. I'm changing it with setCell ,and it works good. my problem is that after the change the cell losts it's edit mode while all other cells of the row are in edit mode. I want to keep the cell in edit mode after i changed it.
for now what i did is saved the row and then selected it again and made in in edit mode - but i don't think it is a good solution - Is there a way to keep in edit mode while changin it?
Thank's In Advance.
If you need to implement the behavior of dependency cells which are all in the editing mode you have to modify the cell contain manually with respect of jQuery.html function for example. If the name of the column which you want to modify has the name "description", and you use 'blur' event on another "code" column then you can do about the following
editoptions: {
dataEvents: [
{
type: 'blur',
fn: function(e) {
var newCodeValue = $(e.target).val();
// get the information from any source about the
// description of based on the new code value
// and construct full new HTML contain of the "description"
// cell. It should include "<input>", "<select>" or
// some another input elements. Let us you save the result
// in the variable descriptionEditHtml then you can use
// populate descriptionEditHtml in the "description" edit cell
if ($(e.target).is('.FormElement')) {
// form editing
var form = $(e.target).closest('form.FormGrid');
$("#description.FormElement",form[0]).html(descriptionEditHtml);
} else {
// inline editing
var row = $(e.target).closest('tr.jqgrow');
var rowId = row.attr('id');
$("#"+rowId+"_description",row[0]).html(descriptionEditHtml);
}
}
}
]
}
The code will work for both inline and form editing.
The working example of dependent <select> elements you can find here.
blur does not fire if value is changed and enter is pressed immediately without moving to other cell. So better is to use
editrules = new
{
custom = true,
custom_func = function( val, col ) { ... }
},
and move this code from blur to custom_func as described in
jqgrid: how send and receive row data keeping edit mode

Resources