UITextView return NO when I call becomeFirstResponder in a nested modelView - uitextview

I have UITextView in a ViewController Post which has a TextView CommentText, When I present as a model view that "Post" from primary viewcontroller the textView can be edited properly.
Now I have another viewcontroller "Comments" and when I present "Comments" as a model view from MainController and then Present the "Post" from "Comments" as modelview it show the "Post'View but I can't edit the TextView, but If I present the "Post" once from primary ViewController and then I show it from "Comments" it worked properly. I checked that TextView is not null/nil in any case.
Can anyone know where is the problem?

Related

#StateObject for a NSManagedObject without context not publishing changes

Like in a lot of apps, I have a list of items (populated by a Core Data fetch request), a sheet to create new items, and a sheet to edit an item when tapping on a row in my list. I'm trying to unify both forms to create and edit an update, and put the cancel / save logic in a superview of the form.
So I've something like this:
ListView: a list with row populated by a Core Data fetch request
AddView: a NavigationView with the FormView embed + cancel and save button
EditView: a NavigationView with the FormView embed + cancel and save button
FormView: a TextField to update the name of the item
In the init() for the AddView, I create a new NSManagedObject without any context (I do that because I don't want my ListView to be updated when I create a new item in the AddView, but only when I save this item -> alternative could be to use a child context, or filter the fetch request results based on the isInserted or objectID.isTemporaryID of the return objects). AddView contains a NavigationView with the FormView embed, a cancel button, and a save button. This save button is disabled based on a computed property on the managed object (name for the object can't be nil).
In the EditView, I pass the item that was tapped from the ListView. This item is an existing NSManagedObject attached to the main viewContext of the app (coming from the fetch request of the ListView). EditView contains a NavigationView with the FormView embed, a cancel button and a save button (exactly like the AddView). This save button is also disabled based on the same computed property.
My issue is that when I update the name of the item from the TextField in my FormView, the condition to enable / disable the save button is not working for the AddView (this AddView is actually not refreshed when I change the item name from the FormView) but working for the EditView (this EditView is refreshed when I change the item name from the FormView). If I attach a context to the new NSManagedObject in the init() of the AddView, the condition is working like in the EditView.
So it appears that a NSManagedObject without any context is not observed by SwiftUI? Am I missing anything or is that a bug?
I wouldn't be surprised (but haven't verified) if the change-notifying ability of a managed object depends on the presence of the context. I can't think of a situation where you'd want to create a managed object without a context.
You should use a child context. The context does a lot of work for you in Core Data (managing relationships, probably change notifying, validation etc), and offers a simple way to cancel / save changes - just save the child context and the data flows back up into the main context, or discard the context to abandon.
A workaround to get the change notifications is to add this override to the NSManagedObject subclass:
override public func willChangeValue(forKey key: String) {
super.willChangeValue(forKey: key)
self.objectWillChange.send()
}
NSManagedObject could be subclassed to add this override (more info here and here)
We can be more specific on the update value if we don't want to trigger the change for every key. This will also work for relationships (not the case for the above solution).
func setName(_ name: String) {
objectWillChange.send()
self.name = name
}
In this case, my AddView updates even if the observed object does not have a context (change notifications are probably trigger only when a context exists for the object). The save button is disabled / enabled based on the following computed property in my NSManagedObject subclass.
var canBeSaved: Bool {
if self.name.isEmpty {
return false
} else {
return true
}
}

Editing CoreData and view's update

I'm having trouble updating my view after editing CoreData.
I have this entry in my CoreData entity named "TST" and through a NavigationLink I'm editing it's name
NavigationLink(destination: editingPage(thread: tr.title)){
Text(tr.title)}
#State var thread : String
Form {
TextField("thread", text:$thread)
}.onDisappear{
do {
try self.managedObjectContext.save()
} catch {
print(error.localizedDescription)
}
}
after editing and saving the MOC It doesn't display the changes until I either restart the app, or go back to the beginning of my navigation View.
Should I have a fetch request on all page displaying CoreData's data? or having some kind of ObservedObject ?
What I'm looking for is the same as changing your device's name in the device settings menu.
Thanks
Tim

Adding Excel upload feature to Employee Timecard Entry doesn't show ProjectTask field

