Trouble indexing into array within asynchronous call, Swift - ios

I'm trying to create a scrollView of "Match" object images that correlates to an array of "Match" objects within my view controller, so that if I tap onto an image of a "Match" in the scrollView, I can take the index of that image in my miniMatchesContainer, and use it to access the Match object within my array that that image corresponds to. I tried going about it with a for-loop, but the problem is that since I'm getting match images from the server asynchronously, the calls return out of order and so my containerView indexes are off (I've added an image of my console's print statemen to show what I mean). So now I'm at a bit of an impasse, and would appreciate some advice as to where to go from here. Should I change my approach? Is there something I'm missing? Code added below.
//function to create contentScrollView for MiniMyatches
func setupMiniContentScroll(contentScroll: UIScrollView) {
let scalar:Double = 4/19
let contentViewDimension = contentScroll.frame.width * CGFloat(scalar)
let contentScrollWidth = CGFloat(LocalUser.matches.count) * (contentViewDimension + CGFloat(12)) - CGFloat(12)
let matchManager = MatchesManager()
for index in 0..<LocalUser.matches.count {
let match = LocalUser.matches[index]
print("Match index: \(index), Match at Index: \(match.itemName)")
matchManager.retrieveMatchThumbnail(match) { img, error in
if let img = img {
//create the mini matches views
let xOrigin = index == 0 ? 12 : CGFloat(index) * contentViewDimension + (CGFloat(12) * CGFloat(index) + CGFloat(12))
let contentFrame = CGRectMake(xOrigin, 10, contentViewDimension, contentViewDimension)
let contentView = self.makeMiniContentView(contentFrame, image: img, matchedPrice: match.matchedPrice)
let tap = UITapGestureRecognizer(target: self, action: #selector(BrowseViewController.toggleItemInfo(_:)))
contentView.addGestureRecognizer(tap)
self.miniMatchContainer.append(contentView)
print("MiniMatchContainer Index: \(self.miniMatchContainer.indexOf(contentView)), Match at Index: \(match.itemName)")
//update the contentScrollView
dispatch_async(dispatch_get_main_queue()) {
let contentLabelFrame = CGRect(x: xOrigin, y: contentFrame.height + 15, width: contentFrame.width, height: 20)
let contentLabel = self.makeMiniContentLabel(contentLabelFrame, itemName: match.itemName)
let priceLabel = self.makeMiniPriceLabel(contentFrame, matchedPrice: match.matchedPrice)
contentScroll.addSubview(contentView)
contentScroll.addSubview(contentLabel)
contentScroll.addSubview(priceLabel)
contentScroll.contentSize = CGSizeMake(contentScrollWidth + CGFloat(16), contentScroll.frame.height)
}
}
}
}
}

I would try:
print("MiniMatchContainer Index: \(index), Match at Index: \(match.itemName)")
Instead of:
print("MiniMatchContainer Index: \(self.miniMatchContainer.indexOf(contentView)), Match at Index: \(match.itemName)")
And I would create the content views outside retrieveMatchThumbnail, updating it inside.

Related

Adding a marking to CGRect that is in Path block in Swift

I am trying to make a rectangle that is made of little squares in it. I am able to create the rectangle using the code given but I want to add a marking on the squares. This way the user can make a distinction between each and every square. I want it to look like a grid.
func drawBoard(boundingRect: CGSize) -> some View {
let columns = self.numColumns
let rows = self.numRows
let blocksize = min(boundingRect.width/CGFloat(columns), boundingRect.height/CGFloat(rows))
let xoffset = (boundingRect.width - blocksize*CGFloat(columns))/2
let yoffset = (boundingRect.height - blocksize*CGFloat(rows))/2
let gameBoard = self.gameBoard
return ForEach(0...columns-1, id:\.self) { (column:Int) in
ForEach(0...rows-1, id:\.self) { (row:Int) in
Path { path in
let x = xoffset + blocksize * CGFloat(column)
let y = boundingRect.height - yoffset - blocksize*CGFloat(row+1)
let rect = CGRect(x: x, y: y, width: blocksize, height: blocksize)
path.addRect(rect)
}
.fill(gameBoard[column][row].color)
}
}
}
I am willing to change the entire code. I just need to make a grid...
GRID
Something like this or with borders or anything. Please help with this solution.

UILabel takes long to load swift

I'm trying to create a view of a "match" image and the "match price" label in the top right corner of the match image. So far everything works fine- I create the image in the container view, I create a UIImageView for the background of my price label, but when I create the actual label and customize it, it takes forever to load in my app (that is to say, everything loads- the match image, the price label background image, but not the actual label detailing the price). Can anyone spot where in my code I'm going wrong?
func setupMiniContentScroll(contentScroll: UIScrollView) {
let scalar:Double = 6/19
let contentViewDimension = contentScroll.frame.width * CGFloat(scalar)
let contentScrollWidth = CGFloat(LocalUser.matches.count) * (contentViewDimension + CGFloat(12)) - CGFloat(12)
contentScroll.backgroundColor = UIColorFromHex(0x34495e)
for index in 0..<LocalUser.matches.count {
let match = LocalUser.matches[index]
MatchesManager.globalManager.retrieveMatchThumbnail(match) { img, error in
if let img = img {
//create the mini matches views
let xOrigin = index == 0 ? 12 : CGFloat(index) * contentViewDimension + (CGFloat(12) * CGFloat(index) + CGFloat(12))
let contentFrame = CGRectMake(xOrigin, 10, contentViewDimension, contentViewDimension)
let contentView = self.makeMiniContentView(contentFrame, image: img, matchedPrice: match.matchedPrice)
contentView.match = match
let tap = UITapGestureRecognizer(target: self, action: #selector(BrowseViewController.toggleItemInfo(_:)))
contentView.addGestureRecognizer(tap)
//update the contentScrollView
dispatch_async(dispatch_get_main_queue()) {
contentScroll.addSubview(contentView)
contentScroll.contentSize = CGSizeMake(contentScrollWidth + CGFloat(16), contentScroll.frame.height)
}
}
}
}
}
//functions to create labels and imgViews for MiniMyMatches
func makeMiniContentView(frame: CGRect, image: UIImage, matchedPrice: Int) -> ItemContainer {
let containerView = ItemContainer(frame: frame)
//create the item image
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: containerView.frame.width, height: containerView.frame.height))
imgView.image = image
imgView.layer.cornerRadius = 5
imgView.layer.masksToBounds = true
imgView.userInteractionEnabled = true
//create the price label
dispatch_async(dispatch_get_main_queue()) {
let priceLabel = self.makeMiniPriceLabel(containerView, matchedPrice: matchedPrice)
containerView.addSubview(imgView)
containerView.addSubview(priceLabel)
}
return containerView
}
func makeMiniPriceLabel(containerView: ItemContainer, matchedPrice: Int) -> UIView {
//price label var
let priceLabelFrame = CGRectMake(containerView.frame.size.width - 35, -7, containerView.frame.size.width * 0.50, containerView.frame.size.height * 0.35)
//create the price container
let priceContainer = UIImageView(frame: priceLabelFrame)
priceContainer.image = UIImage(named: "venn.png")
//create the price label
let priceLabel = UILabel(frame: CGRect(x: 3, y:0, width: priceContainer.frame.width, height: priceContainer.frame.height))
priceLabel.text = "$\(matchedPrice)"
priceLabel.numberOfLines = 1
priceLabel.textColor = UIColor.whiteColor()
priceLabel.font = priceLabel.font.fontWithSize(20)
priceContainer.addSubview(priceLabel)
return priceContainer
}
My guess is that the closure for your retrieveMatchThumbnail function is being called on a background thread. You have code in that closure that is manipulating UI objects. I would move ALL the UI code inside your call to dispatch_async():
MatchesManager.globalManager.retrieveMatchThumbnail(match) { img, error in
if let img = img {
//create the mini matches views
let xOrigin = index == 0 ? 12 : CGFloat(index) * contentViewDimension + (CGFloat(12) * CGFloat(index) + CGFloat(12))
let contentFrame = CGRectMake(xOrigin, 10, contentViewDimension, contentViewDimension)
//update the contentScrollView
dispatch_async(dispatch_get_main_queue()) {
let contentView = self.makeMiniContentView(contentFrame, image: img, matchedPrice: match.matchedPrice)
contentView.match = match
let tap = UITapGestureRecognizer(target: self, action: #selector(BrowseViewController.toggleItemInfo(_:)))
contentView.addGestureRecognizer(tap)
contentScroll.addSubview(contentView)
contentScroll.contentSize = CGSizeMake(contentScrollWidth + CGFloat(16), contentScroll.frame.height)
}
}
}

UIImage wont recognize tap gesture Swift

I'm trying to add a tap gesture recognizer to some UIImages that are in a side scrollview, but the images flat out won't recognize the tap and I can't see where I went wrong. I've tried "scrollView.bringSubViewToFront(imgView)" because I figured they might be getting buried in layers of other views, but that didn't do the trick either. "contentView" is the UIImageView in question, where my scrollView is just a collection of those. Any help here would be appreciated, thank you.
//function to create contentScrollView for MiniMatches
func setupMiniContentScroll(contentScroll: UIScrollView) {
let scalar:Double = 4/19
let contentViewDimension = contentScroll.frame.width * CGFloat(scalar)
let contentScrollWidth = CGFloat(LocalUser.matches.count) * (contentViewDimension + CGFloat(12)) - CGFloat(12)
let matchManager = MatchesManager()
for index in 0..<LocalUser.matches.count {
let match = LocalUser.matches[index]
matchManager.retrieveMatchThumbnail(match) { img, error in
if let img = img {
//create the mini matches views
let xOrigin = index == 0 ? 12 : CGFloat(index) * contentViewDimension + (CGFloat(12) * CGFloat(index) + CGFloat(12))
let contentFrame = CGRectMake(xOrigin, 10, contentViewDimension, contentViewDimension)
let contentView = self.makeMiniContentView(contentFrame, image: img, matchedPrice: match.matchedPrice)
let tap = UITapGestureRecognizer(target: self, action: #selector(BrowseViewController.toggleItemInfo(_:)))
contentView.addGestureRecognizer(tap)
self.miniMatchContainer.append(contentView)
//update the contentScrollView
dispatch_async(dispatch_get_main_queue()) {
let contentLabelFrame = CGRect(x: xOrigin, y: contentFrame.height + 15, width: contentFrame.width, height: 20)
let contentLabel = self.makeMiniContentLabel(contentLabelFrame, itemName: match.itemName)
let priceLabel = self.makeMiniPriceLabel(contentFrame, matchedPrice: match.matchedPrice)
contentScroll.addSubview(contentView)
contentScroll.addSubview(contentLabel)
contentScroll.addSubview(priceLabel)
contentScroll.contentSize = CGSizeMake(contentScrollWidth + CGFloat(16), contentScroll.frame.height)
}
}
}
}
}
Did you set UIImage property userInteractionEnabled to true?

Swift: How can I bind data to a view in a loop?

I want to bind data to an array of UIImageView-es by for-loop. But I don't know how to bind as simple as Javascript data attribute.
I have an idea that making an array [UIImage : data] to do this.
Ok, here is something wrong with my code:
Questions:
How to get its patternSelector in selectDecoration?
How to create outlets binding with data in a loop? For example, How can I bind every variable pattern to each action selectDecoration() ?
func selectDecoration(recognizer: UITapGestureRecognizer) {
print("OK")
}
let patternSelectorHeight: CGFloat = 120
var i : Int = 0
//var patternViews = [UIImageView]()
for pattern in decorationPatterns {
let patternSelector = UIImageView(image: UIImage(named: "test.jpg"))
if let p = patternSelector.image {
let proportion = p.size.width / p.size.height
let w = proportion * patternSelectorHeight
let patternX: CGFloat = (w + Conf.Size.margin) * CGFloat(i) + Conf.Size.margin
let patternOrigin = CGPoint(x: patternX, y: decorationSelectionTitleHeight)
patternSelector.userInteractionEnabled = true
patternSelector.frame = CGRect(origin: patternOrigin, size: CGSize(width: w, height: patternSelectorHeight))
patternSelector.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "selectDecoration:"))
decorationBg.addSubview(patternSelector)
decorationBg.bringSubviewToFront(patternSelector)
//patternViews.append(patternSelector)
}
i++
}
There is no inbuilt support for data binding in Swift. You need to keep a reference to your UIImageView objects, probably, as you suggest, in an array, so that you can update them later.
You need to set patterSelector.userInteractionEnabled=true for the UITapGestureRecognizer to work.
let patterSelectorSize = CGSize(width: 80.0, height: 160.0)
var imageViews = [UIImageView]() // This probably needs to be an instance variable so you can access it outside of this function
var i : Int = 0
for _ in decorationPatterns {
let patternX: CGFloat = (patterSelectorSize.width + 10) * CGFloat(i) + 10
let patternOrigin = CGPoint(x: patternX, y: CGFloat(60))
let patterSelector = UIImageView(image: UIImage(named: "default.jpeg"))
patterSelector.frame = CGRect(origin: patternOrigin, size: patterSelectorSize)
patterSelector.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "selectDecoration:"))
patterSelector.userInteractionEnabled=true
imageViews.append(patterSelector)
decorationBg.addSubview(patterSelector)
decorationBg.bringSubviewToFront(patterSelector)
i++
}
You must also ensure that userInteractionEnabled is true in decorationBg otherwise touches won't be propagated to your image views
(1) How to create outlets binding with data in a loop?
If you would like to bind String data then try accessibilityLabel property. You can set & retrieve value silently.
(2) why the UITapGestureRecoginzer in follow doesn't work at all?
You missed to enable the userInteractionEnabled of your UIImageView
Hope this helps you.

