Geocoder a list of data from array just show the last data on mapview - mkmapview

I am new in coding and i am blocked facing this issue. I try to make a loop to geocode all data from my table "restaurants" and show them on map view but when i launch my application only the last data is shown. How can i show all my list on the map ?
Many thanks for your help
let geoCoder = CLGeocoder()
var i = 0
while i < restaurants.count-1 {
i += 1
geoCoder.geocodeAddressString(restaurants[i].location, completionHandler: {
placemarks, error in
if error != nil {
print(error!)
return
}
if let placemarks = placemarks {
//Get the first placemarks
let placemark = placemarks[0]
//Add annotation
let annotation = MKPointAnnotation()
annotation.title = self.restaurants[i].name
annotation.subtitle = self.restaurants[i].type
if let location = placemark.location { annotation.coordinate = location.coordinate
self.mapView.showAnnotations([annotation], animated: true)
self.mapView.selectAnnotation(annotation, animated: true)
//set the zoom level
let region = MKCoordinateRegionMakeWithDistance(annotation.coordinate, 25000, 25000)
self.mapView.setRegion(region, animated: false)
}
}
}
)
}

Related

SwiftUI show loading view while core data is being loaded

How I can show loading view while core data is being loaded?.
Currently my app's core data store some many images in Binary Data. So when I switch to another tab showing data stored in core data, app lags 1.5 seconds.
So here are two things I have tried:
first I tried to minimize amount of data being loaded from core data using downsample function:
func downsample(imageAt imageURL: Data, to pointSize: CGSize, scale: CGFloat = UIScreen.main.scale) -> UIImage? {
// Create an CGImageSource that represent an image
//CGImageSourceCreateWithData(_ data: CFData, _ options: CFDictionary?)
let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let imageSource = CGImageSourceCreateWithData(imageURL as CFData, imageSourceOptions) else {
return nil
}
// Calculate the desired dimension
let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
// Perform downsampling
let downsampleOptions = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
] as CFDictionary
guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else {
return nil
}
// Return the downsampled image as UIImage
return UIImage(cgImage: downsampledImage)
}
let small = downsample(imageAt: data, to: size)
Image(uiImage: small!)
But there were no difference in lagging time.
So i tried this:
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \ToDoItem2.createdAt, ascending: false)])
var toDoItems: FetchedResults<ToDoItem>
var body: some View {
VStack{
if toDoItems.isEmpty {
LoadingView()
} else {
List {
ForEach(toDoItems) { item in
ToDoItemView(item: item)
}
}
}
}
}
}
Tried to detect no loaded state as toDoItems.isEmpty but it doesn't work
Would be there anyway to show loading view while core data is being loaded?
Thanks
you could try using NSAsynchronousFetchRequest, something like this approach (does not have to be exactly like this (untested) code):
struct ContentView: View {
#Environment(\.managedObjectContext) private var viewContext
#State private var toDoItems: [ToDoItem] = []
#State var isLoading = true
var body: some View {
VStack {
if isLoading {
LoadingView()
} else {
List {
ForEach(toDoItems) { item in
ToDoItemView(item: item)
}
}
}
}
.onAppear {
isLoading = true
let fetchRequest: NSFetchRequest<ToDoItem> = ToDoItem.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \ToDoItem.createdAt, ascending: false)]
let asyncFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { fetchResult -> Void in
if let resutls = fetchResult.finalResult {
self.toDoItems = resutls
}
self.isLoading = false
}
do {
_ = try viewContext.execute(asyncFetchRequest)
} catch {
print("error: \(error)")
}
}
}
}

Pictures are not loading at view controller start up Swift

