SwiftUI - Unable to simultaneously satisfy constraints - layout

I am really new to SwiftUI. Did many lessons of the course of Angela. I checked the problem with the same error message but I have the feeling that these threads are related to SWIFT and not to SwiftUI. I don't understand which constraint I should skip when using only VStack or NavigationView...
I want to switch from a general view (my starting view) via some buttons to different areas of the app.
Start-View:
Button 1
Member-View (List View)
Member View Details
Button 2
Club View (List View)
Club Details
My code is reduced to the following:
import SwiftUI
struct StartMenueView: View {
#State private var selection: String? = nil
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Detail View"), tag: "A", selection: $selection) { EmptyView() }
HStack {
Button("Detail View") {
selection = "A"
}
}
.navigationTitle("Master")
}
}
}
}
And the error message which appears when running the app.
2022-02-09 19:35:42.884859+0100 SwiftUIByExample[20911:419354] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x600003fd4640 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x156f17750]-(6)-[_UIModernBarButton:0x156f151e0'Master'] (active)>",
"<NSLayoutConstraint:0x600003fd4690 'CB_Trailing_Trailing' _UIModernBarButton:0x156f151e0'Master'.trailing <= _UIButtonBarButton:0x156f14e00.trailing (active)>",
"<NSLayoutConstraint:0x600003fd5360 'UINav_static_button_horiz_position' _UIModernBarButton:0x156f17750.leading == UILayoutGuide:0x6000025e1500'UIViewLayoutMarginsGuide'.leading (active)>",
"<NSLayoutConstraint:0x600003fd53b0 'UINavItemContentGuide-leading' H:[_UIButtonBarButton:0x156f14e00]-(6)-[UILayoutGuide:0x6000025e1260'UINavigationBarItemContentLayoutGuide'] (active)>",
"<NSLayoutConstraint:0x600003fcf200 'UINavItemContentGuide-trailing' UILayoutGuide:0x6000025e1260'UINavigationBarItemContentLayoutGuide'.trailing == _UINavigationBarContentView:0x156f13970.trailing (active)>",
"<NSLayoutConstraint:0x600003ff7250 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x156f13970.width == 0 (active)>",
"<NSLayoutConstraint:0x600003fcf0c0 'UIView-leftMargin-guide-constraint' H:|-(8)-[UILayoutGuide:0x6000025e1500'UIViewLayoutMarginsGuide'](LTR) (active, names: '|':_UINavigationBarContentView:0x156f13970 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600003fd4640 'BIB_Trailing_CB_Leading' H:[_UIModernBarButton:0x156f17750]-(6)-[_UIModernBarButton:0x156f151e0'Master'] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2022-02-09 19:35:42.885452+0100 SwiftUIByExample[20911:419354] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x600003fd8780 UIView:0x156e0f510.trailing == _UIBackButtonMaskView:0x156e0edb0.trailing (active)>",
"<NSLayoutConstraint:0x600003fd4c30 'Mask_Trailing_Trailing' _UIBackButtonMaskView:0x156e0edb0.trailing == _UIButtonBarButton:0x156f14e00.trailing (active)>",
"<NSLayoutConstraint:0x600003fd4d70 'MaskEV_Leading_BIB_Trailing' H:[_UIModernBarButton:0x156f17750]-(0)-[UIView:0x156e0f510] (active)>",
"<NSLayoutConstraint:0x600003fd5360 'UINav_static_button_horiz_position' _UIModernBarButton:0x156f17750.leading == UILayoutGuide:0x6000025e1500'UIViewLayoutMarginsGuide'.leading (active)>",
"<NSLayoutConstraint:0x600003fd53b0 'UINavItemContentGuide-leading' H:[_UIButtonBarButton:0x156f14e00]-(6)-[UILayoutGuide:0x6000025e1260'UINavigationBarItemContentLayoutGuide'] (active)>",
"<NSLayoutConstraint:0x600003fcf200 'UINavItemContentGuide-trailing' UILayoutGuide:0x6000025e1260'UINavigationBarItemContentLayoutGuide'.trailing == _UINavigationBarContentView:0x156f13970.trailing (active)>",
"<NSLayoutConstraint:0x600003ff7250 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x156f13970.width == 0 (active)>",
"<NSLayoutConstraint:0x600003fcf0c0 'UIView-leftMargin-guide-constraint' H:|-(8)-[UILayoutGuide:0x6000025e1500'UIViewLayoutMarginsGuide'](LTR) (active, names: '|':_UINavigationBarContentView:0x156f13970 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600003fd8780 UIView:0x156e0f510.trailing == _UIBackButtonMaskView:0x156e0edb0.trailing (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2022-02-09 19:35:42.889483+0100 SwiftUIByExample[20911:419354] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x600003fd45f0 'BIB_Leading_Leading' H:|-(0)-[_UIModernBarButton:0x156f17750] (active, names: '|':_UIButtonBarButton:0x156f14e00 )>",
"<NSLayoutConstraint:0x600003fd5360 'UINav_static_button_horiz_position' _UIModernBarButton:0x156f17750.leading == UILayoutGuide:0x6000025e1500'UIViewLayoutMarginsGuide'.leading (active)>",
"<NSLayoutConstraint:0x600003fd53b0 'UINavItemContentGuide-leading' H:[_UIButtonBarButton:0x156f14e00]-(6)-[UILayoutGuide:0x6000025e1260'UINavigationBarItemContentLayoutGuide'] (active)>",
"<NSLayoutConstraint:0x600003fcf200 'UINavItemContentGuide-trailing' UILayoutGuide:0x6000025e1260'UINavigationBarItemContentLayoutGuide'.trailing == _UINavigationBarContentView:0x156f13970.trailing (active)>",
"<NSLayoutConstraint:0x600003ff7250 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x156f13970.width == 0 (active)>",
"<NSLayoutConstraint:0x600003fcf0c0 'UIView-leftMargin-guide-constraint' H:|-(8)-[UILayoutGuide:0x6000025e1500'UIViewLayoutMarginsGuide'](LTR) (active, names: '|':_UINavigationBarContentView:0x156f13970 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600003fd45f0 'BIB_Leading_Leading' H:|-(0)-[_UIModernBarButton:0x156f17750] (active, names: '|':_UIButtonBarButton:0x156f14e00 )>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

