I have a UITableViewCell with two images, my goal is to expand these images upon a long press by the user. In the best case scenario the image would cover the entire screen with a small 'x' or something to close.
I have the following function I'm using within the custom UITableViewCell, but the image only expands the size of the cell. I can't figure out how to expand the image over the entire tableview/navBar/tabbar of the superview.
#objc func answerOneLongPress(_ sender: UILongPressGestureRecognizer) {
let imageView = sender.view as! UIImageView
let newImageView = UIImageView(image: imageView.image)
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
newImageView.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
newImageView.backgroundColor = .black
newImageView.contentMode = .scaleAspectFit
self.addSubview(newImageView)
}
Please let me know if you need more information. I feel like this should be happening in the UITableViewController as opposed to the cell, but haven't been able to get it working that way.
You should not add your view to cell but to a view controller or to a key window. That depends on your needs. what happens in your case is your image view is added on a cell and is being clipped and also it is not positioned correctly.
I would use some kind of object that handles presenting of this image. Let the code speak for itself:
class ImageOverlayController {
private var startFrame: CGRect
private var backgroundView: UIView
private var imageView: UIImageView
private init(startFrame: CGRect, backgroundView: UIView, imageView: UIImageView) {
self.startFrame = startFrame
self.backgroundView = backgroundView
self.imageView = imageView
}
private convenience init() { self.init(startFrame: .zero, backgroundView: UIView(), imageView: UIImageView()) }
static func showPopupImage(inController viewController: UIViewController? = nil, fromImageView imageView: UIImageView) -> ImageOverlayController {
guard let targetView = viewController?.view ?? UIApplication.shared.keyWindow else { return ImageOverlayController() } // This should never happen
let startFrame = imageView.convert(imageView.bounds, to: targetView)
let backgroundView: UIView = {
let view = UIView(frame: targetView.bounds)
view.backgroundColor = UIColor.black.withAlphaComponent(0.0)
return view
}()
let newImageView: UIImageView = {
let view = UIImageView(frame: startFrame)
view.image = imageView.image
return view
}()
let controller = ImageOverlayController(startFrame: startFrame, backgroundView: backgroundView, imageView: imageView)
backgroundView.addSubview(newImageView)
targetView.addSubview(backgroundView)
UIView.animate(withDuration: 0.3) {
backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
newImageView.frame = targetView.bounds
}
return controller
}
func dimiss(completion: (() -> Void)? = nil) {
UIView.animate(withDuration: 0.3, animations: {
self.imageView.frame = self.startFrame
self.backgroundView.backgroundColor = self.backgroundView.backgroundColor?.withAlphaComponent(0.0)
}) { _ in
self.backgroundView.removeFromSuperview()
completion?()
}
}
}
As you say a button must still be added which may then call dismiss on the view.
Note: The code I provided was not really tested but just quickly put together. Let me know if there are any issues so I modify it.
Related
I need to get a label's center.x directly aligned with the image inside a tabBar's imageView. Using the below code the label is misaligned, instead of the label's text "123" being directly over the bell inside the tabBar, it's off to the right.
guard let keyWindow = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else { return }
guard let fourthTab = tabBarController?.tabBar.items?[3].value(forKey: "view") as? UIView else { return }
guard let imageView = fourthTab.subviews.compactMap({ $0 as? UIImageView }).first else { return }
guard let imageViewRectInWindow = imageView.superview?.superview?.convert(fourthTab.frame, to: keyWindow) else { return }
let imageRect = AVMakeRect(aspectRatio: imageView.image!.size, insideRect: imageViewRectInWindow)
myLabel.text = "123"
myLabel.textAlignment = .center // I also tried .left
myLabel.center.x = imageRect.midX
myLabel.center.y = UIScreen.main.bounds.height - 74
myLabel.frame.size.width = 50
myLabel.frame.size.height = 21
print("imageViewRectInWindow: \(imageViewRectInWindow)") // (249.99999999403948, 688.0, 79.00000000298022, 48.0)
print("imageRect: \(imageRect)") // (265.4999999955296, 688.0, 48.0, 48.0)
print("myLabelRect: \(myLabel.frame)") // (289.4999999955296, 662.0, 50.0, 21.0)
It might be a layout issue, as in, setting the coordinates before everything is laid out. Where do you call the code from? I was able to get it to work with the following, but got strange results with some if the functions you use, so cut out a couple of them. Using the frame of the tabview worked for me, and calling the coordinate setting from the view controller's viewDidLayoutSubviews function.
class ViewController: UIViewController {
var myLabel: UILabel = UILabel()
var secondTab: UIView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.addSubview(myLabel)
myLabel.textColor = .black
myLabel.text = "123"
myLabel.textAlignment = .center // I also tried .left
myLabel.frame.size.width = 50
myLabel.frame.size.height = 21
secondTab = tabBarController?.tabBar.items?[1].value(forKey: "view") as? UIView
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard let secondTab = secondTab else {
return
}
myLabel.center.x = secondTab.frame.midX
myLabel.center.y = UIScreen.main.bounds.height - 70
}
}
Try below code to return a centreX by tabbar item index.
extension UIViewController {
func centerX(of tabItemIndex: Int) -> CGFloat? {
guard let tabBarItemCount = tabBarController?.tabBar.items?.count else { return nil }
let itemWidth = view.bounds.width / CGFloat(tabBarItemCount)
return itemWidth * CGFloat(tabItemIndex + 1) - itemWidth / 2
}
}
I want to create an image gallery similar to this:
http://sweettutos.com/2015/04/13/how-to-make-a-horizontal-paging-uiscrollview-with-auto-layout-in-storyboards-swift/
Where each time the user swipes and the image changes and the label title changes.I have written the code and included the if statement, however, it does not work properly and it only changes as one value.
Here is my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var mainscrollview: UIScrollView!
var imageArray = [UIImage]()
#IBOutlet var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
mainscrollview.frame = view.frame
imageArray = [#imageLiteral(resourceName: "goku"), #imageLiteral(resourceName: "boruto"), #imageLiteral(resourceName: "tail"), #imageLiteral(resourceName: "sage"),#imageLiteral(resourceName: "tobi")]
for i in 0..<imageArray.count{
let imageView = UIImageView()
imageView.image = imageArray[i]
imageView.contentMode = .scaleAspectFit
let xPosition = self.view.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xPosition, y: 0, width: self.mainscrollview.frame.width,height: self.mainscrollview.frame.height)
mainscrollview.contentSize.width = mainscrollview.frame.width * CGFloat(i + 1)
mainscrollview.addSubview(imageView)
let gokuImage = UIImage(named: "goku")
let borutoImage = UIImage(named: "boruto")
if imageView.image == gokuImage{
label.text = "Goku"
}
if imageView.image == borutoImage{
label.text = "bor"
}
}
}
}
This is where the project is going wrong:
let gokuImage = UIImage(named: "Goku")
let borutoImage = UIImage(named: "boruto")
if imageView.image == gokuImage{
label.text = "Goku"
}
if imageView.image == borutoImage{
label.text = "bor"
}
I think this is because the if statement can not directly access the current image that is being displayed in the UIScroll view.
Here is the link to my project: http://www.mediafire.com/file/adkzcpvdtodlyl5/gallery_copy.zip
To achieve what you want to do, here is one of the option available which is fairly simple:
Your label is in your ScrollView, so it will scroll with your first image. You need to move it above your ScrollView, at the same hierarchy level as the ScrollView.
In order to change your label's text when you scroll, you can detect when your page has stopped decelerating. To do so:
(a) Add UIScrollViewDelegate to class ViewController: UIViewController,
(b) in viewDidLoad(), add 'mainscrollview.delegate = self',
(c) write the 'scrollViewDidEndDecelerating' method.
In the end, your code should look like this:
import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet weak var mainscrollview: UIScrollView!
#IBOutlet var label: UILabel!
var imageArray = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
imageArray = [#imageLiteral(resourceName: "goku"), #imageLiteral(resourceName: "boruto"), #imageLiteral(resourceName: "tail"), #imageLiteral(resourceName: "sage"),#imageLiteral(resourceName: "tobi")]
mainscrollview.frame = view.frame
mainscrollview.delegate = self
label.text = "Initial label value is Boruto"
for i in 0..<imageArray.count{
let imageView = UIImageView()
imageView.image = imageArray[i]
imageView.contentMode = .scaleAspectFit
let xPosition = self.view.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xPosition, y: 0, width: self.mainscrollview.frame.width,height: self.mainscrollview.frame.height)
mainscrollview.contentSize.width = mainscrollview.frame.width * CGFloat(i + 1)
mainscrollview.addSubview(imageView)
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView){
// Test the offset and calculate the current page after scrolling ends
let pageWidth:CGFloat = mainscrollview.frame.width
let currentPage:CGFloat = floor((mainscrollview.contentOffset.x-pageWidth/2)/pageWidth)+1
// Change the text accordingly
if Int(currentPage) == 0{
label.text = "Boruto"
}else if Int(currentPage) == 1{
label.text = "Goku"
}else if Int(currentPage) == 2{
label.text = "Sage"
}else if Int(currentPage) == 3{
label.text = "Tail"
}else if Int(currentPage) == 4{
label.text = "Tobi"
}else{
label.text = "Other character"
}
}
}
The first thing you should do is remove the label from the scroll view and add it into the main view i.e the scrollview and the label should have sibling relationship rather than parent-child relationship.
Modify your ViewController accordingly
class ViewController: UIViewController, UIScrollViewDelegate {
// your other code
override func viewDidLoad() {
super.viewDidLoad()
mainscrollview.delegate = self
// your other code
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let imageNumber = round(scrollView.contentOffset.x / scrollView.frame.size.width)
switch imageNumber {
case 0:
label.text = "Goku"
case 1:
label.text = "Bor"
case 2:
label.text = "Tail"
case 3:
label.text = "Sage"
case 4:
label.text = "Tobi"
default:
label.text = "Unknown"
}
}
}
You use the UILabel from storyboards and UIImageView from the code. You add all the images to the scrollView, so that everyone follows one another, but you use your label only with the first image.
In other words, you need to add UILabel to the code and add it to each image. Instead of a lot of "if-cycle", I suggest you use an array of labels, just as you use the image.
var labelArray = [String]()
// in viewDidLoad add your label names
labelArray = ["goku", "boruto", "next word", "next word", "next word"]
// create UILabel
let yourLabel = UILabel(frame: CGRect(x: xPosition, y: self.mainscrollview.frame.height - 100, width: self.mainscrollview.frame.width, height: 40))
// add each labelName to each image
yourLabel.text = labelArray[i]
// add image and label to scrollView
mainscrollview.addSubview(imageView)
mainscrollview.addSubview(yourLabel)
I am trying to add a Page Control on top of my UIScrollView Custom class. Everything is wokring fine. But whenever I slide the scrollView, the Page Control will slide along the first Image as well.
Here is my code:
import Foundation
import UIKit
import SwiftyGif
class MBSPagingScrollView: UIScrollView {
var pageControl = UIPageControl()
var imagesArray = [String]()
override init (frame : CGRect) {
super.init(frame : frame)
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
init(frame : CGRect, imagesarray : [String]){
super.init(frame : frame)
imagesArray = imagesarray
self.isPagingEnabled = true
self.showsHorizontalScrollIndicator = false
for (index, imageName) in imagesArray.enumerated() {
var imageView = UIImageView()
if imageName.hasSuffix(".gif") {
let gifManager = SwiftyGifManager(memoryLimit:30)
let gif = UIImage(gifName: imageName)
imageView = UIImageView(gifImage: gif, manager: gifManager)
}
else {
imageView = UIImageView(image: UIImage(named: imageName))
imageView.contentMode = UIViewContentMode.scaleToFill
}
imageView.frame = CGRect(x: CGFloat(index) * self.frame.size.width, y: 0, width: self.frame.size.width, height: self.frame.size.height)
self.addSubview(imageView)
}
self.contentSize = CGSize(width:self.frame.size.width * CGFloat(imagesArray.count),height: self.frame.size.height)
configurePageControl()
pageControl.addTarget(self, action: #selector(self.changePage(sender:)), for: UIControlEvents.valueChanged)
}
func configurePageControl() {
self.pageControl.frame = CGRect(x: 0, y: self.frame.size.height - 50, width: self.frame.size.width, height: 50)
self.pageControl.numberOfPages = imagesArray.count
self.pageControl.currentPage = 0
self.pageControl.tintColor = UIColor.red
self.pageControl.pageIndicatorTintColor = UIColor.black
self.pageControl.currentPageIndicatorTintColor = MainOrangeColor
self.addSubview(pageControl)
}
// // MARK : TO CHANGE WHILE CLICKING ON PAGE CONTROL
func changePage(sender: AnyObject) -> () {
let x = CGFloat(pageControl.currentPage) * self.frame.size.width
self.setContentOffset(CGPoint(x:x, y:0), animated: true)
}
func selfDidEndDecelerating(_ scrollView: UIScrollView) {
let pageNumber = round(self.contentOffset.x / self.frame.size.width)
pageControl.currentPage = Int(pageNumber)
}
}
And here are images of the problem happening
As you can see the Page Control is sliding with the first image. I need it to stay in the center.
Any help would be appreciated!
I think you can do that by going to Storyboard, and from the left menu of the UI Builder, drag your page control down and put it out of your scroll view and under it, like in the following picture:
Then you'll need to add the suitable constraints to place your page control properly.
Good luck!
If you don’t want it to scroll... don’t add it to the scroll View.
Just add the page control to the self.view instead of to the scroll view.
The comparison is between this:
let viewController = storyboard!.instantiateViewControllerWithIdentifier("ViewController") as! AViewController
versus this:
let viewController = AViewController()
Unfortunately, this question is not able to answer my question.
I've created this view controller:
final class ImageVC: UIViewController {
var imageView: UIImageView!
var scrollView: UIScrollView!
var originLabel: UILabel!
var image: UIImage?
override func viewDidLoad() {
super.viewDidLoad()
guard let image = image else { fatalError() }
imageView = UIImageView(image: image)
scrollView = UIScrollView(frame: view.bounds)
scrollView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
scrollView.backgroundColor = UIColor.blackColor()
scrollView.contentSize = imageView.bounds.size
scrollView.addSubview(imageView)
view.addSubview(scrollView)
originLabel = UILabel(frame: CGRect(x: 20, y: 30, width: 0, height: 0))
originLabel.backgroundColor = UIColor.blackColor()
originLabel.textColor = UIColor.whiteColor()
view.addSubview(originLabel)
scrollView.delegate = self
setZoomParametersForSize(scrollView.bounds.size)
}
override func viewWillLayoutSubviews() {
print("layout")
setZoomParametersForSize(scrollView.bounds.size)
}
func setZoomParametersForSize(scrollViewSize: CGSize) {
let imageSize = imageView.bounds.size
let widthScale = scrollViewSize.width / imageSize.width
let heightScale = scrollViewSize.height / imageSize.height
let minScale = min(widthScale, heightScale)
scrollView.minimumZoomScale = minScale
scrollView.maximumZoomScale = 3.0
scrollView.zoomScale = minScale
}
}
extension ImageVC: UIScrollViewDelegate {
func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? {
return imageView
}
func scrollViewDidScroll(scrollView: UIScrollView) {
originLabel.text = "\(scrollView.contentOffset)"
originLabel.sizeToFit()
}
}
This is meant to take an image, and when presented, will allow the user to zoom/pan through the image.
When I instantiate this VC using the first method (by instantiating from storyboard with identifier), it behaves fine.
However, when instantiating it the second way; let viewController = ImageVC(), viewWillLayoutSubviews will be triggered whenever scrollView detects movement, disallowing the ability to zoom in and out.
Advice appreciated.
I am trying to make an infinite loop image gallery app. I did it by setting up a UIScrollView inside which I inserted UIImageViews.
I am able to load 4 images and swipe between them, but the problem I have is how to implement the infinite scroll, so that after the last image, the first one appears and opposite.
As I am newbie in Swift, I don't know how to handle this problem nor where to start. I will appreciate any suggestion, example or link on how to solve this problem.
Thanks in advance!
Here is the peace of my code:
class FirstViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
self.scrollView.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
let scrollViewWidth: CGFloat = self.scrollView.frame.width
let scrollViewHeight: CGFloat = self.scrollView.frame.height
let numPics: Int = 3
let imgOne = UIImageView()
let imgTwo = UIImageView()
let imgThree = UIImageView()
let imgFour = UIImageView()
var arrPics = [imgOne, imgTwo, imgThree, imgFour]
var arrSlide = ["slide1.png", "slide2.png", "slide3.png", "slide4.png"]
for i in 0...numPics {
arrPics[i] = UIImageView(frame: CGRectMake(0,scrollViewHeight * CGFloat(i),scrollViewWidth, scrollViewHeight))
arrPics[i].image = UIImage(named: arrSlide[i])
self.scrollView.addSubview(arrPics[i])
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.width, self.scrollView.frame.height*4)
}
Don't use a scroll view but either a table view or a collection view.
The delegates of both classes want a cell count from you. Then give the delegates an insane number of cells, a number so high that it is totally unlikely that any human being will ever scroll to reach the end. Something like 9999999 cells. This is no problem for such classes because in the background implementation they do not create really this high number of cells but only maximal the number of cells which could be visible at the same time on the screen. The cells in fact are reused. So when cells are falling out at the bottom those cells are going to be reused at the top and vice versa. As a last point is: set the starting point in the middle of such a high number, something like 5555555. I think that is the easiest solution if you are not going to implement a full image recycling algorithm like they did.
#IBOutlet weak var scrollView: UIScrollView!
var scrollViewHeight: CGFloat!
let numPics: Int = 4
override func viewDidLoad()
{
super.viewDidLoad()
scrollView.delegate = self
self.scrollView.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height)
let scrollViewWidth: CGFloat = self.scrollView.frame.width
scrollViewHeight = self.scrollView.frame.height
let imgOne = UIImageView()
let imgTwo = UIImageView()
let imgThree = UIImageView()
let imgFour = UIImageView()
let imgFive = UIImageView()
var arrPics = [imgOne, imgTwo, imgThree, imgFour,imgFive]
var arrSlide = ["slide1.jpeg", "slide1.jpeg", "slide1.jpeg", "slide1.jpeg","slide1.jpeg"]
for i in 0...numPics {
arrPics[i] = UIImageView(frame: CGRectMake(0,scrollViewHeight * CGFloat(i),scrollViewWidth, scrollViewHeight))
arrPics[i].image = UIImage(named: arrSlide[i])
self.scrollView.addSubview(arrPics[i])
}
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.width, self.scrollView.frame.height * CGFloat(numPics+1))
}
func scrollViewDidScroll(scrollView: UIScrollView)
{
scrollViewHeight = self.scrollView.frame.height
self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.width, self.scrollView.frame.height * scrollViewHeight )
}
Using Timer you can use infinite scrolling like below
func showview() {
if(videosimageslider.isEmpty) {
// print("Empty Array")
} else {
// print("Not Empty ")
// print(videosimageslider.count)
for var index=0; index<videosimageslider.count; index++ {
if let url = NSURL(string: videosimageslider[index]) {
if let data = NSData(contentsOfURL: url){
if let imageUrl = UIImage(data: data) {
let myimageview:UIImageView = UIImageView()
// myimageview.image = imageUrl
let block: SDWebImageCompletionBlock! = {(image: UIImage!, error: NSError!, cacheType: SDImageCacheType, imageUrl: NSURL!) -> Void in
//println(self)
}
// let url = NSURL(string: "http://yikaobang-test.u.qiniudn.com/FnZTPYbldNXZi7cQ5EJHmKkRDTkj")
myimageview.sd_setImageWithURL(url, completed: block)
myimageview.frame.size.width = imagewidth
myimageview.frame.size.height = imageheight
myimageview.contentMode = UIViewContentMode.ScaleToFill
myimageview.frame.origin.x = xscrollpos
myimageview.frame.origin.y = 5
scrollview!.addSubview(myimageview)
xscrollpos += imagewidth
scrollviewcontentSize += imagewidth
scrollview.contentSize = CGSize(width: scrollviewcontentSize, height: imageheight)
}
}
}
}
scrollingTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "newStartScrolling", userInfo: nil, repeats: true)
scrollingTimer.fire()
}
}
// call the timer function //
func newStartScrolling() {
var currentOffset = scrollview.contentOffset
currentOffset = CGPointMake(currentOffset.x+3, currentOffset.y )
if(currentOffset.x < scrollview.contentSize.width - 500) {
scrollview.setContentOffset(currentOffset, animated: false)
currentOffset = CGPointMake(0, currentOffset.y )
//scrollview.contentSize = CGSize(width: 0, height: 0);
} else {
scrollingTimer.invalidate()
showview()
}
}
This is what will solve your problem, just adjust the controller to go in vertical slide view and load the resources at your will.
Link