I am using import AlamofireImage/Alamofire to load up pictures I am downloading from Firebase Storage on my tableview cells. However, when I run the app, I cannot see the pictures unless I navigate to a different page and then come back to the tableview page. Can anyone help?
At application start up:
After I navigate to a different view controller and coming back to the page
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = topNewsTableView.dequeueReusableCell(withIdentifier: "topNewsCell", for: indexPath) as! TopNewsCell
cell.cellDelegate = self
cell.favoriteDelegate = self
cell.share.tag = indexPath.row
cell.collect.tag = indexPath.row
cell.selectionStyle = .none
let article = articles[indexPath.row]
cell.topNewsText.text = article.title
let imageRef = storageRef.child("images/" + article.imageURL)
cell.imageView?.isHidden = true
if article.imageURL != ""{
imageRef.downloadURL { url, error in
if let error = error {
} else {
cell.imageView?.isHidden = false
AF.request(url!).responseData { (response) in
if response.error == nil {
if let data = response.data {
let image = UIImage(data: data)
cell.imageView?.isHidden = false
cell.imageView?.image = self.resizeImage(image: image!, targetSize: CGSize(width: 350.0, height: 300.0))
}
}
}
}
}
}
cell.indexPath = indexPath
return cell
}

NetSuite SuiteTalk API - Get Inventory Details