I have a customization to the Employee Timecard Entry screen (EP305000) which enables the Excel upload functionality into the Details tab grid. I did this by adding the attribute [PXImport(typeof(EPTimeCard))] to the 'Activities' view re-declaration in a TimeCardMaint BLC extension as follows:
[PXImport(typeof(EPTimeCard))]
[PXViewName(PX.Objects.EP.Messages.TimeCardDetail)]
public PXSelectJoin<EPTimecardDetail,
InnerJoin<CREmployee,
On<CREmployee.userID, Equal<EPTimecardDetail.ownerID>>,
LeftJoin<CRActivityLink,
On<CRActivityLink.noteID, Equal<EPTimecardDetail.refNoteID>>,
LeftJoin<CRCase,
On<CRCase.noteID, Equal<CRActivityLink.refNoteID>>,
LeftJoin<PX.Objects.AR.Customer,
On<PX.Objects.AR.Customer.bAccountID, Equal<CRCase.customerID>>,
LeftJoin<PX.Objects.EP.TimeCardMaint.ContractEx,
On<PX.Objects.EP.TimeCardMaint.ContractEx.contractID, Equal<CRCase.contractID>>,
LeftJoin<PMProject,
On<PMProject.contractID, Equal<EPTimecardDetail.projectID>>>>>>>>,
Where<CREmployee.bAccountID, Equal<Current<EPTimeCard.employeeID>>,
And<EPTimecardDetail.weekID, Equal<Current<EPTimeCard.weekId>>,
And<EPTimecardDetail.trackTime, Equal<True>,
And<EPTimecardDetail.approvalStatus, NotEqual<ActivityStatusListAttribute.canceled>,
And<Where<EPTimecardDetail.timeCardCD, IsNull, Or<EPTimecardDetail.timeCardCD, Equal<Current<EPTimeCard.timeCardCD>>>>>>>>>,
OrderBy<Asc<EPTimecardDetail.date>>> Activities;
I also set the 'AllowImport' property of the grid to 'True'. This seems to work ok, except that the 'ProjectTask' field of the upload does not allow mapping - i.e., if you go through the import process, when you get to the field mapping part, you can't map the Excel field for ProjectTask to the grid's ProjectTask. It just doesn't show up.
Would this be because the source BLC has as delegate method for 'activities' that I didn't reproduce in my extension?
What could be the reason for not allowing mapping to the ProjectTask field?
Since the ProjectTask field is disabled by default, this was solved by adding a parameter to the [ProjectTask] attribute, called "AlwaysEnabled" via the CacheAttached event, as shown below:
public class TimeCardMaint_Extension : PXGraphExtension<TimeCardMaint>
{
[PXDefault(typeof(Search<PMTask.taskID, Where<PMTask.projectID, Equal<Current<TimeCardMaint.EPTimecardDetail.projectID>>, And<PMTask.isDefault, Equal<True>>>>), PersistingCheck = PXPersistingCheck.Nothing)]
[ProjectTask(typeof(TimeCardMaint.EPTimecardDetail.projectID),
BatchModule.TA,
DisplayName = "Project Task",
BqlField = typeof(PMTimeActivity.projectTaskID),
AlwaysEnabled = true)]
protected virtual void EPTimecardDetail_ProjectTaskID_CacheAttached(PXCache cache)
{
}

How to create layout elements in Orchard 1.9

Can someone please guide me on how to create layout elements in Orchard 1.9. I couldn't find any resource online.
In general, creating a new layout element is similar to creating a new part. There is a driver and a few views involved in the process. To the point - you need to implement as follows:
An element class.. Class that inherits from Element, which contains all the element data. A model, so to speak.
A driver. Class that inherits from ElementDriver<TElement>, where TElement is the type you created above. Each element has it's own driver that handles displaying admin editor (and the postback) and frontend display views.
Shapes. All shapes should be placed under /Views/Elements/ folder, by convention.
Display shape. Named after your element, ie. MyElement.cshtml. This one renders your element on frontend.
Design display shape.. Named after your element, with .Design suffix, ie. MyElement.Design.cshtml. This one renders your element inside the layout editor.
Editor shape.. This one should be put in /Views/EditorTemplates/ folder instead. Default naming convention is Elements.MyElement.cshtml. It renders the editor shown when you drop a new element on layout editor canvas.
With all above done, your new element should appear in the list of elements on the right side of the layout editor, ready to use.
If you want to do some more complex elements, please check the existing implementations. Layouts module has a very decent architecture so you should get up to speed pretty quickly. Just keep in mind the necessary steps I wrote above.
To create a custom layout element first create a class that inherits from Element. Element is found in the Orchard.Layouts namespace so you need to add a reference. To follow Orchard standards put this file in a folder called Elements.
public class MyElement : Element
{
public override string Category
{
get { return "Content"; }
}
public string MyCustomProperty
{
get { return this.Retrieve(x => x.MyCustomProperty); }
set { this.Store(x => x.MyCustomProperty, value); }
}
}
Next, create a driver class in a folder called Drivers. This class inherits from ElementDriver<TElement> and likely you will want to override the OnBuildEditor and OnDisplaying methods. OnBuildEditor is used for handling creating our editors shape and updating our database when the editor is saved. OnDisplaying is used when we need to do things when displaying our element. Oftentimes, you will want to add properties to the shape which can be done with context.ElementShape.MyAdditionalProperty = "My Value";
public class MyElementDriver : ElementDriver<MyElement>
{
protected override EditorResult OnBuildEditor(MyElement element, ElementEditorContext context)
{
var viewModel = new MyElementEditorViewModel
{
MyCustomProperty = element.MyCustomProperty
};
var editor = context.ShapeFactory.EditorTemplate(TemplateName: "Elements.MyElement", Model: viewModel);
if (context.Updater != null)
{
context.Updater.TryUpdateModel(viewModel, context.Prefix, null, null);
element.MyCustomProperty = viewModel.MyCustomProperty;
}
return Editor(context, editor);
}
protected override void OnDisplaying(Reddit element, ElementDisplayContext context)
{
context.ElementShape.MyAdditionalProperty = "My Value";
}
}
We then just need our views. Our editor view goes into Views/EditorTemplates. The file name needs to be what we set the template name of the editor shape. In our case the view name will be Elements.MyElement.cshtml.
#model MyNameSpace.ViewModels.MyElementEditorViewModel
<fieldset>
<div>
#Html.LabelFor(m => m.MyCustomProperty, T("My Custom Property"))
#Html.TextBoxFor(m => m.MyCustomProperty, new { #class = "text medium" })
</div>
</fieldset>
Finally, we just need a view for our frontend. This view goes into the following folder Views/Elements. The name of the view file is the same as our element class name. For this example the file would be called MyElement.cshtml.
#using MyNameSpace.Elements
#using MyNameSpace.Models
#{
var element = (MyElement)Model.Element;
}
<h1>#element.MyCustomProperty</h1>
You will then have a new element that you can drag into your layout with the layout editor.
For more details on creating an element from start to finish check out my blog post on creating a Reddit element.

