In UI test, I can get the first cell using this code:
let app = XCUIApplication()
app.launch()
let tablesQuery = app.tables
let cell = tablesQuery.children(matching:.any).element(boundBy: 0)
How to check if that cell contains a imageview ?
public func hasImageViewInside(_ cell: UITableViewCell) -> Bool {
for child in cell.subviews {
if let _ = child as? UIImageView {
return true
}
}
return false
}
for viw in cell.contentView.subviews {
if ((viw as? UIImageView) != nil) {
print("123")
}
}
Swift 5
Give the cell's ImageView an accessibility identifier first either in storyboard or ViewDidLoad
func testIsImageViewNil() {
let imageView = app.images["PhotosCollectionViewController.ImageCell.ImageView"]
XCTAssertNotNil(imageView)
}
for case let imageView as UIImageView in cell.contentView.subviews {
if imageView.tag == 1001 {
imageView.image = UIImage(named: "myCustomImage")
}
}
//OR Altervnatively
cell.contentView.subviews.flatMap { $0 as? UIImageView }.forEach { imageView in
if imageView.tag == 1001 {
imageView.image = UIImage(named: "myCustomImage")
}
}
Related
I have a custom UICollectionViewFlowLayout which lays out items with a left-aligned format.
LAYOUT
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
var newAttributesArray = [UICollectionViewLayoutAttributes]()
let superAttributesArray = super.layoutAttributesForElements(in: rect)!
guard let attributesToReturn = superAttributesArray.map( { $0.copy() }) as? [UICollectionViewLayoutAttributes] else {
return nil
}
for (index, attributes) in attributesToReturn.enumerated() {
if index == 0 || attributesToReturn[index - 1].frame.origin.y != attributes.frame.origin.y {
attributes.frame.origin.x = sectionInset.left
} else {
let previousAttributes = attributesToReturn[index - 1]
let previousFrameRight = previousAttributes.frame.origin.x + previousAttributes.frame.width
attributes.frame.origin.x = previousFrameRight + minimumInteritemSpacing
}
newAttributesArray.append(attributes)
}
return attributesToReturn
}
When I reload a cell that is not the first in the horizontal line, the cell tried performs peculiarly is in the below illustration. I believe this is a layout issue. Reloading a cell that's first does not act this way.
RELOAD METHOD
func selectedInterest(for cell: InterestCell) {
guard let indexPath = mainView.collectionView.indexPath(for: cell),
let documentID = cell.interest?.documentID,
let isSaved = cell.interest?.isSaved else { return }
var interest = self.interests[indexPath.item]
interest.isSaved = !isSaved
self.interests[indexPath.item] = interest
self.mainView.collectionView.collectionViewLayout.invalidateLayout()
self.mainView.collectionView.reloadItems(at: [indexPath])
}
I have tried to invalidate the layout before reloading however this has no effect.
In the past, I customized the images of indicators of Page Control using some functions like the following code provided by #Politta.
class CustomPageControl: UIPageControl {
#IBInspectable var currentPageImage: UIImage?
#IBInspectable var otherPagesImage: UIImage?
override var numberOfPages: Int {
didSet {
updateDots()
}
}
override var currentPage: Int {
didSet {
updateDots()
}
}
override func awakeFromNib() {
super.awakeFromNib()
pageIndicatorTintColor = .clear
currentPageIndicatorTintColor = .clear
clipsToBounds = false
}
private func updateDots() {
for (index, subview) in subviews.enumerated() {
let imageView: UIImageView
if let existingImageview = getImageView(forSubview: subview) {
imageView = existingImageview
} else {
imageView = UIImageView(image: otherPagesImage)
// Modify image size
imageView.frame = ....
imageView.center = subview.center
subview.addSubview(imageView)
subview.clipsToBounds = false
}
imageView.image = currentPage == index ? currentPageImage : otherPagesImage
}
}
private func getImageView(forSubview view: UIView) -> UIImageView? {
if let imageView = view as? UIImageView {
return imageView
} else {
let view = view.subviews.first { (view) -> Bool in
return view is UIImageView
} as? UIImageView
return view
}
}
}
Now I found that Subviews count is not working on iOS 14 as Apple had introduced some new APIs for UIPageControll. Now when I try to use a function setIndicatorImage(image, index) provided by #Soumen, the image shows abnormally big. Modifying the size of page control doesn't help me. In the past, since I add image view to current view of page control, I can define its frame, but now the function setIndicatorImage() just takes image as its parameter. How do I solve the issue?
class CustomPageControl: UIPageControl {
#IBInspectable var currentPageImage: UIImage?
#IBInspectable var otherPagesImage: UIImage?
override var numberOfPages: Int {
didSet {
updateDots()
}
}
override var currentPage: Int {
didSet {
updateDots()
}
}
override func awakeFromNib() {
super.awakeFromNib()
if #available(iOS 14.0, *) {
defaultConfigurationForiOS14AndAbove()
} else {
pageIndicatorTintColor = .clear
currentPageIndicatorTintColor = .clear
clipsToBounds = false
}
}
private func defaultConfigurationForiOS14AndAbove() {
if #available(iOS 14.0, *) {
for index in 0..<numberOfPages {
let image = index == currentPage ? currentPageImage : otherPagesImage
setIndicatorImage(image, forPage: index)
}
// give the same color as "otherPagesImage" color.
pageIndicatorTintColor = .gray
// give the same color as "currentPageImage" color.
currentPageIndicatorTintColor = .red
/*
Note: If Tint color set to default, Indicator image is not showing. So, give the same tint color based on your Custome Image.
*/
}
}
private func updateDots() {
if #available(iOS 14.0, *) {
defaultConfigurationForiOS14AndAbove()
} else {
for (index, subview) in subviews.enumerated() {
let imageView: UIImageView
if let existingImageview = getImageView(forSubview: subview) {
imageView = existingImageview
} else {
imageView = UIImageView(image: otherPagesImage)
// Modify image size
imageView.frame = ....
imageView.center = subview.center
subview.addSubview(imageView)
subview.clipsToBounds = false
}
imageView.image = currentPage == index ? currentPageImage : otherPagesImage
}
}
}
private func getImageView(forSubview view: UIView) -> UIImageView? {
if let imageView = view as? UIImageView {
return imageView
} else {
let view = view.subviews.first { (view) -> Bool in
return view is UIImageView
} as? UIImageView
return view
}
}
}
For iOS 14, the hierarchy of Views has changed, so we cannot get subviews count of UIPageControl like we did before (in iOS < 14). To get them like before, you need to change your accessing method of dot subviews like below.
For accessing them in iOS 14,
Before:
for (index, subview) in subviews.enumerated() {
//Your rest of the code
}
After:
var dotViews: [UIView] = subviews
if #available(iOS 14, *) {
let pageControl = dotViews[0]
let dotContainerView = pageControl.subviews[0]
dotViews = dotContainerView.subviews
}
for (index, subview) in dotViews.enumerated() {
//Your rest of the code
}
Your full code may look like this after modification:
class CustomPageControl: UIPageControl {
#IBInspectable var currentPageImage: UIImage?
#IBInspectable var otherPagesImage: UIImage?
override var numberOfPages: Int {
didSet {
updateDots()
}
}
override var currentPage: Int {
didSet {
updateDots()
}
}
override func awakeFromNib() {
super.awakeFromNib()
pageIndicatorTintColor = .clear
currentPageIndicatorTintColor = .clear
clipsToBounds = false
}
private func updateDots() {
var dotViews: [UIView] = subviews
if #available(iOS 14, *) {
let pageControl = dotViews[0]
let dotContainerView = pageControl.subviews[0]
dotViews = dotContainerView.subviews
}
for (index, subview) in dotViews.enumerated() {
let imageView: UIImageView
if let existingImageview = getImageView(forSubview: subview) {
imageView = existingImageview
} else {
imageView = UIImageView(image: otherPagesImage)
// Modify image size
imageView.frame = ....
imageView.center = subview.center
subview.addSubview(imageView)
subview.clipsToBounds = false
}
imageView.image = currentPage == index ? currentPageImage : otherPagesImage
}
}
private func getImageView(forSubview view: UIView) -> UIImageView? {
if let imageView = view as? UIImageView {
return imageView
} else {
let view = view.subviews.first { (view) -> Bool in
return view is UIImageView
} as? UIImageView
return view
}
}
}
In this way, you can access your dot views and proceed code like before (Customising the images of indicators, change background color etc.)
For iOS 14.0 you have to access pageControl.subviews[0].subviews[0].subviews in order to get the dots views of the pageControl. Instead, for iOS < 14.0 you'll get the dots views accessing pageControl.subviews
private func updatePageControlDots() {
var currentDot = UIView()
if #available(iOS 14, *) {
let pageControlContent = pageControl.subviews[0]
let dotContainerView = pageControlContent.subviews[0]
currentDot = dotContainerView.subviews[currentPage]
} else {
currentDot = pageControl.subviews[currentPage]
}
}
When I m trying to access the parent class cell through child class by calling its method it crashes, because only visible cell of collectionview are in queue but cellForItemAtIndexPath for invisible cells are nil
Here is the code for my child class:
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
print(self.collectionView.visibleCells())
for cell in self.collectionView.visibleCells() {
let collectionViewCell = cell as! PSMediaCell
let indexPath:NSIndexPath = self.collectionView.indexPathForCell(collectionViewCell)!
let imageObj:NSMutableDictionary = imageArr.objectAtIndex(indexPath.item) as! NSMutableDictionary
// this is calling parent class method
mediavc.mymethodforback(indexPath, vc: self)
if imageObj.objectForKey("image_url")?.rangeOfString("mov").length > 0 {
} else {
var imageView:UIImageView = collectionViewCell.contentView.viewWithTag(122) as! UIImageView
imagev = imageView
let indicatorView:UIActivityIndicatorView = collectionViewCell.contentView.viewWithTag(233) as! UIActivityIndicatorView
var videoIcon:UIImageView = cell.contentView.viewWithTag(133) as! UIImageView
var imageOverlay:UIImageView = collectionViewCell.contentView.viewWithTag(234) as! UIImageView
//var videoIcon:UIImageView = collectionViewCell.contentView.viewWithTag(133) as! UIImageView
var downloadingFilePath = NSTemporaryDirectory().stringByAppendingPathComponent(imageObj.objectForKey("image_url") as! String)
let imageN = UIImage(contentsOfFile:downloadingFilePath as String )
if imageN == nil {
var downloadRequest = AWSS3TransferManagerDownloadRequest()
var downloadingFilePath1 = NSTemporaryDirectory().stringByAppendingPathComponent(imageObj.objectForKey("image_url") as! String)
let downloadingFileURL = NSURL(fileURLWithPath: downloadingFilePath1)
downloadRequest.bucket = "photosharebucket1"
downloadRequest.key = imageObj.objectForKey("image_url") as! String
downloadRequest.downloadingFileURL = downloadingFileURL
self.download(downloadRequest, ImageObj:imageObj)
indicatorView.hidden = false
imageOverlay.hidden = false
} else {
imageView.image = imageN
indicatorView.hidden = true
imageOverlay.hidden = true
videoIcon.hidden = true
}
}
}
}
This is code of parent class:
func mymethodforback(images:NSIndexPath , vc :PSUserSentPhotoDetailsVC) {
print( images.row)
print(PSMediaDetailVC.collection.visibleCells().count)
var cell :PSMediaCollectionViewCell = PSMediaDetailVC.collection.cellForItemAtIndexPath(images) as! PSMediaCollectionViewCell
// here i m getting nil because cell for particular indexpath is not visible in parent class
var imageView:UIImageView = cell.imagevieww as UIImageView
PSMediaDetailVC.imageve = imageView
print("xcxcxcxc")
print( PSMediaDetailVC.imageve)
print(PSMediaDetailVC.collection)
print(self)
self.ysl_addTransitionDelegate(PSMediaDetailVC.collection)
self.navigationController(PSMediaDetailVC.nvc, animationControllerForOperation: UINavigationControllerOperation.Push, fromViewController: self, toViewController:vc )
// self.ysl_pushTransitionAnimationWithToViewControllerImagePointY(0, animationDuration: 0.3)
}
The cells in a UICollectionView display information from the underlying data model, they don't store information in their own right. As a result, there is no need for cells that aren't currently onscreen to held in memory; they can easily be recreated by calling cellForItemAtIndexPath just before they are needed. This enables the collection view to reuse cell objects and reduce its memory footprint.
The result of all this is that you cannot get cells that are not currently displayed by calling cellForItemAtIndexPath on the collection view.
I want to realize a sort of matrix editable by touch.
A series of black or white cell that if tapped switch form black to withe or viceversa.
For doing this i will use UIImageView arranged in stack view a Tap Gesture Recognizer. But how do I know which UIImageView was tapped? And in which way I can change the UIImage in the UIImageView?
If they are just white and black, it would be much simpler to use a UIView and set its backgroundColor to .white or .black. You can use the tag property of the UIViews to identify them.
In your gestureRecognizer handler, the recognizer.view tells you the view that triggered the gesture.
You can assign the tags in Interface Builder. For example, if you have an 8x8 array of squares, you could use a 2-digit number where the first digit is the row and the second digit is the column. Thus, your tags would be 11, 12, ..., 87, 88.
func squareTapped(recognizer: UIGestureRecognizer) {
if let view = recognizer.view {
let tag = view.tag
let row = tag / 10
let col = tag % 10
print("The view at row \(row), column \(col) was tapped")
if view.backgroundColor == .black {
view.backgroundColor = .white
} else {
view.backgroundColor = .black
}
}
}
If you do want to use images, then load the images as properties of your viewController and assign them based upon the row and column of your image. Here I have used an Outlet Collection to hold all of the UIImageViews. In Interface Builder, you'd connect each of your cells to the squares property.
class BoardViewController: UIViewController {
let blackImage = UIImage(named: "blackImage")!
let whiteImage = UIImage(named: "whiteImage")!
#IBOutlet var squares: [UIImageView]!
var recognizersAdded = false
func setUpBoard() {
for imageview in squares {
if !recognizersAdded {
let recognizer = UITapGestureRecognizer(target: self, action: #selector(squareTapped))
imageview.addGestureRecognizer(recognizer)
imageview.isUserInteractionEnabled = true
}
let tag = view.tag
let row = tag / 10
let col = tag % 10
// Just for demo purposes, set up a checkerboard pattern
if (row + col) % 2 == 0 {
imageview.image = blackImage
} else {
imageview.image = whiteImage
}
}
recognizersAdded = true
}
func squareTapped(recognizer: UIGestureRecognizer) {
if let view = recognizer.view as? UIImageView {
let tag = view.tag
let row = tag / 10
let col = tag % 10
print("The view at row \(row), column \(col) was tapped")
if view.image == blackImage {
view.image = whiteImage
} else {
view.image = blackImage
}
}
}
}
The code I'm using now is this, it works well.
But I want to use a pan gesture (UIPanGetureRecognizer) to change colors of the UIView. How can I do?
class ViewController: UIViewController {
#IBOutlet weak var PrincipalRowStack: UIStackView!
let rowsNumber=10
let colsNumber=10
override func viewDidLoad() {
// Do any additional setup after loading the view, typically from a nib.
super.viewDidLoad()
var rowsStack = [UIStackView]()
var images = [[UIView]]()
var gestRec = [[UITapGestureRecognizer]]()
for r in 0...rowsNumber-1 {
rowsStack.append(UIStackView())
rowsStack[r].axis = .horizontal
rowsStack[r].distribution = .fillEqually
images.append([UIView]())
gestRec.append([UITapGestureRecognizer]())
for c in 0...colsNumber-1{
images[r].append(UIView())
gestRec[r].append(UITapGestureRecognizer())
gestRec[r][c].addTarget(self, action: #selector(ViewController.Tap(_:)))
images[r][c].contentMode = .scaleToFill
images[r][c].layer.borderWidth = 1
images[r][c].layer.borderColor = UIColor(red:0.5, green:0.5, blue:0.5, alpha: 1.0).cgColor
images[r][c].addGestureRecognizer(gestRec[r][c])
rowsStack[r].addArrangedSubview(images[r][c])
}
}
for s in rowsStack{
PrincipalRowStack.addArrangedSubview(s)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func Tap(_ recognizer: UITapGestureRecognizer) {
if let view = recognizer.view {
//let tag = view.tag
//let row = tag / 10
//let col = tag % 10
print("Tapped!")
if view.backgroundColor == .black {
view.backgroundColor = .white
} else {
view.backgroundColor = .black
}
}
}
}
I'solved adding a PanGestureRecognizer to the principalStackView and than i use this function with a hitTest to know which view should change color.
func pan(_ recognizer: UIPanGestureRecognizer){
if (recognizer.state == .began ||
recognizer.state == .changed)
{
let loc = recognizer.location(in: principalRowStack)
let view = principalRowStack.hitTest(loc, with: UIEvent())
view?.backgroundColor = .black
}
}
Assign a tag to each UIImageView and Add Tap gestures on them.
Upon tap on any view , you can get the image view tapped by :
UIImageView *imageView = gestureRecogniser.view
I'm trying to change the indicator image , following the example in EAIntroView
here is the objective-c code from EAIntroView
SMPageControl *pageControl = [[SMPageControl alloc] init];
pageControl.pageIndicatorImage = [UIImage imageNamed:#"pageDot"];
pageControl.currentPageIndicatorImage = [UIImage imageNamed:#"selectedPageDot"];
[pageControl sizeToFit];
intro.pageControl = (UIPageControl *)pageControl;
intro.pageControlY = 130.f;
and here is the swift code I'm trying to implement
// SMPageControl
pageControl = SMPageControl()
pageControl.pageIndicatorImage = UIImage(named: "pageDot")
pageControl.currentPageIndicatorImage = UIImage(named: "selectedPageDot")
pageControl.sizeToFit()
pageControl.backgroundColor = UIColor.clearColor()
intro.pageControl = pageControl as? UIPageControl
swift code has a warning here
intro.pageControl = pageControl as? UIPageControl
the warning is :
Cast from 'SMPageControl!' to unrelated type 'UIPageControl' always fails
any help ?
For changing page Indicator Image [SMPageControl] / EAIntroView in swift I have created custom class of UIPageControl like below:
import UIKit
class CustomPageControl: UIPageControl {
let activePage: UIImage = UIImage(named: "icon-selected-dot")!
let inActivePage: UIImage = UIImage(named: "icon-dot")!
override var numberOfPages: Int {
didSet {
updateDots()
}
}
override var currentPage: Int {
didSet {
updateDots()
}
}
override func awakeFromNib() {
super.awakeFromNib()
self.pageIndicatorTintColor = UIColor.clear
self.currentPageIndicatorTintColor = UIColor.clear
self.clipsToBounds = false
}
func updateDots() {
var i = 0
for view in self.subviews {
var imageView = self.imageView(forSubview: view)
if imageView == nil {
if i == self.currentPage {
imageView = UIImageView(image: activePage)
} else {
imageView = UIImageView(image: inActivePage)
}
imageView!.center = view.center
view.addSubview(imageView!)
view.clipsToBounds = false
}
if i == self.currentPage {
imageView!.alpha = 1.0
imageView?.image = activePage
} else {
imageView!.alpha = 0.5
imageView?.image = inActivePage
}
i += 1
}
}
fileprivate func imageView(forSubview view: UIView) -> UIImageView? {
var dot: UIImageView?
if let dotImageView = view as? UIImageView {
dot = dotImageView
} else {
for foundView in view.subviews {
if let imageView = foundView as? UIImageView {
dot = imageView
break
}
}
}
return dot
}
}
then in your class just add these lines and you can use custom images for dot
//Custom page Control
let pageControl = CustomPageControl()
pageControl.updateDots()
intro.pageControl = pageControl
Hope it will work for you! #ynamao
You can see from the source code of SMPageControl that it isn't a subclass of UIPageControl. Which means the error is expected: UIPageControl is a completely unrelated type, to which the value cannot be cast.
The Objective-C you pointed to might work, but it's bad and wrong: inline cast to UIPageControl achieves nothing here and can cause internal inconsistencies.
This is exactly the kind of sloppiness that Swift compiler is designed to prevent, and it's doing its job well.
Your best bet is to forgo using this library in Swift code.