Form
Form copied to clipboard
[Question] How does your GroupHeaders keep track of their collapsed state upon dequeue?
I'm trying to trace the logic of how your group headers keep track of their collapsed state. The scenario would be like so:
- Collapse a group
- Scroll down so the group disappears from screen
- Scroll back up so it gets re-instantiated for view
I'm failing to figure out where you guys figure out if it should be collapsed or not. I only see the part where you assign if it is collapsible.
Thanks
Hi @jasper-ch-chan! Did you get it working?
Hi @3lvis I think I have an understanding of what is happening, however, I'm not sure why it was designed that way. It seems like for every group header, you register a new FORMGroupHeaderView class with a new identifier. Why is that? Why not just re-use the same identifier?
In order to support element-independent styles, the reuse identifiers must be unique. Otherwise, all elements would get the same styling -- which isn't always desired.
hi @jeffleeismyhero I see. And could you kindly point to me how you guys are re-assigning the collapsed state of the group header itself? I can't seem to find that anywhere.
@jasper-ch-chan I'm not able to replicate the issue you've described. Nevertheless, you can look for three different collapsed-related terms/keywords in the code:
-
collapsed
(the default state) -
collapse
(the action/method used to actually collapse) -
collapsible
(whether a group can be collapsed
https://github.com/hyperoslo/Form/search?utf8=%E2%9C%93&q=collapse&type=Code
If you can post the JSON you're using, we might be able to point you to the issue faster.
Here's the JSON I'm using in the Basic-ObjC demo application:
{
"groups":[
{
"id":"personal-details",
"title":"Personal Details",
"collapsed":true,
"sections":[
{
"id":"personal-details-0",
"fields":[
{
"id":"first_name",
"title":"First name",
"accessibility_label":"First Name Accessibility Label",
"info":"Fornavn",
"type":"name",
"disabled":true,
"size":{
"width":30,
"height":1
},
"validations":{
"required":true,
"min_length":2
}
},
{
"id":"last_name",
"title":"Last name",
"type":"name",
"info":"Family name",
"size":{
"width":30,
"height":1
},
"validations":{
"required":true,
"min_length":2
}
},
{
"id":"display_name",
"title":"Display name",
"info":"This is the name that is going to be used across the application.",
"placeholder": "Display name",
"type":"text",
"size":{
"width":40,
"height":1
},
"formula":"first_name last_name"
}
]
},
{
"id":"personal-details-1",
"fields":[
{
"id":"address",
"title":"Address",
"type":"text",
"size":{
"width":50,
"height":1
}
},
{
"id":"email",
"title":"E-post",
"info":"bork\nbork\nbork",
"type":"email",
"size":{
"width":25,
"height":1
},
"validations":{
"format":"[\\w._%+-]+@[\\w.-]+\\.\\w{2,}",
"required":true
}
},
{
"id":"phone",
"title":"Phone number",
"type":"text",
"input_type":"number",
"size":{
"width":25,
"height":1
}
},
{
"id":"image",
"type":"image",
"size":{
"width":100,
"height":1.7
},
"hidden":true
}
]
}
]
},
{
"id":"employment",
"title":"Employment",
"sections":[
{
"id":"employment-0",
"fields":[
{
"id":"start_date",
"title":"Start date",
"info":"The day of employment",
"type":"date",
"size":{
"width":25,
"height":1
}
},
{
"id":"start_time",
"title":"Start time",
"type":"time",
"size":{
"width":25,
"height":1
}
},
{
"id":"end_date",
"title":"End date",
"type":"date",
"validations":{
"compare_to":"start_date",
"compare_rule":">"
},
"size":{
"width":25,
"height":1
}
},
{
"id":"end_time",
"title":"End time",
"type":"time",
"size":{
"width":25,
"height":1
},
"validations":{
"required":true,
"min_length":2
}
},
{
"id":"contract_type",
"title":"Contract type",
"info":"Type of contract\n Permanent or temporary",
"type":"select",
"size":{
"width":50,
"height":1
},
"values":[
{
"id":0,
"title":"Permanent",
"info":"Regular employee",
"default":true,
"targets":[
{
"id":"end_date",
"type":"field",
"action":"show"
},
{
"id":"end_time",
"type":"field",
"action":"show"
},
{
"id":"employment-1",
"type":"section",
"action":"show"
}
]
},
{
"id":1,
"title":"Temporary",
"targets":[
{
"id":"end_date",
"type":"field",
"action":"hide"
},
{
"id":"end_time",
"type":"field",
"action":"hide"
},
{
"id":"employment-1",
"type":"section",
"action":"hide"
}
]
}
]
}
]
},
{
"id":"employment-1",
"fields":[
{
"id":"base_salary",
"title":"Base salary",
"type":"select",
"size":{
"width":25,
"height":1
},
"values":[
{
"title":"Salary 1",
"id":0,
"value":100,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
},
{
"title":"Salary 2",
"id":1,
"value":200,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
},
{
"title":"Salary 3",
"id":2,
"value":10,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
}
]
},
{
"id":"bonus_enabled",
"title":"Bonus enabled",
"type":"select",
"size":{
"width":25,
"height":1
},
"values":[
{
"id":true,
"title":"Ja"
},
{
"id":false,
"title":"Nei"
}
]
},
{
"id":"bonus",
"title":"Bonus",
"type":"float",
"size":{
"width":25,
"height":1
},
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
],
"validations":{
"max_value":100,
"min_value":10
}
},
{
"id":"total",
"title":"Total",
"type":"float",
"size":{
"width":25,
"height":1
},
"disabled":true,
"formula":"$base_salary + $bonus"
}
]
},
{
"id":"companies",
"type":"dynamic",
"action_title":"ADD COMPANY ✛",
"fields":null
},
{
"id":"contacts",
"type":"dynamic",
"action_title":"ADD CONTACT ✛",
"fields":null
}
]
},
{
"id":"employment2",
"title":"Employment 2",
"sections":[
{
"id":"employment-0",
"fields":[
{
"id":"start_date",
"title":"Start date",
"info":"The day of employment",
"type":"date",
"size":{
"width":25,
"height":1
}
},
{
"id":"start_time",
"title":"Start time",
"type":"time",
"size":{
"width":25,
"height":1
}
},
{
"id":"end_date",
"title":"End date",
"type":"date",
"validations":{
"compare_to":"start_date",
"compare_rule":">"
},
"size":{
"width":25,
"height":1
}
},
{
"id":"end_time",
"title":"End time",
"type":"time",
"size":{
"width":25,
"height":1
},
"validations":{
"required":true,
"min_length":2
}
},
{
"id":"contract_type",
"title":"Contract type",
"info":"Type of contract\n Permanent or temporary",
"type":"select",
"size":{
"width":50,
"height":1
},
"values":[
{
"id":0,
"title":"Permanent",
"info":"Regular employee",
"default":true,
"targets":[
{
"id":"end_date",
"type":"field",
"action":"show"
},
{
"id":"end_time",
"type":"field",
"action":"show"
},
{
"id":"employment-1",
"type":"section",
"action":"show"
}
]
},
{
"id":1,
"title":"Temporary",
"targets":[
{
"id":"end_date",
"type":"field",
"action":"hide"
},
{
"id":"end_time",
"type":"field",
"action":"hide"
},
{
"id":"employment-1",
"type":"section",
"action":"hide"
}
]
}
]
}
]
},
{
"id":"employment-1",
"fields":[
{
"id":"base_salary",
"title":"Base salary",
"type":"select",
"size":{
"width":25,
"height":1
},
"values":[
{
"title":"Salary 1",
"id":0,
"value":100,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
},
{
"title":"Salary 2",
"id":1,
"value":200,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
},
{
"title":"Salary 3",
"id":2,
"value":10,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
}
]
},
{
"id":"bonus_enabled",
"title":"Bonus enabled",
"type":"select",
"size":{
"width":25,
"height":1
},
"values":[
{
"id":true,
"title":"Ja"
},
{
"id":false,
"title":"Nei"
}
]
},
{
"id":"bonus",
"title":"Bonus",
"type":"float",
"size":{
"width":25,
"height":1
},
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
],
"validations":{
"max_value":100,
"min_value":10
}
},
{
"id":"total",
"title":"Total",
"type":"float",
"size":{
"width":25,
"height":1
},
"disabled":true,
"formula":"$base_salary + $bonus"
}
]
},
{
"id":"companies",
"type":"dynamic",
"action_title":"ADD COMPANY ✛",
"fields":null
},
{
"id":"contacts",
"type":"dynamic",
"action_title":"ADD CONTACT ✛",
"fields":null
}
]
},
{
"id":"employment3",
"title":"Employment 3",
"sections":[
{
"id":"employment-0",
"fields":[
{
"id":"start_date",
"title":"Start date",
"info":"The day of employment",
"type":"date",
"size":{
"width":25,
"height":1
}
},
{
"id":"start_time",
"title":"Start time",
"type":"time",
"size":{
"width":25,
"height":1
}
},
{
"id":"end_date",
"title":"End date",
"type":"date",
"validations":{
"compare_to":"start_date",
"compare_rule":">"
},
"size":{
"width":25,
"height":1
}
},
{
"id":"end_time",
"title":"End time",
"type":"time",
"size":{
"width":25,
"height":1
},
"validations":{
"required":true,
"min_length":2
}
},
{
"id":"contract_type",
"title":"Contract type",
"info":"Type of contract\n Permanent or temporary",
"type":"select",
"size":{
"width":50,
"height":1
},
"values":[
{
"id":0,
"title":"Permanent",
"info":"Regular employee",
"default":true,
"targets":[
{
"id":"end_date",
"type":"field",
"action":"show"
},
{
"id":"end_time",
"type":"field",
"action":"show"
},
{
"id":"employment-1",
"type":"section",
"action":"show"
}
]
},
{
"id":1,
"title":"Temporary",
"targets":[
{
"id":"end_date",
"type":"field",
"action":"hide"
},
{
"id":"end_time",
"type":"field",
"action":"hide"
},
{
"id":"employment-1",
"type":"section",
"action":"hide"
}
]
}
]
}
]
},
{
"id":"employment-1",
"fields":[
{
"id":"base_salary",
"title":"Base salary",
"type":"select",
"size":{
"width":25,
"height":1
},
"values":[
{
"title":"Salary 1",
"id":0,
"value":100,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
},
{
"title":"Salary 2",
"id":1,
"value":200,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
},
{
"title":"Salary 3",
"id":2,
"value":10,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
}
]
},
{
"id":"bonus_enabled",
"title":"Bonus enabled",
"type":"select",
"size":{
"width":25,
"height":1
},
"values":[
{
"id":true,
"title":"Ja"
},
{
"id":false,
"title":"Nei"
}
]
},
{
"id":"bonus",
"title":"Bonus",
"type":"float",
"size":{
"width":25,
"height":1
},
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
],
"validations":{
"max_value":100,
"min_value":10
}
},
{
"id":"total",
"title":"Total",
"type":"float",
"size":{
"width":25,
"height":1
},
"disabled":true,
"formula":"$base_salary + $bonus"
}
]
},
{
"id":"companies",
"type":"dynamic",
"action_title":"ADD COMPANY ✛",
"fields":null
},
{
"id":"contacts",
"type":"dynamic",
"action_title":"ADD CONTACT ✛",
"fields":null
}
]
},
{
"id":"employment4",
"title":"Employment 4",
"sections":[
{
"id":"employment-0",
"fields":[
{
"id":"start_date",
"title":"Start date",
"info":"The day of employment",
"type":"date",
"size":{
"width":25,
"height":1
}
},
{
"id":"start_time",
"title":"Start time",
"type":"time",
"size":{
"width":25,
"height":1
}
},
{
"id":"end_date",
"title":"End date",
"type":"date",
"validations":{
"compare_to":"start_date",
"compare_rule":">"
},
"size":{
"width":25,
"height":1
}
},
{
"id":"end_time",
"title":"End time",
"type":"time",
"size":{
"width":25,
"height":1
},
"validations":{
"required":true,
"min_length":2
}
},
{
"id":"contract_type",
"title":"Contract type",
"info":"Type of contract\n Permanent or temporary",
"type":"select",
"size":{
"width":50,
"height":1
},
"values":[
{
"id":0,
"title":"Permanent",
"info":"Regular employee",
"default":true,
"targets":[
{
"id":"end_date",
"type":"field",
"action":"show"
},
{
"id":"end_time",
"type":"field",
"action":"show"
},
{
"id":"employment-1",
"type":"section",
"action":"show"
}
]
},
{
"id":1,
"title":"Temporary",
"targets":[
{
"id":"end_date",
"type":"field",
"action":"hide"
},
{
"id":"end_time",
"type":"field",
"action":"hide"
},
{
"id":"employment-1",
"type":"section",
"action":"hide"
}
]
}
]
}
]
},
{
"id":"employment-1",
"fields":[
{
"id":"base_salary",
"title":"Base salary",
"type":"select",
"size":{
"width":25,
"height":1
},
"values":[
{
"title":"Salary 1",
"id":0,
"value":100,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
},
{
"title":"Salary 2",
"id":1,
"value":200,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
},
{
"title":"Salary 3",
"id":2,
"value":10,
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
]
}
]
},
{
"id":"bonus_enabled",
"title":"Bonus enabled",
"type":"select",
"size":{
"width":25,
"height":1
},
"values":[
{
"id":true,
"title":"Ja"
},
{
"id":false,
"title":"Nei"
}
]
},
{
"id":"bonus",
"title":"Bonus",
"type":"float",
"size":{
"width":25,
"height":1
},
"targets":[
{
"id":"total",
"type":"field",
"action":"update"
}
],
"validations":{
"max_value":100,
"min_value":10
}
},
{
"id":"total",
"title":"Total",
"type":"float",
"size":{
"width":25,
"height":1
},
"disabled":true,
"formula":"$base_salary + $bonus"
}
]
},
{
"id":"companies",
"type":"dynamic",
"action_title":"ADD COMPANY ✛",
"fields":null
},
{
"id":"contacts",
"type":"dynamic",
"action_title":"ADD CONTACT ✛",
"fields":null
}
]
}
],
"templates":{
"fields":null,
"sections":[
{
"id":"companies",
"fields":[
{
"id":"companies[:index].name",
"title":"Company Name",
"type":"name",
"validations":{
"required":true,
"min_length":2
},
"size":{
"width":50,
"height":1
}
},
{
"id":"companies[:index].phone_number",
"title":"Phone number",
"type":"number",
"size":{
"width":30,
"height":1
}
},
{
"id":"companies[:index].remove",
"title":"Remove",
"type":"button",
"size":{
"width":20,
"height":1
}
}
]
},
{
"id":"contacts",
"fields":[
{
"id":"contacts[:index].name",
"title":"Contact name",
"type":"name",
"validations":{
"required":true,
"min_length":2
},
"size":{
"width":50,
"height":1
}
},
{
"id":"contacts[:index].phone_number",
"title":"Phone number",
"type":"number",
"size":{
"width":30,
"height":1
}
},
{
"id":"contacts[:index].remove",
"title":"Remove",
"type":"button",
"size":{
"width":20,
"height":1
}
}
]
}
]
}
}
Hi @jeffleeismyhero So the issue is that our JSON itself, sets a few group headers to be not collapsed by default. Lets say I collapse said group, scroll down so the header disappears. Then I scroll back up so the header appears. This will trigger my configureGroupHeaderAtIndexPathBlock
Inside this block, we assign the collapsible and collapsed state via the FORMGroup object being provided. However, it appears the FORMGroup JSON collapsed value doesn't change despite us having collapsed the header. Does that make more sense in regards to what I'm asking?
@jasper-ch-chan so you are using configureGroupHeaderAtIndexPathBlock
to custom configure the group header? Can you post that method?
@jeffleeismyhero
self.dataSource.configureGroupHeaderAtIndexPathBlock = ^UICollectionReusableView *(FORMGroup *group, UICollectionView *collectionView, NSIndexPath *indexPath) {
NSString *identifier = [NSString stringWithFormat:@"%@-%@", FORMHeaderViewCellIdentifier, group.groupID];
[collectionView registerClass:[FORMHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:identifier];
FORMHeaderView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:identifier forIndexPath:indexPath];
headerView.collapsible = group.collapsible;
headerView.isCollapsed = group.collapsed;
return headerView;
};
FORMHeaderView is a class I created, which is a subclass of the original FORMGroupHeaderView. Essentially it adds a UIImageView and isCollapsed boolean for our purpose.``