Add this line to NavigationView will solve the issue.
.navigationViewStyle(.stack)
Source:
https://www.hackingwithswift.com/forums/100-days-of-swiftui/unable-to-simultaneously-satisfy-constraints-warning-when-adding-a-navigation-title/12883.

Related

Is it possible to map CDialog RADIO controls to an enum class object instead of int?

I have a standard dialog resource which has some radio controls on it.
At the moment it is all done the normal way so the first radio is mapped to a int variable.
DDX_Radio(pDX, IDC_RADIO_DISPLAY_EVERYONE, m_iDisplayMode);
DDX_Radio(pDX, IDC_RADIO_SELECT_EVERYONE, m_iSelectMode);
Here is the thing ... I have these associated enumerations:
enum class DisplayMode { Everyone = 0, Brother, Sister };
enum class SelectMode { Everyone = 0, Elders, MinisterialServants, Appointed, Custom, None };
Therefore, whenever I need to do some comparisons of the mapped variable I have to do it like this:
Example 1:
m_iDisplayMode = to_underlying(DisplayMode::Everyone);
m_iSelectMode = to_underlying(SelectMode::None);
Example 2:
if (m_iDisplayMode == to_underlying(DisplayMode::Everyone))
bInclude = true;
else if (m_iDisplayMode == to_underlying(DisplayMode::Brother) && mapPublisher.second.eGender == Gender::Male)
bInclude = true;
else if (m_iDisplayMode == to_underlying(DisplayMode::Sister) && mapPublisher.second.eGender == Gender::Female)
bInclude = true;
The to_underlying function is a helper function that has been suggested previously to me here on SO and has been invaluable:
template <typename E>
constexpr auto to_underlying(E e) noexcept
{
return static_cast<std::underlying_type_t<E>>(e);
}
What I want to know is whether it is possible to map those radio controls directly to the DisplayMode or SelectMode objects? So instead of mapping to 1 etc. it maps to DisplayMode::Everyone etc. This would simplfy the code in this context and avoid the need for all the to_underlying calls.
This is the MFC source for DDX_Radio:
void AFXAPI DDX_Radio(CDataExchange* pDX, int nIDC, int& value)
// must be first in a group of auto radio buttons
{
pDX->PrepareCtrl(nIDC);
HWND hWndCtrl;
pDX->m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl);
ASSERT(::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP);
ASSERT(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON);
if (pDX->m_bSaveAndValidate)
value = -1; // value if none found
// walk all children in group
int iButton = 0;
do
{
if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
{
// control in group is a radio button
if (pDX->m_bSaveAndValidate)
{
if (::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)
{
ASSERT(value == -1); // only set once
value = iButton;
}
}
else
{
// select button
::SendMessage(hWndCtrl, BM_SETCHECK, (iButton == value), 0L);
}
iButton++;
}
else
{
TRACE(traceAppMsg, 0, "Warning: skipping non-radio button in group.\n");
}
hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT);
} while (hWndCtrl != NULL &&
!(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP));
}
I am trying to use the code in the answer but get this error:
MFC supports mapping between data (class members) and UI state. The standard mechanism is called Dialog Data Exchange (DDX) which the code in the question is using already (DDX_Radio). The data exchange is two-way, triggered by a call to UpdateData, where an argument of TRUE translates the UI state into values, and FALSE reads the associated values and adjusts the UI appropriately.
There are a number of standard dialog data exchange routines provided by MFC already, but clients can provide their own in case none of them fit the immediate use case. The question falls into this category, and conveniently provides the implementation of DDX_Radio to serve as a starting point.
The implementation looks a fair bit intimidating, though things start to make sense once the code has been augmented with a few comments here and there:
CustomDDX.h:
template<typename E>
void AFXAPI DDX_RadioEnum(CDataExchange* pDX, int nIDC, E& value)
{
// (1) Prepare the control for data exchange
pDX->PrepareCtrl(nIDC);
HWND hWndCtrl;
pDX->m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl);
// (2) Make sure this routine is associated with the first
// radio button in a radio button group
ASSERT(::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP);
// And verify, that it is indeed a radio button
ASSERT(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON);
// (3) Iterate over all radio buttons in this group
using value_t = std::underlying_type_t<E>;
value_t rdbtn_index {};
do {
if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON) {
// (4) Control is a radio button
if (pDX->m_bSaveAndValidate) {
// (5) Transfer data from UI to class member
if (::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0) {
value = static_cast<E>(rdbtn_index);
}
} else {
// (6) Transfer data from class member to UI
::SendMessage(hWndCtrl, BM_SETCHECK,
(static_cast<E>(rdbtn_index) == value), 0L);
}
++rdbtn_index;
} else {
// (7) Not a radio button -> Issue warning
TRACE(traceAppMsg, 0,
"Warning: skipping non-radio button in group.\n");
}
// (8) Move to next control in tab order
hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT);
}
// (9) Until there are no more, or we moved to the next group
while (hWndCtrl != NULL && !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP));
}
This declares a function template that can be instantiated for arbitrary scoped enum types, and implements the logic to translate between UI state and enum value. The integral underlying value of the enum serves as the zero-based index into the radio button group selection.
The implementation needs a bit of explanation, though. The following list provides a bit more information regarding the numbered // (n) code comments:
This initializes internal state used by the framework. The precise details aren't very important, as long as the correct function is called. There are 3 implementations, one for OLE controls, one for edit controls, and one for every thing else. We're in the "everything else" category.
Perform sanity checks. This verifies that the control identified by nIDC is the first control in a radio button group (WS_GROUP), and that it is indeed a radio button control. This helps weed out bugs early when running a debug build.
Initialize the radio button index counter (rdbtn_index), and start iterating over radio buttons.
Make sure the control we're operating on in this iteration is a radio button control (if not, see 7.).
When translating UI state back to member variables, verify whether the current control is checked, and store its index in the group as a scoped enum value.
Otherwise (i.e. when translating data to UI state) set the check mark if the numeric value of the enum matches the control index, and uncheck it otherwise. The latter is not strictly required when using BS_AUTORADIOBUTTON controls, but it's not harmful either.
If we encounter a control that isn't a radio button control, issue a warning. Closely watch the debug output for this message; it designates a bug in the dialog template. Make sure to set the WS_GROUP style on the first control following this radio button group (in tab order).
Move on to the next control in tab order.
Terminate the loop if either there is no trailing control, or the control starts a new group, designated by the WS_GROUP style.
That's a fair bit to digest. Luckily, use of this function template is far less cumbersome. For purposes of illustration, let's use the following scoped enums:
enum class Season {
Spring,
Summer,
Fall,
Winter
};
enum class Color {
Red,
Green,
Blue
};
and add the following class members to the dialog class:
private:
Season season_ {};
Color color_ { Color::Green };
All that's left is setting up the DDX associations, i.e.:
void CRadioEnumDlg::DoDataExchange(CDataExchange* pDX) {
CDialogEx::DoDataExchange(pDX);
DDX_RadioEnum(pDX, IDC_RADIO_SPRING, season_);
DDX_RadioEnum(pDX, IDC_RADIO_RED, color_);
}
(with CRadioEnumDlg deriving from CDialogEx). All the template machinery is neatly hidden, with the template type argument getting inferred from the final argument.
For completeness, here is the dialog template used:
IDD_RADIOENUM_DIALOG DIALOGEX 0, 0, 178, 107
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,59,86,50,14
PUSHBUTTON "Cancel",IDCANCEL,121,86,50,14
CONTROL "Spring",IDC_RADIO_SPRING,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,7,7,39,10
CONTROL "Summer",IDC_RADIO_SUMMER,"Button",BS_AUTORADIOBUTTON,7,20,39,10
CONTROL "Fall",IDC_RADIO_FALL,"Button",BS_AUTORADIOBUTTON,7,33,39,10
CONTROL "Winter",IDC_RADIO_WINTER,"Button",BS_AUTORADIOBUTTON,7,46,39,10
CONTROL "Red",IDC_RADIO_RED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,54,7,39,10
CONTROL "Green",IDC_RADIO_GREEN,"Button",BS_AUTORADIOBUTTON,54,20,39,10
CONTROL "Blue",IDC_RADIO_BLUE,"Button",BS_AUTORADIOBUTTON,54,33,39,10
END
as well as its accompanying resource.h:
#define IDD_RADIOENUM_DIALOG 102
#define IDC_RADIO_SPRING 1000
#define IDC_RADIO_SUMMER 1001
#define IDC_RADIO_FALL 1002
#define IDC_RADIO_WINTER 1003
#define IDC_RADIO_RED 1004
#define IDC_RADIO_GREEN 1005
#define IDC_RADIO_BLUE 1006
Adjusting a default-generated MFC application (dialog-based) with the above produces the following result when launched:
That's sweet, actually. Note in particular that the second row of radio buttons has the second item checked, which matches the initial value set in the dialog class' implementation (Color color_ { Color::Green }).
So all's good then?
Well, yeah. I guess. Sort of, anyway. Let's talk about the things that aren't quite as cool, things to watch out for, and problems that simply don't have a solution.
The implementation provided above makes a number of assumptions, none of which can be verified at compile time, and only some of them can (and are) verified at run time:
Enum values need to be backed by integral values, starting at 0, and counting up without any gaps. To my knowledge, there's no way to enforce this today (C++20), and the most effective way to ensure this is a code comment.
The order of enum values must match the tab order of radio button controls. Again, this is nothing that can be enforced nor verified.
The control ID specified in the DDX_RadioEnum call must be the start of a radio button group. This is verified at run time (the first ASSERT).
The control ID specified in the DDX_RadioEnum call must identify a radio button control. Again, this is verified at run time (the second ASSERT).
The first control following the radio button group (in tab order) must have the WS_GROUP style set. This is verified at run time, in part. If the control following is not a radio button control, a warning is issued. If the control happens to be a radio button, then this is not something that can be verified.
Those assumptions certainly aren't impossible to match. The hard part is keeping those invariants valid over time. If you can, then this implementation is worth a try.

