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?
Related
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 created a auto scroll banner using scrollview in my application. There are 3 images added to scrollview, so I want when scroll reaches the last image it should start from first image and continue scrolling and this process should be repeated.
Right now what is happening is after it reaches the last image, scrollview stops scrolling.
Following is my code:
func setUpScrollView() {
let numberOfViews : Int = 3;
for i in 0...numberOfViews-1{
let origin = CGFloat(i) * (self.view?.frame.size.width)!
let height : CGFloat
height = (self.bannerScrollView?.frame.size.height)!
let image : UIImageView = UIImageView.init(frame: CGRect(x : origin,y : 0.0,width : (self.view?.frame.size.width)!,height : height))
image.image = UIImage.init(named: String.init(format: "banner%d", i+1))
image.contentMode = UIViewContentMode.scaleToFill
self.bannerScrollView?.addSubview(image)
}
self.bannerScrollView?.contentSize = CGSize(width: self.view.frame.size.width * CGFloat(numberOfViews), height: (self.bannerScrollView?.frame.size.height)!)
self.timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(autoScroll), userInfo: nil, repeats: true)
self.pageControl = UIPageControl.init(frame: CGRect.zero)
self.pageControl?.numberOfPages = 3
self.pageControl?.isEnabled = false
self.bannerScrollView?.addSubview(self.pageControl!)
}
func autoScroll() {
let targetXOffset = (self.bannerScrollView?.contentOffset.x)! + (self.bannerScrollView?.bounds.size.width)!
if targetXOffset <= CGFloat(((self.bannerScrollView?.contentSize.width)! - (self.bannerScrollView?.bounds.size.width)!)) {
self.bannerScrollView?.setContentOffset(CGPoint(x: targetXOffset,y: 0), animated: true)
}
let width : CGFloat = (self.bannerScrollView?.frame.size.width)!
let page = ((self.bannerScrollView?.contentOffset.x)! + width)/width
}
func autoScroll() {
guard let scrollView = bannerScrollView else { return }
let width: CGFloat = scrollView.bounds.width
let targetXOffset = scrollView.contentOffset.x + width
if targetXOffset <= CGFloat((scrollView.contentSize.width - width)) {
scrollView.setContentOffset(CGPoint(x: targetXOffset, y: 0), animated: true)
} else {
scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: true)
}
let page = (scrollView.contentOffset.x + width)/width
//need to do something with this page value
}
Be careful with all those ! - they are waiting to blow up on you
What you need to do here is simply simulate an infinite scrolling experience.
You want to duplicate your first image to the end of the scrollview so you now have 4 images, where the first and the last match.
When you begin your auto scroll, you check if you are on this repeated 4th page. If you are, you set the content offset back to (0, 0) with no animation (this is not apparent to the user since they are the same) and then simply animate on to the second image, and repeat.
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 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)
}
}
}