Alternative way to show a UIPageControl (Swift)

I'm trying to add an arrow or like an image to correlate to how many view I have in my UIScrollView in swift. Something in line of this
(UPDATE) - My question is how do you insert an image into your scrollview that will tell it to go forward or backwards depending on how many images you have.
Now I know there is the UIPageControl but I'm looking into an alternative way to show that there is a picture and to the right or right and left. This is the code I have so far. I'll try the best I can to explain what I'm doing.
My picSource.count is data from a website that is carrying pictures
I'm multiplying my number of views by the screenwidth
This is an if else statement that shows if a picture is there post bgImage if not post no photoprovided image.
Let me know if you need more information or if I should revise. Thanks!
// Gallery
if (picSource.count > 1){
var picNum:CGFloat = CGFloat(picSource.count)
// UI News Image Scroller
var scroll = UIScrollView(frame:CGRectMake(-5, 650, screenWidth, 260))
scroll.pagingEnabled = true
scroll.showsHorizontalScrollIndicator = false
scroll.showsVerticalScrollIndicator = false
scroll.contentSize = CGSizeMake(picNum*screenWidth, 220)
// Number of views
var numberOfViews = picSource.count
var imageName = ""
// Loop to add views to scroll view
for (var i = 0; i < numberOfViews; i++){
var myIntValue:Int = Int(screenWidth)
var xOrigin = i * myIntValue
// Creates UIImageView instance using myImage
var scrollImageView = UIImageView(frame: CGRect(origin: CGPoint(x: xOrigin, y: 5), size: CGSize(width: screenWidth, height: 250)))
// Tests which image name needs to be used
imageName = picSource[i]
// Creates image object using specified image name "...jpg"
var url = NSURL(string: "http://www/images/cl/" + adID[adCurrentTag] + "/gallery/" + imageName)
var maybeError: NSError?
if var imageData :NSData = NSData(contentsOfURL: url!,options: NSDataReadingOptions.DataReadingMappedIfSafe, error: &maybeError) {
var bgImage = UIImage(data:imageData)
scrollImageView.contentMode = .ScaleAspectFit
scrollImageView.image = bgImage
// Adds Image to scroll view
scroll.addSubview(scrollImageView)
}else if let error = maybeError {
var photoNotProvided = UIImage(named:"noPhoto800x800.png")
var photoNotProvidedView = UIImageView(frame: CGRectMake(5, 650.0, screenWidth, 300.0))
scrollImageView.image = photoNotProvided
scroll.addSubview(scrollImageView)
}
}
border.addSubview(scroll)

Resources