FLTK: Fl_Value_Input subclass does not receive FL_KEYDOWN events (only FL_KEYUP)

I subclassed FL_Value_Input such that I can give the widget a color when it is modified, but the user has not yet pressed enter.
The silly thing is that the handle(int e) function is never invoked in case the event is a FL_KEYDOWN event, other events (such as FL_KEYUP, FL_DRAG, FL_FOCUS etc) are being propagated fine.
The widget is part of a widget hierarchy... Could it be that one of its parents in this hierarchy is absorbing this specific FL_KEYDOWN event?
EDIT: apparently, the widget also doesn't have focus (tested by comparing this to Fl::focus()), which is odd, as I am typing into it.
Any help would be appreciated.
Fx_Value_Input::Fx_Value_Input(int x, int y, int w, int h, const char* l)
: Fl_Value_Input(x, y, w, h, l)
{}
int Fx_Value_Input::handle(int e)
{
int r = Fl_Value_Input::handle(e);
if (e == FL_KEYBOARD)
{
if ((Fl::event_key() != FL_Enter && Fl::event_key() != FL_KP_Enter ) )
color(Fx::get_modified_color());
else if ((Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter) && color() == Fx::get_modified_color())
color(FL_WHITE);
redraw();
}
return r;
}
Using FL_KEYDOWN is the Windows way of doing things, which, as you have found, doesn't work because you need to subclass Fl_Input_: not Fl_Input and even then, it doesn't work properly.
Instead, use when(FL_WHEN_CHANGED|FL_WHEN_ENTER_KEY). Check changed() when keys are pressed and clear_changed(). It isn't set when the enter key is pressed. You can change colours back when that condition is met.
inputkey = new Fl_Input...
inputkey->when(FL_WHEN_CHANGED| FL_WHEN_ENTER_KEY);
inputkey->callback(cb);
....
void cb(Fl_Widget *ob)
{
Fl_Input* ip = dynamic_cast<Fl_Input*>(ob);
if (ip->changed())
{
// change colour
ip->clear_changed();
}
else
{
// change colour
}
}
Got a reply on the FLTK Google group, explaining the issue... There is no easy workaround for this...
FLTK delivers key strokes directly to the focus widget. Fl_Value_Input
includes an Fl_Input widget, and that widget becomes the focus widget when you click in the input field.
Hence the "parent" Fl_Value_Input never sees the Fl_KEYDOWN events.
Note that I quoted "parent" because Fl_Value_Input is a very
special case. It's not derived from Fl_Group, but it behaves somewhat
like an Fl_Group widget.
So why does your derived widget get FL_KEYUP events? That's another
feature of FLTK: FL_KEYUP events are also delivered to the focus
widget in the first place. However, AFAICT no core FLTK widget
handles FL_KEYUP events - they all return 0 in their handle()
methods. If the focus widget doesn't handle an event FLTK tries to
deliver it to the parent widget and then up through the widget
hierarchy until it reaches the top level window. Hence (and this is
IMHO weird) eventually all widgets will receive all FL_KEYUP events
unless one widget returns 1 from its handle() method (which is
usually not the case). Depending on the depth of the focus widget in
the total widget hierarchy it may be that some widgets get the same
FL_KEYUP event multiple times, because all parent groups of the focus
widget try to deliver the event to all their children.