I'm using the SuiteTalk (API) service for NetSuite to retrieve a list of Assemblies. I need to load the InventoryDetails fields on the results to view the serial/lot numbers assigned to the items. This is the current code that I'm using, but the results still show those fields to come back as NULL, although I can see the other fields for the AssemblyBuild object. How do I get the inventory details (serials/lot#'s) to return on a transaction search?
public static List<AssemblyBuildResult> Get()
{
var listAssemblyBuilds = new List<AssemblyBuildResult>();
var service = Service.Context();
var ts = new TransactionSearch();
var tsb = new TransactionSearchBasic();
var sfType = new SearchEnumMultiSelectField
{
#operator = SearchEnumMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new string[] { "_assemblyBuild" }
};
tsb.type = sfType;
ts.basic = tsb;
ts.inventoryDetailJoin = new InventoryDetailSearchBasic();
// perform the search
var response = service.search(ts);
response.pageSizeSpecified = true;
// Process response
if (response.status.isSuccess)
{
// Process the records returned in the response
// Get more records with pagination
if (response.totalRecords > 0)
{
for (var x = 1; x <= response.totalPages; x++)
{
var records = response.recordList;
foreach (var t in records)
{
var ab = (AssemblyBuild) t;
listAssemblyBuilds.Add(GetAssemblyBuildsResult(ab));
}
if (response.pageIndex < response.totalPages)
{
response = service.searchMoreWithId(response.searchId, x + 1);
}
}
}
}
// Parse and return NetSuite WorkOrder into assembly WorkOrderResult list
return listAssemblyBuilds;
}
After much pain and suffering, I was able to solve this problem with the following code:
/// <summary>
/// Returns List of AssemblyBuilds from NetSuite
/// </summary>
/// <returns></returns>
public static List<AssemblyBuildResult> Get(string id = "", bool getDetails = false)
{
// Object to populate and return results
var listAssemblyBuilds = new List<AssemblyBuildResult>();
// Initiate Service and SavedSearch (TransactionSearchAdvanced)
var service = Service.Context();
var tsa = new TransactionSearchAdvanced
{
savedSearchScriptId = "customsearch_web_assemblysearchmainlist"
};
// Filter by ID if specified
if (id != "")
{
tsa.criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
internalId = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new[] {
new RecordRef() {
type = RecordType.assemblyBuild,
typeSpecified = true,
internalId = id
}
}
}
}
};
}
// Construct custom columns to return
var tsr = new TransactionSearchRow();
var tsrb = new TransactionSearchRowBasic();
var orderIdCols = new SearchColumnSelectField[1];
var orderIdCol = new SearchColumnSelectField();
orderIdCols[0] = orderIdCol;
tsrb.internalId = orderIdCols;
var tranDateCols = new SearchColumnDateField[1];
var tranDateCol = new SearchColumnDateField();
tranDateCols[0] = tranDateCol;
tsrb.tranDate = tranDateCols;
var serialNumberCols = new SearchColumnStringField[1];
var serialNumberCol = new SearchColumnStringField();
serialNumberCols[0] = serialNumberCol;
tsrb.serialNumbers = serialNumberCols;
// Perform the Search
tsr.basic = tsrb;
tsa.columns = tsr;
var response = service.search(tsa);
// Process response
if (response.status.isSuccess)
{
var searchRows = response.searchRowList;
if (searchRows != null && searchRows.Length >= 1)
{
foreach (SearchRow t in searchRows)
{
var transactionRow = (TransactionSearchRow)t;
listAssemblyBuilds.Add(GetAssemblyBuildsResult(transactionRow, getDetails));
}
}
}
// Parse and return NetSuite WorkOrder into assembly WorkOrderResult list
return listAssemblyBuilds;
}
private static string GetAssemblyBuildLotNumbers(string id)
{
var service = Service.Context();
var serialNumbers = "";
var tsa = new TransactionSearchAdvanced
{
savedSearchScriptId = "customsearch_web_assemblysearchlineitems"
};
service.searchPreferences = new SearchPreferences { bodyFieldsOnly = false };
tsa.criteria = new TransactionSearch()
{
basic = new TransactionSearchBasic()
{
internalId = new SearchMultiSelectField
{
#operator = SearchMultiSelectFieldOperator.anyOf,
operatorSpecified = true,
searchValue = new[] {
new RecordRef() {
type = RecordType.assemblyBuild,
typeSpecified = true,
internalId = id
}
}
}
}
};
// Construct custom columns to return
var tsr = new TransactionSearchRow();
var tsrb = new TransactionSearchRowBasic();
var orderIdCols = new SearchColumnSelectField[1];
var orderIdCol = new SearchColumnSelectField();
orderIdCols[0] = orderIdCol;
tsrb.internalId = orderIdCols;
var serialNumberCols = new SearchColumnStringField[1];
var serialNumberCol = new SearchColumnStringField();
serialNumberCols[0] = serialNumberCol;
tsrb.serialNumbers = serialNumberCols;
tsr.basic = tsrb;
tsa.columns = tsr;
var response = service.search(tsa);
if (response.status.isSuccess)
{
var searchRows = response.searchRowList;
if (searchRows != null && searchRows.Length >= 1)
{
foreach (SearchRow t in searchRows)
{
var transactionRow = (TransactionSearchRow)t;
if (transactionRow.basic.serialNumbers != null)
{
return transactionRow.basic.serialNumbers[0].searchValue;
}
}
}
}
return serialNumbers;
}
private static AssemblyBuildResult GetAssemblyBuildsResult(TransactionSearchRow tsr, bool getDetails)
{
if (tsr != null)
{
var assemblyInfo = new AssemblyBuildResult
{
NetSuiteId = tsr.basic.internalId[0].searchValue.internalId,
ManufacturedDate = tsr.basic.tranDate[0].searchValue,
SerialNumbers = tsr.basic.serialNumbers[0].searchValue
};
// If selected, this will do additional NetSuite queries to get detailed data (slower)
if (getDetails)
{
// Look up Lot Number
assemblyInfo.LotNumber = GetAssemblyBuildLotNumbers(tsr.basic.internalId[0].searchValue.internalId);
}
return assemblyInfo;
}
return null;
}
What I learned about pulling data from NetSuite:
Using SavedSearches is the best method to pull data that doesn't automatically come through in the API objects
It is barely supported
Don't specify an ID on the SavedSearch, specify a criteria in the TransactionSearch to get one record
You will need to specify which columns to actually pull down. NetSuite doesn't just send you the data from a SavedSearch automatically
You cannot view data in a SavedSearch that contains a Grouping
In the Saved Search, use the Criteria Main Line = true/false to read data from the main record (top of UI screen), and line items (bottom of screen)