How To Add Monotouch Dialog to FlyoutNaviagtionController

I'm a noob to monotouch and can't manage to add table views to flyoutnavigation controller. Does anyone have an example?
** update
My setup is a bit complicated and after much hacking managed to get the static table cells displayed on my storyboard with the below config.
tab bar controller
|
-tab 1
|
-tab 2 - nav controller
|
uiview controller A - flyout nav A --> uiview controller A1...A3
|
uiview controller B - flyout nav B --> tableview controller B1 (static cells)
I've cast the tableview controllers as uiview controllers on the flyout navs' setup thus allowing the tableviewcontroller to be displayed correctly and navigated to main uiview controllers via sergues. For example in the flyouts setup:
NavigationRoot = new RootElement ("FlyoutNavigationB") {
new Section ("Pages") {
new StringElement ("A Table View with static cells")
}
},
ViewControllers = new [] {
TableViewControllerB1 as UIViewController,
},
This hack seems to work but cleaner solutions are warmly invited (as I'm only 2weeks dev experience with monotouch) and feel a little saddened that I have uiview controllers sitting unattached on the storyboard without ability to do sergues via the flyout nav. It almost behaves like old school xib development.
Anyway I'm experimenting with tying this upto monotouch dialog now but not yet sure how. Comments most welcome to aid my learning :P
* update
Ok I think I got it working by creating a subclassed dialogviewcontroller. Then passing this subclass instance into the ViewControllers array in flyoutnavigationcontroller like so:
public partial class MyDvcController : DialogViewController
{
public MyDvcController (UINavigationController nav): base (UITableViewStyle.Grouped, null)
{
navigation = nav;
Root = new RootElement ("Demos"){
new Section ("Element API"){
new StringElement ("iPhone Settings Sample", DemoElementApi),
}
};
}
}
In my calling flyoutnav controller:
// Supply view controllers corresponding to menu items:
ViewControllers = new [] {
new UIViewController { View = new UILabel { Text = "Address txt" }, Title = "Address" },
new MyDvcController(NavigationController) as UIViewController
}
My wish list is almost complete...now how to add custom upickerviews with lovely transparent input accessory views wired to inputviews so they show automatically from calling monotouch dialog elements...hmmmm...another quest awaits...
Are you trying to push a second list over inside the flyout? I couldn't get it to work either.
I got it to work by modifying the source and redoing how the selection list is setup--but those changes are on my machine at work. I'll try to push those when I have a chance.

Resources