Drools update nested member attribute

I'm facing a problem while using Drools.
I try to update an attribute from a nested member. The update seems to work, but the when clause do not consider it.
I have 2 Obj object, sharing the same Cpt object.
Cpt cpt = new Cpt();
Obj obj1 = new Obj("obj1");
obj1.setComposant("R2");
obj1.counter = cpt;
Obj obj2 = new Obj("obj2");
obj2.setComposant("R2");
obj2.counter = cpt;
kSession.insert(obj2);
kSession.insert(obj1);
My rule is define as:
rule "R2"
when
m : Obj(composant == "R2" && counter.value == 0)
then
System.out.println(m.getName() + " " + m.getCounter().getValue());
m.getCounter().increment();
end
I was expecting Obj1 to match the when clause, then update the value of the counter (from 0 to 1). So the Obj2 should not match the where clause.
But in fact, it does, even if the display is as I expected :
obj1 0
obj2 1
Can someone explain me what am I doing wrong ?
All reactions of the Drools Rule Engine with respect to changes in the set of facts require to be notified by using one of the extensions for the Right Hand Side language. You need to call update(f) for the modified fact object f, or you may use the modify(f){...} statement.
However... Changing a contained object X via the reference from fact A and telling the Engine that fact A has been modified will not make it see that fact B, also referencing X, has been changed as well.
This is where you should reconsider your design. Is it really necessary to have an X shared via references from A and B? Or: what about making X a fact and updating it? The latter may mean that you have to rewrite your rules, making the relation between Obj and Cpt visible on the left hand side. But, in my experience, it is usually better to have this than some complex mechanism propagating update notifications from some joint contained object to its parents.
Edit What I mean by "making the relation visible" is shown by the rule below:
rule "R2"
when
Obj(composant == "R2", $counter: counter )
$c: Cpt( this == $counter, value == 0)
then
modify( $c ){ increment() }
end