NSCollectionViewFlowLayout - left alignment

NSCollectionViewFlowLayout produces a layout with items justified on the right margin or, if the container is only wide enough for one item, centres items. I was expecting an alignment option, e.g. on the delegate, but am not finding anything in the docs. Does it require subclassing NSCollectionViewFlowLayout to achieve this?
Here is a subclass that produces a left justified flow layout:
class LeftFlowLayout: NSCollectionViewFlowLayout {
override func layoutAttributesForElementsInRect(rect: CGRect) -> [NSCollectionViewLayoutAttributes] {
let defaultAttributes = super.layoutAttributesForElementsInRect(rect)
if defaultAttributes.isEmpty {
// we rely on 0th element being present,
// bail if missing (when there's no work to do anyway)
return defaultAttributes
}
var leftAlignedAttributes = [NSCollectionViewLayoutAttributes]()
var xCursor = self.sectionInset.left // left margin
// if/when there is a new row, we want to start at left margin
// the default FlowLayout will sometimes centre items,
// i.e. new rows do not always start at the left edge
var lastYPosition = defaultAttributes[0].frame.origin.y
for attributes in defaultAttributes {
if attributes.frame.origin.y > lastYPosition {
// we have changed line
xCursor = self.sectionInset.left
lastYPosition = attributes.frame.origin.y
}
attributes.frame.origin.x = xCursor
// by using the minimumInterimitemSpacing we no we'll never go
// beyond the right margin, so no further checks are required
xCursor += attributes.frame.size.width + minimumInteritemSpacing
leftAlignedAttributes.append(attributes)
}
return leftAlignedAttributes
}
}
#Obliquely's answer fails when the collectionViewItems are not uniform in height. Here is their code modified to handle non-uniformly-sized items in Swift 4.2:
class CollectionViewLeftFlowLayout: NSCollectionViewFlowLayout
{
override func layoutAttributesForElements(in rect: CGRect) -> [NSCollectionViewLayoutAttributes]
{
let defaultAttributes = super.layoutAttributesForElements(in: rect)
if defaultAttributes.isEmpty {
return defaultAttributes
}
var leftAlignedAttributes = [NSCollectionViewLayoutAttributes]()
var xCursor = self.sectionInset.left // left margin
var lastYPosition = defaultAttributes[0].frame.origin.y // if/when there is a new row, we want to start at left margin
var lastItemHeight = defaultAttributes[0].frame.size.height
for attributes in defaultAttributes
{
// copy() Needed to avoid warning from CollectionView that cached values are mismatched
guard let newAttributes = attributes.copy() as? NSCollectionViewLayoutAttributes else {
continue;
}
if newAttributes.frame.origin.y > (lastYPosition + lastItemHeight)
{
// We have started a new row
xCursor = self.sectionInset.left
lastYPosition = newAttributes.frame.origin.y
}
newAttributes.frame.origin.x = xCursor
xCursor += newAttributes.frame.size.width + minimumInteritemSpacing
lastItemHeight = newAttributes.frame.size.height
leftAlignedAttributes.append(newAttributes)
}
return leftAlignedAttributes
}
}
A shorter solution for swift 4.2:
class CollectionViewLeftFlowLayout: NSCollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: NSRect) -> [NSCollectionViewLayoutAttributes] {
let attributes = super.layoutAttributesForElements(in: rect)
if attributes.isEmpty { return attributes }
var leftMargin = sectionInset.left
var lastYPosition = attributes[0].frame.maxY
for itemAttributes in attributes {
if itemAttributes.frame.origin.y > lastYPosition { // NewLine
leftMargin = sectionInset.left
}
itemAttributes.frame.origin.x = leftMargin
leftMargin += itemAttributes.frame.width + minimumInteritemSpacing
lastYPosition = itemAttributes.frame.maxY
}
return attributes
}
}
In case your items have the same width...
In the other delegate method, you should change the frame of the NSCollectionViewLayoutAttributes
- (NSCollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSCollectionViewLayoutAttributes *attributes = [[super layoutAttributesForItemAtIndexPath:indexPath] copy];
NSRect modifiedFrame = [attributes frame];
modifiedFrame.origin.x = floor(modifiedFrame.origin.x / (modifiedFrame.size.width + [self minimumInteritemSpacing])) * (modifiedFrame.size.width + [self minimumInteritemSpacing]);
[attributes setFrame:modifiedFrame];
return [attributes autorelease];
}

