I'm Trying reorder UICollectionViewcell Images on drag and drop using RealmSwift As database, My UI is not updating on a drag and drop and strange behaviour, some Images are duplicating , my code is Like this
RealmModel As
class StoryAlbumDM: Object {
dynamic var id = 0
dynamic var type = ""
dynamic var isImage: Int = 0
dynamic var textData = ""
dynamic var imageData: NSData? = nil
dynamic var rowId: Int = 0
dynamic var position: Int = 0
dynamic var storyId: Int = 0
dynamic var isCoverImage: Int = 0
dynamic var imagePath = ""
let allStories = List<StoryAlbumDM>()
}
On drag and drop I'm doing Like this
func collectionView(collectionView: UICollectionView, atIndexPath: NSIndexPath, didMoveToIndexPath toIndexPath: NSIndexPath) {
print("moveItemAtIndexPath")
let fromIndexPath: Int = atIndexPath.row
print("from", fromIndexPath)
let toIndexPathInt: Int = toIndexPath.row
print("To", toIndexPath)
let fromData: StoryAlbumDM!
fromData = realm.objects(StoryAlbumDM.self).filter("position = %d AND storyId = %d", fromIndexPath, self.storyID).first!
let toData: StoryAlbumDM!
toData = realm.objects(StoryAlbumDM.self).filter("position = %d AND storyId = %d", toIndexPath, self.storyID).first!
var tempData = StoryAlbumDM()
self.performSelectorOnMainThread(#selector(StoryViewController.updateSrtoryInRealm), withObject: self.collectionView, waitUntilDone: true)
dispatch_async(dispatch_get_main_queue(), {
self.collectionView.performBatchUpdates({
self.collectionView.reloadData()
}, completion: nil)
})
}
func updateSrtoryInRealm() {
self.tempData.type = self.toData.type
self.tempData.isImage = self.toData.isImage
self.tempData.textData = self.toData.textData
self.tempData.rowId = self.toData.rowId
self.tempData.imageData = self.toData.imageData
self.tempData.position = self.toData.position
self.tempData.storyId = self.toData.storyId
self.tempData.isCoverImage = self.toData.isCoverImage
self.tempData.imagePath = self.toData.imagePath
do {
try! realm.write {
self.toData.type = self.fromData.type
self.toData.isImage = self.fromData.isImage
self.toData.textData = self.fromData.textData
self.toData.rowId = self.fromData.rowId
self.toData.imageData = self.fromData.imageData
self.toData.position = self.fromData.position
self.toData.storyId = self.fromData.storyId
self.toData.isCoverImage = self.fromData.isCoverImage
self.toData.imagePath = self.fromData.imagePath
// title.id = temp.id
self.fromData.type = self.tempData.type
self.fromData.isImage = self.tempData.isImage
self.fromData.textData = self.tempData.textData
self.fromData.rowId = self.tempData.rowId
self.fromData.imageData = self.tempData.imageData
self.fromData.position = self.tempData.position
self.fromData.storyId = self.tempData.storyId
self.fromData.isCoverImage = self.tempData.isCoverImage
self.fromData.imagePath = self.tempData.imagePath
}
//}
}
catch {
print("Printed error : ")
}
Problem: Images Are duplicating, Not updating on UI , Reorder strange behaviour, please help me on this
I answered a similar question recently, but I'll re-explain it here. :)
Easily, the best and quickest way to re-order Realm objects inside a Realm file is to make an overarching List object that holds all of the Realm objects of a given type.
For example in this case, you make another object to hold that allStories value you already created:
// Model class that manages the ordering of story album objects
class StoryAlbumDMList: Object {
let allStories = List<StoryAlbumDM>()
}
// Model class for the actual story album objects
class StoryAlbumDM: Object {
dynamic var id = 0
dynamic var type = ""
dynamic var isImage: Int = 0
dynamic var textData = ""
dynamic var imageData: NSData? = nil
dynamic var rowId: Int = 0
dynamic var position: Int = 0
dynamic var storyId: Int = 0
dynamic var isCoverImage: Int = 0
dynamic var imagePath = ""
}
This way, when you want to re-order the list, all you need to do is re-order them inside this array.
Like I said in the other question, one other way you can do it (Which is not as good, but also doesn't require an extra Realm object) is to add another property named orderedIndex, which simply contains a number indicating the numerical order of these objects. When you want to re-order them, it's simply a matter of re-setting these numbers.
Let me know if you need any more clarification!
Related
I'm creating a social media outreach tracker. I want to create a drop-down list of the contact name. The problem is that I have two sources of names on two different sheets.
I wrote a script that pulls the names from the two different sources and combines them to a single array.
I was hoping to set the source range as that array.
Here is my code:
function setDataValid_(range, sourceRange) {
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(sourceRange, true)
.build();
range.setDataValidation(rule);
}
function onEdit() {
var auditionsSheet = SpreadsheetApp.getActiveSpreadsheet();
var castingDirectorsTab = auditionsSheet.getSheetByName("Casting Directors");
var contactsTab = auditionsSheet.getSheetByName("Contacts");
var socialMediaOutreachTab = auditionsSheet.getSheetByName("Social Media Outreach");
var lastRowCD = castingDirectorsTab.getLastRow();
var lastRowContacts = contactsTab.getLastRow();
var activeCell = socialMediaOutreachTab.getActiveCell();
var activeColumn = activeCell.getColumn();
// get data
var castingDirectorNameData = castingDirectorsTab.getRange(2, 1, lastRowCD, 1).getValues();
var contactNameData = contactsTab.getRange(2, 1, lastRowContacts, 1).getValues();
//get name data to a single arrays
var castingDirectorName = [];
castingDirectorNameData.forEach(function(yr) {
castingDirectorName.push(yr[0]);
});
var contactName = [];
contactNameData.forEach(function(yr) {
contactName.push(yr[0]);
});
// get rid of the empty bits in the arrays
for (var x = castingDirectorName.length-1; x > 0; x--) {
if ( castingDirectorName[x][0] === undefined ) {
castingDirectorName.splice( x, 1 )
}
}
for (var x = contactName.length-1; x > 0; x--) {
if ( contactName[x][0] === undefined ) {
contactName.splice( x, 1 )
}
}
//combine two data sources for data validation
var combinedNames = [];
combinedNames.push(castingDirectorName + contactName);
Logger.log (combinedNames);
Logger.log( typeof combinedNames);
// data validation set up and build
if (activeColumn == 1 && auditionsSheet.getName() == "Social Media Outreach") {
var range = auditionsSheet.getRange(activeCell.getRow(), activeColumn +1);
var sourceRange = combinedNames;
setDataValid_(range, sourceRange)
}
}
When I enter a date in Col A on Social Media Outreach, nothing happens in Col 2.
I was using an existing working nested data validation script I have but the sourceRange pulls from a sheet based on the value in the active cell. Here is that code:
function setDataValid_(range, sourceRange) {
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(sourceRange, true)
.build();
range.setDataValidation(rule);
}
function onEdit() {
var aSheet = SpreadsheetApp.getActiveSheet();
var aCell = aSheet.getActiveCell();
var aColumn = aCell.getColumn();
// data validation for Auditions Tab Projet Type to Project Details
if (aColumn == 9 && aSheet.getName() == 'Auditions') {
var range = aSheet.getRange(aCell.getRow(), aColumn + 1);
var sourceRange = SpreadsheetApp.getActiveSpreadsheet().getRangeByName('RefTables!' + aCell.getValue())
setDataValid_(range, sourceRange)
}
}
For this script when I select from the data validation drop-down, a new data validation comes up in the next col with the appropriate secondary data validation.
So the question is, can the source range be set to an array or do I need to put the names back into my sheet to reference a la the second script.
I've looked through the documentation and searched and can't find an answer. I'm relatively new to GAS and am not sure of all the inner workings of the data validation builder.
Why is this not working!?
struct ChocolateBox {
var caramelDelight = []
caramelDelight["flavor"] = "caramel"
}
I tried this without the struct, still doesn't work:
var caramelDelight = []
caramelDelight["flavor"] = "caramel"
I have to add initial values into the array for it to work, for example:
var caramelDelight = ["test":"test"]
caramelDelight["flavor"] = "caramel"
Please explain.
Your var caramelDelight = [] doesn't create an empty dictionary.
To create an empty dictionary use [:]() and specify the types of the keys and values, example: var caramelDelight = [String:String]().
There's also this alternative syntax: var caramelDelight: [String:String] = [:].
Also to modify the var in your struct you need to create an instance of the struct first:
struct ChocolateBox {
var caramelDelight = [String:String]()
}
var cb = ChocolateBox()
cb.caramelDelight["flavor"] = "caramel"
println(cb.caramelDelight) // [flavor: caramel]
UPDATE:
You can also create an initializer for your struct if you need to prepopulate the dictionary:
struct ChocolateBox {
var caramelDelight: [String:String]
init(dict: [String:String]) {
self.caramelDelight = dict
}
}
var cb = ChocolateBox(dict: ["flavor": "caramel"])
Of course then you can update the dictionary as usual:
cb.caramelDelight["color"] = "brown"
println(cb.caramelDelight) // [color: brown, flavor: caramel]
That is because caramelDelight is actually an array, not a dictionary. You can fix that by doing var caramelDelight: [String:String] = [:]
Why don't I see the values of a particular variable in the debugger?
Here's the data type:
struct hashTag {
var parseID:String?
var blguID:String
var owner:String?
var ownerImage:UIImage?
var displayName:String?
var tagDescription:String?
var startTimeStamp:NSDate?
var endTimeStamp:NSDate?
var accessPermission:hashtagPermission = .greenUnlockPublic
var geo:geoStatus?
var coordinates:CLLocationCoordinate2D?
var radiusInMeters:CGFloat?
var duration:Int?
var discoverableBy:[String]?
init(blissID blugID:String, owner:String, ownerImage:UIImage, displayName:String, accessPermission:hashtagPermission, startTimeStamp:NSDate) {
self.blguID = blugID
self.owner = owner
self.ownerImage = ownerImage
self.displayName = displayName
self.accessPermission = accessPermission
self.startTimeStamp = startTimeStamp
}
init(blissID blugID:String, location coordinates:CLLocationCoordinate2D, radius:CGFloat, duration:Int) {
self.blguID = blugID
self.coordinates = coordinates
self.radiusInMeters = radius
self.duration = duration
}
init(blissID blugID:String, radius:CGFloat, duration:Int) {
self.blguID = blugID
self.radiusInMeters = radius
self.duration = duration
}
}
Here's a screen dump.
Notice that the values are shown in the variables view; but not in the debugger (nor via println()):
Is there a remedy or is this another debugger bug?
I have a huge spreadsheet matrix, from which I create a long list of check boxes. The users then select different abilities, press search. The code the cross-checks with the database spreadsheet, returning names of the persons who has those abilities.
I need to update the "rightPanel" with the results of my search. But i simple can't figure out how to - if at all posible - update a panel in my UI..
var dataSSkey = 'sheetID'; //datasheet ID
var dataSheet = SpreadsheetApp.openById(dataSSkey).getSheetByName('Ansatte');
var groupsArray = [[],[],[],[]];
var lastRow = dataSheet.getLastRow();
var lastColumn = dataSheet.getLastColumn();
var dataArray = dataSheet.getRange(1,1,lastRow,lastColumn).getValues();
var numberGroups
var app = UiApp.createApplication().setTitle('Find Consultant');
var panel = app.createVerticalPanel();
var leftPanel = app.createVerticalPanel().setWidth(450);
var rightPanel = app.createVerticalPanel().setWidth(450);
var grid = app.createGrid(1, 2).setId('myGrid')
var outputArray = []; //to store output from search
var positiveList = [[],[]]; //array to store name and folder-ID of consultants matching
var numberPositive = 0; //number of consultants matching
function doGet() {
buildGroupsArray()
addCheckBoxesToUI()
var scrollPanel = app.createScrollPanel().setHeight(460);
//Search button
var searchButton = app.createButton('Search');
var clickHandler = app.createServerClickHandler("respondToSearch");
searchButton.addClickHandler(clickHandler);
clickHandler.addCallbackElement(panel);
var spacerImage = app.createImage("http://www.bi..ge.jpg").setHeight(3);
scrollPanel.add(panel);
rightPanel.add(app.createLabel('resultat her'));
leftPanel.add(scrollPanel);
leftPanel.add(spacerImage);
leftPanel.add(searchButton);
grid.setWidget(0, 0, leftPanel)
grid.setWidget(0, 1, rightPanel);
app.add(grid);
return app;
}
function respondToSearch(e){
var numberLogged = 0;
//define firstEmpty
var firstEmpty = "A"+lastRow;
if(lastRow !== 1){
firstEmpty = "A"+(lastRow+1);
};
//find selected competencies --> store in array + count competencies
for(i = 1; i <= lastRow; i++){
if (e.parameter["Checkbox"+i] == "true") {
var value = e.parameter["CheckboxValue"+i];
outputArray[numberLogged] = value;
numberLogged++;
}
}
for(i = 2; i <= lastColumn; i++){
var numberCorrect = 0;
//Run through rows according to content of output from selection
for(j in outputArray){
//Check if consultant own selected competency
if(dataArray[outputArray[j]][i] == "x"){
numberCorrect++; //if consultant owns selected competency then count
}
}
//if consultant owns all competencies, then add name and folder-id to array
if(numberCorrect == numberLogged){
positiveList[0][numberPositive] = dataArray[1][i]; //Add consultant name
positiveList[1][numberPositive] = dataArray[2][i]; //Add consultant-folder ID
numberPositive++ //count the number of consultants that own all competencies
}
}
for(j in positiveList[0]){
var name = positiveList[0][j];
var id = positiveList[1][j];
Logger.log(name);
Logger.log(id)
var anchor = app.createAnchor(name,'https://ww......folderviewid='+id);
rightPanel.add(anchor)
}
return app;
}
I don't really understand the problem you have...
In your handler function you only have to use app=UiApp.getActiveApplication() and from there populate the panel exactly the same way you did it in the doGet() function, ending with a return app; that will actually update the current Ui.
There are dozens of examples all around... did I misunderstand something in your question ?
Edit : following your comment.
I suppose you defined your variables outside of the doGet function hoping they will become global and so available to all the functions in your script but this is not going to work. Global variables in Google Apps script can't be updated by functions.
I would strongly recommend that you create app and panels in the doGet function and give them an ID so that you can get them back and update their values (or content) from the handler functions.
Here is a re-written version of your code (didn't test)
: (some parts are not reproduced (see //...)
var dataSSkey = 'sheetID'; //datasheet ID
var dataSheet = SpreadsheetApp.openById(dataSSkey).getSheetByName('Ansatte');
var groupsArray = [[],[],[],[]];
var lastRow = dataSheet.getLastRow();
var lastColumn = dataSheet.getLastColumn();
var dataArray = dataSheet.getRange(1,1,lastRow,lastColumn).getValues();
var numberGroups
var outputArray = []; //to store output from search
var positiveList = [[],[]]; //array to store name and folder-ID of consultants matching
var numberPositive = 0; //number of consultants matching
function doGet() {
var app = UiApp.createApplication().setTitle('Find Consultant');
var panel = app.createVerticalPanel();
var leftPanel = app.createVerticalPanel().setWidth(450).setId('leftPanel');
var rightPanel = app.createVerticalPanel().setWidth(450).setId('rightPanel');;
var grid = app.createGrid(1, 2).setId('myGrid')
buildGroupsArray(app); // in this function get app as parameter or use UiApp.getActiveApplication();
addCheckBoxesToUI(app);// in this function get app as parameter or use UiApp.getActiveApplication();
var scrollPanel = app.createScrollPanel().setHeight(460);
//...
//...
return app;
}
function respondToSearch(e){
//...
//...
var app = UiApp.getActiveApplication();
var rightPanel = app.getElementById('rightPanel');
for(j in positiveList[0]){
var name = positiveList[0][j];
var id = positiveList[1][j];
Logger.log(name);
Logger.log(id)
var anchor = app.createAnchor(name,'https://ww......folderviewid='+id);
rightPanel.add(anchor)
}
return app;
}
I have a datatable with 100,000+ DataRow. Which method is faster to access the collection?
Is there any faster way to process the rows collection ?
Method 1:
var rows= dsDataSet.Tables["dtTableName"].Rows;
int rowCount = dsDataSet.Tables["dtTableName"].Rows.Count;
for (int c = 0; c < rowCount; c++)
{
var theRow = rows[c];
//process the dataRow
}
Method 2:
for (int c = 0; c < dsDataSet.Tables["dtTableName"].Rows.Count; c++)
{
var theRow = dsDataSet.Tables["dtTableName"].Rows[c];
//process the dataRow
}
It is worth noting the most direct way to access cells is via the DataColumn indexer; the data is actually stored in the columns, not the rows (no: really).
So something like:
var table = dataSet.Tables["dtTableName"];
// HERE: fetch the DataColumn of those you need, for example:
var idCol = table.Columns["Id"];
var nameCol = table.Columns["Name"];
// now loop
foreach(DataRow row in table.Rows)
{
var id = (int)row[idCol];
var name = (string)row[nameCol];
// ...
}
However, frankly if you want the best performance, I would start by saying "don't use DataSet / DataTable". That is actually a very complicated model designed to be all kinds of flexible, with change tracking, rule enforcement, etc. If you want fast, I'd use a POCO and something like "dapper", for example:
public class Foo {
public int Id {get;set;}
public string Name {get;set;}
}
...
string region = "North";
foreach(var row in conn.Query<Foo>("select * from [Foo] where Region = #region",
new { region })) // <=== simple but correct parameterisation
{
// TODO: do something with row.Id and row.Name, which are direct
// properties of the Foo row returned
var id = row.Id;
var name = row.Name;
// ...
}
or even skip the type via dynamic:
string region = "North";
foreach(var row in conn.Query("select * from [Foo] where Region = #region",
new { region })) // ^^^ note no <Foo> here
{
// here "row" is dynamic, but still works; not quite as direct as a
// POCO object, though
int id = row.Id; // <=== note we can't use `var` here or the
string name = row.Name; // variables would themselves be "dynamic"
// ...
}