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)
}
}
}
Related
I am on Swift 5.
The goal is to center a UIImageView vertically inside a view. Currently it looks like
Note all the image bubbles are running off of the cell.
This is the code that lead to this:
let imageView = UIImageView()
let width = self.frame.width
let height = self.frame.height
let img_width = height //* 0.8
let img_height = height
let y = (height - img_height)/2
let x = width*0.05
imageView.frame = CGRect(
x: x
, y: CGFloat(y)
, width: img_width
, height: img_height
)
let rounded = imageView
.makeRounded()
.border(width:1.0, color:Color.white.cgColor)
self.addSubview(rounded)
The imageView extension functions are:
func makeRounded() -> UIImageView {
self.layer.borderWidth = 0.5
self.layer.masksToBounds = false
self.layer.borderColor = Color.white.cgColor
self.layer.cornerRadius = self.frame.width/2
self.clipsToBounds = true
// see https://developer.apple.com/documentation/uikit/uiview/contentmode
self.contentMode = .scaleAspectFill
return self
}
func border( width: CGFloat, color: CGColor ) -> UIImageView{
self.layer.borderWidth = width
self.layer.borderColor = color
return self
}
Which is very vanilla.
This is odd because I laid out the textview vertically in the exact same way, that is: (parentHeight - childHeight)/2, and it is centered. You can see it in the blue text boxes in cell two and three.
____ EDIT _______
This is how I laid out the cell
let data = dataSource[ row - self._data_source_off_set ]
let cell = tableView.dequeueReusableCell(withIdentifier: "OneUserCell", for: indexPath) as! OneUserCell
// give uuid and set delegate
cell.uuid = data.uuid
cell.delegate = self
// render style: this must be set
cell.hasFooter = false //true
cell.imageSource = data
cell.headerTextSource = data
cell.footerTextSource = data
// color schemes
cell.backgroundColor = Color.offWhiteLight
cell.selectionColor = Color.graySecondary
Add these constraints to you imageView and remove frame and its calculations
self.contentView.addSubview(rounded)
self.mimageView.translatesAutoresizingMaskIntoConstraints = false
self.mimageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor,constant: 20).isActive = true
self.mimageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
self.mimageView.heightAnchor.constraint(equalTo: contentView.heightAnchor).isActive = true
self.mimageView.widthAnchor.constraint(equalTo: contentView.heightAnchor).isActive = true
I'm trying to register a tap inside an UIScrollView but can't seem to get it work. I tried everything I've found on here to no success. I'm using a subclass of UIImageView to add an identifier to it which shouldn't be the problem. Can't seem to get it to work with the default UIImageView class either. "didTapImage" just does not get executed no matter what I do.
func fillSelectionView(){
let imageNameArray = ["entdecken", "erleben", "erinnern"]
imageArray = [UIImage(named: imageNameArray[0]), UIImage(named: imageNameArray[1]), UIImage(named: imageNameArray[2])] as! [UIImage]
for i in 0..<imageArray.count{
let imageView = UIImageViewWithIdentifier()
imageView.image = imageArray[i]
imageView.contentMode = .scaleAspectFit
imageView.setIdentifier(ident: imageNameArray[i])
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(UIGestureRecognizer(target: self, action: Selector(("didTapImage"))))
let xPosition = self.selectionView.frame.width * CGFloat(Double(i))
imageView.frame = CGRect(x: xPosition, y: 0, width: selectionView.frame.width, height: selectionView.frame.height)
selectionView.contentSize.width = self.selectionView.frame.width * CGFloat((Double(i) + 1))
selectionView.addSubview(imageView)
}
}
func didTapImage(gesture: UIGestureRecognizer) {
let point = gesture.location(in: gesture.view)
let imageViewTouched = gesture.view as! UIImageViewWithIdentifier
let imageViewIdent = imageViewTouched.getIdentifier()
print(point)
print("touched" + imageViewIdent)
}
I have a pain map where I use location coordinates to plot where there has been pain. I can add the "dots" in a for in loop and they show up fine. However I cannot remove them before I instantiate them outside the for in loop. So every time I update the view it will plot new ones not ones on top of the old ones. What can I do?
This version adds the dots well but I cannot remove them outside as I cannot call dot.removeFromSuperview()
DispatchQueue.global(qos: .background).async {
if let locationNotNilX = self.painDiagramAnalysisModel.painLocationXFor(days: self.daysChosen){
x = locationNotNilX
count = locationNotNilX.count
}
if let locationNotNilY = self.painDiagramAnalysisModel.painLocationYFor(days: self.daysChosen){
y = locationNotNilY
}
let locationsArray = zip(x, y)
print("zipped array \(locationsArray)")
DispatchQueue.main.async {
let dot = UIImageView()
dot.removeFromSuperview()
dot.image = nil
for item in locationsArray {
self.locationPainY = (diagramHeight * CGFloat(item.1)) + originY
self.locationPainX = (diagramWidth * CGFloat(item.0)) + originX
print(" locationX \(self.locationPainX) locationY \(self.locationPainY)")
dot.image = UIImage(named: "dot")
dot.frame = CGRect(x: self.locationPainX - (dotDiameter / 4), y: self.locationPainY - (dotDiameter / 4), width: dotDiameter , height: dotDiameter)
if count > 2 {
dot.alpha = 0.6
} else {
dot.alpha = 1.0
}
dot.readingsPressedAnimation()
self.view.addSubview(dot)
}
}
}
This version removes the dot but there is only one dot (self hangs on to the dot and just instantiates it once in the for in loop.
let dot = UIImageView()
dot.removeFromSuperview()
dot.image = nil
DispatchQueue.global(qos: .background).async {
if let locationNotNilX = self.painDiagramAnalysisModel.painLocationXFor(days: self.daysChosen){
x = locationNotNilX
count = locationNotNilX.count
}
if let locationNotNilY = self.painDiagramAnalysisModel.painLocationYFor(days: self.daysChosen){
y = locationNotNilY
}
let locationsArray = zip(x, y)
print("zipped array \(locationsArray)")
DispatchQueue.main.async {
for item in locationsArray {
self.locationPainY = (diagramHeight * CGFloat(item.1)) + originY
self.locationPainX = (diagramWidth * CGFloat(item.0)) + originX
print(" locationX \(self.locationPainX) locationY \(self.locationPainY)")
dot.image = UIImage(named: "dot")
dot.frame = CGRect(x: self.locationPainX - (dotDiameter / 4), y: self.locationPainY - (dotDiameter / 4), width: dotDiameter , height: dotDiameter)
if count > 2 {
dot.alpha = 0.6
} else {
dot.alpha = 1.0
}
dot.readingsPressedAnimation()
self.view.addSubview(dot)
}
}
}
How can I add many instances of the dot and remove them outside the loop?
Iterate over your maps subviews and remove all UIImageViews:
func removeDots() {
for case let dot as UIImageView in yourPainMapView.subViews {
dot.removeFromSuperView()
}
}
In case you are using other UIImageView subViews you do not want to remove, subclass UIImageView (class MyDot:UIImage {...}):
for case let dot as MyDot in yourPainMapView.subViews
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.
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?