Using predicates to search within results

I have a Core Data model of cars created and I'd like select one of vehicle type PLUS any of several different colors Here's what I envision to do this:
Selection ViewController
1) Select the type of car (sedan, coupe, SUV, hybrid) (I envision a segmented control to set this as a predicate)
2) Once the type of vehicle is selected, I'd like to select from a list of colors and display only cars that satisfy the type of vehicle I selected in the first step. For example, I'd like to display only SUVs that are (red, green, and blue) or only hybrids that are black and white.
Results TableViewController
3) Once those are selected, I'd like to dump them on another TVC.
This post helps a bit, but person asking the question trying to find disparate items (ex: A, B, & C). I'm trying to find X, Y & Z within only A.
Can I apply multiple predicates to an NSFetchRequest? Would it be better to manually parse my results?
I'm running into trouble when I try retrieve one type of vehicle plus a list of selected colors
let vehiclePredicate = NSPredicate(format: "type == %#", "(one of sedan/coupe/SUV/hybrid)")
AND any of the following criteria
let colorRedPredicate = NSPredicate(format: "color == %#", "red")
let colorGreenPredicate = NSPredicate(format: "color == %#", "green")
let colorBluePredicate = NSPredicate(format: "color == %#", "red")
let colorBlackPredicate = NSPredicate(format: "color == %#", "black")
let colorWhitePredicate = NSPredicate(format: "color == %#", "white")
let colorPredicates: NSArray = [**add colors here when they're selected**]
I'm trying to figure out the cleanest way to do this, but the examples I've found only retrieve disparate items.
The other roadblock I've run into is how to get the results from my SelectionVC to the ResultsTVC. I know I'm going to need a delegate, but what will I need? Any assistance on this would be greatly appreciated.
You want to end up with a predicate like:
type == %# AND color IN %#
And then supply your type as a string and the colours as a collection. You can do this with a compound predicate created from 2 others or just change what your UI selection means and generate the two parameters as the segment name and an array of colours.
If you want to combine multiple options of the oboe then you need to create each and then use a compound predicate to combine them.
Your question is not clear. Your predicates have wrong syntax - are unintelligible. That being said, I think I guess what you want, so here it is.
You could start with a fully loaded table view without any filters. You could implement a filter menu (or buttons) and present the appropriate control (segmented control, table view with multiple selection, table view with single selection, slider etc) modally.
In your original table view you use a NSFetchedResultsController, and you keep a NSPredicate as a property. Whenever the predicate changes, you nil out the fetched results controller and reload your table.
The modal control views can feed back to your master view controller which then modifies the predicate. You can combine all predicates with the NSCompoundPredicate API. Maybe it is most convenient to keep them in a mutable array, so you have them handy.
var predicateArray = [NSPredicate]()
E.g. start with this
predicate = NSPredicate(value: "true")
predicateArray.append(predicate)
Now the color controller sends back ["red", "green", "blue"]. You could construct or predicates by constructing the right predicate string, or by using orPredicateWithSubpredicates, but it might be easier to just use collection operators:
let colorPredicate = NSPredicate(format: "color in %#", colorStrings)!
predicateArray.append(colorPredicate)
In your fetched results controller getter you reconstruct the predicate based on your predicate array:
fetchRequest.predicate =
NSCompoundPredicate.andPredicateWithSubpredicates(predicateArray)

Avoiding a fetch in a custom setter

I have two entities, Item and Category. Each item has one category, and a category can have 0-many items.
I have a special category, the misc category. I denote this with a boolean property, isMisc, so category.isMisc=YES.
When I delete a category, I want to reassign any of its items to the "misc" category. So I wrote the following custom setter for item:
- (void)setCategory:(Category *)category
{
[self willChangeValueForKey:#"category"];
if (category == nil) {
category = [Database theMiscCategory];
}
[self setPrimitiveValue:category forKey:#"category"];
[self didChangeValueForKey:#"category"];
}
The problem is, [Database theMiscCategory] performs a fetch, which I believe is discouraged. Is there another way to do this?
I have looked at just letting item.category = nil, but this introduces enough complications in other areas of the code that I'd much rather have a "misc" category.
You could create or fetch the "misc" category object once in your program, e.g. after creating the managed object context. Then your custom setter method can always use this instance.
If you work with several managed object contexts, then you would have to create one object for each context.

Resources