How to make parent child relationship in C1flexgrid

I am using C1Flexgrid and I need to make parent child relation in this grid. But child details need to show in same grid (no other grid ) and when I clicked on + expand should happen and vice versa.
I have written below code where I am having one column in datatable related to parent and child . If it is parent then I am making it 1 else 0.
When I tried with this code. R2 row is coming as child node of r which should not be a case as it is parent node.
Please help me on this .
private void Form3_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable("customers");
dt.Columns.Add("abc");
dt.Columns.Add("ddd");
dt.Columns.Add("eee");
dt.Columns.Add("parent");
var r = dt.NewRow();
r["abc"] = "11";
r["ddd"] = "12";
r["eee"] = "13";
r["parent"] = "1";
var r1 = dt.NewRow();
r1["ddd"] = "12";
r1["eee"] = "14";
r1["parent"] = "0";
var r2 = dt.NewRow();
r2["abc"] = "11";
r2["ddd"] = "1222";
r2["eee"] = "14";
r2["parent"] = "1";
var rr32 = dt.NewRow();
rr32["abc"] = "11";
rr32["ddd"] = "1222";
rr32["eee"] = "14";
rr32["parent"] = "0";
dt.Rows.Add(r);
dt.Rows.Add(r1);
dt.Rows.Add(r2);
dt.Rows.Add(rr32);
grid1.DataSource = dt;
GroupBy("parent", 1);
// show outline tree
grid1.Tree.Column = 2;
// autosize to accommodate tree
grid1.AutoSizeCol(grid1.Tree.Column);
grid1.Tree.Show(1);
}
void GroupBy(string columnName, int level)
{
object current = null;
for (int r = grid1.Rows.Fixed; r < grid1.Rows.Count; r++)
{
if (!grid1.Rows[r].IsNode)
{
var value = grid1[r, columnName];
string value2 = grid1[r, "parent"].ToString();
if (!object.Equals(value, current))
{
// value changed: insert node, apply style
if (value2.Equals("0"))
{
grid1.Rows.InsertNode(r, level);
grid1.Rows[r].Style = _nodeStyle[Math.Min(level, _nodeStyle.Length - 1)];
r++;
}
// show group name in first scrollable column
//grid1[r, grid1.Cols.Fixed+1] = value;
// update current value
current = value;
}
}
}
}
}
Your code was almost there, i have manipulated GroupBy method to fit your need. It solves your current requirement but you have to handle sorting and other functionalists of grid yourself.
Hope this helps!
void GroupBy(string columnName, int level)
{
object current = null;
for (int r = grid1.Rows.Fixed; r < grid1.Rows.Count; r++)
{
if (!grid1.Rows[r].IsNode)
{
var value = grid1[r, columnName];
if (!object.Equals(value, current))
{
// value changed: insert node, apply style
grid1.Rows.InsertNode(r, level);
grid1.Rows[r].Style = _nodeStyle[Math.Min(level, _nodeStyle.Length - 1)];
// show group name in first scrollable column
Row row = grid1.Rows[r + 1];
for (int i = 0; i < grid1.Cols.Count; i++)
{
grid1[r, i] = row[i];
}
grid1.Rows[r + 1].Visible = false;
r++;
// update current value
current = value;
}
}
}
}

Resources