report an error Ambiguous reference to member 'subviews' - ios

Changes in the table a multiple-choice button style
NSArray *subviews = [[tableView cellForRowAtIndexPath:indexPath] subviews];/* An array of */
for (id subCell in subviews) {
if ([subCell isKindOfClass:[UIControl class]]) {
for (UIImageView *circleImage in [subCell subviews]) {
circleImage.image = [UIImage imageNamed:#"CellButtonSelected"];
}
}
}
/* change */
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) /* table Proxy method */
var subviews = tableView.cellForRowAtIndexPath(indexPath)!.subviews
for subCell: AnyObject in subviews {
if (subCell is UIControl) {
for circleImage: UIImageView in subCell.subviews { /* report an error Ambiguous reference to member 'subviews' */
/* How to solve?*/
circleImage.image = UIImage(named: "CellButtonSelected")!
}
}
}
report an error Ambiguous reference to member 'subviews'
/**Swift, the table of custom boxes, no system, how to change/

Maybe something like this:
for circleImage: UIView in subCell.subviews! {
if let circleImage = circleImage as? UIImageView {
circleImage.image = UIImage(named: "CellButtonSelected")!
}
}

The button click event custom selection button collapsed
for row in 0...self.collectionBookList.count {
let indexPath=NSIndexPath.init(forRow: row, inSection: 0)
self.myTableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition:.Bottom)
let subviews = self.myTableView.cellForRowAtIndexPath(indexPath)!.subviews
for subCell: AnyObject in subviews {
if (subCell is UIControl) {
for circleImage: UIView in subCell.subviews! {
if let circleImage = circleImage as? UIImageView {
circleImage.image = UIImage(named: "delete")!
}
}
}
}
}

Related

Swift UI test, how to check if a cell has an imageview

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")
}
}

UICollectionView with gesture recogniser

I have a UICollectionView which has a long press gesture attached to it. It works fine when i'm pressing on a cell but if the touched area isn't a cell the app crashes with EXC_BREAKPOINT
It crashes on the
if let indexPath : NSIndexPath = collectView.indexPathForItemAtPoint(point)! {
line. I believe that i need to check that the point is actually a cell but i'm not sure what to check for
the code is as follows
#IBAction func longPressCell(sender: UILongPressGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.Began) {
if let point : CGPoint = sender.locationInView(self.collectionView) {
if let collectView = self.collectionView {
if let indexPath : NSIndexPath = collectView.indexPathForItemAtPoint(point)! {
let adopt : UserPet = self.fetchedResultsController.objectAtIndexPath(indexPath) as! UserPet
NSLog("Adopt: \(adopt)")
}
}
}
}
}
collectView.indexPathForItemAtPoint(point) != nil {
Solved it

Override layout.subviews deletion control in Swift

I am trying to implement similar code to this in a Swift project
https://gist.github.com/joaofranca/3159618
I am having difficulty getting the class for the subview in the NSStringFromClass sections.
I have tried NSStringFromClass(subview.class) but Swift doesn't like it.
Do you know how to use this in Swift?
Thanks,
Andy
Update:
You can call classForCoder on classes derived from NSObject:
var s: NSObject = "hello"
var i: NSObject = 3
NSStringFromClass(s.classForCoder) // "NSString"
NSStringFromClass(i.classForCoder) // "NSNumber"
Original answer:
In Swift, instead of identifying a class by name, use is:
Objective-C:
for (UIView *subview in self.subviews) {
if([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellDeleteConfirmationControl"]) {
// do magic here
...
}else if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellEditControl"]) {
// do magic here
...
}else if ([NSStringFromClass([subview class]) isEqualToString:#"UITableViewCellReorderControl"]) {
// do magic here
Swift:
for subview in self.subviews as [UIView] {
if subview is UITableViewCellDeleteConfirmationControl {
// do magic here
...
} else if subview is UITableViewCellEditControl {
// do magic here
...
} else if subview is UITableViewCellReorderControl {
// do magic here
Swift 2.0 ->
Override the layoutSubviews()
class MyCustomCell: UITableViewCell {
override func aSubView() {
super.layoutSubviews()
for aSubView in self.subviews {
if String(aSubView.classForCoder).rangeOfString("UITableViewCellDeleteConfirmationView") != nil {
// Do whatever you want to do with default Delete Button.
// aSubView is the Delete Button.
aSubView.frame = CGRectMake(aSubView.frame.origin.x, aSubView.frame.origin.y, aSubView.frame.size.width, aSubView.frame.size.height - 10)
}
}
}
}
swift 3.0 -> using constraints.
override func layoutSubviews() {
super.layoutSubviews()
for aSubView in self.subviews as [UIView] {
if String(describing: aSubView.classForCoder).range(of: "UITableViewCellDeleteConfirmationView") != nil {
aSubView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
aSubView.widthAnchor.constraint(equalToConstant: 50),
aSubView.heightAnchor.constraint(equalToConstant: 50),
])
}
}
}

Shared ancestor between two views

What is the most efficient way to find the lowest common ancestor between two UIView instances?
Short of implementing Lowest Common Ancestor, are there any UIKit APIs that can be leveraged to find it?
NSView has ancestorSharedWithView: so I suspect this might be added sooner than later to iOS.
I'm currently using this quick and dirty solution, which is inefficient if the given view isn't a sibling or direct ancestor.
- (UIView*)lyt_ancestorSharedWithView:(UIView*)aView
{
if (aView == nil) return nil;
if (self == aView) return self;
if (self == aView.superview) return self;
UIView *ancestor = [self.superview lyt_ancestorSharedWithView:aView];
if (ancestor) return ancestor;
return [self lyt_ancestorSharedWithView:aView.superview];
}
(for those implementing a similar method, the unit tests of the Lyt project might be helpful)
It's not too hard, using -isDescendantOfView:.
- (UIView *)my_ancestorSharedWithView:(UIView *)aView
{
UIView *testView = self;
while (testView && ![aView isDescendantOfView:testView])
{
testView = [testView superview];
}
return testView;
}
Swift 3:
extension UIView {
func findCommonSuperWith(_ view:UIView) -> UIView? {
var a:UIView? = self
var b:UIView? = view
var superSet = Set<UIView>()
while a != nil || b != nil {
if let aSuper = a {
if !superSet.contains(aSuper) { superSet.insert(aSuper) }
else { return aSuper }
}
if let bSuper = b {
if !superSet.contains(bSuper) { superSet.insert(bSuper) }
else { return bSuper }
}
a = a?.superview
b = b?.superview
}
return nil
}
}
A functional alternative:
Swift (assuming the use of your favourite OrderedSet)
extension UIView {
func nearestCommonSuperviewWith(other: UIView) -> UIView {
return self.viewHierarchy().intersect(other.self.viewHierarchy()).first
}
private func viewHierarchy() -> OrderedSet<UIView> {
return Set(UIView.hierarchyFor(self, accumulator: []))
}
static private func hierarchyFor(view: UIView?, accumulator: [UIView]) -> [UIView] {
guard let view = view else {
return accumulator
}
return UIView.hierarchyFor(view.superview, accumulator: accumulator + [view])
}
}
Objective-C (implemented as a category on UIView, assuming the existence of a firstObjectCommonWithArray method)
+ (NSArray *)hierarchyForView:(UIView *)view accumulator:(NSArray *)accumulator
{
if (!view) {
return accumulator;
}
else {
return [self.class hierarchyForView:view.superview accumulator:[accumulator arrayByAddingObject:view]];
}
}
- (NSArray *)viewHierarchy
{
return [self.class hierarchyForView:self accumulator:#[]];
}
- (UIView *)nearestCommonSuperviewWithOtherView:(UIView *)otherView
{
return [[self viewHierarchy] firstObjectCommonWithArray:[otherView viewHierarchy]];
}
Here's a little shorter version, as a category on UIView:
- (UIView *)nr_commonSuperview:(UIView *)otherView
{
NSMutableSet *views = [NSMutableSet set];
UIView *view = self;
do {
if (view != nil) {
if ([views member:view])
return view;
[views addObject:view];
view = view.superview;
}
if (otherView != nil) {
if ([views member:otherView])
return otherView;
[views addObject:otherView];
otherView = otherView.superview;
}
} while (view || otherView);
return nil;
}
Your implementation only check two view level in one iteration.
Here is mine:
+ (UIView *)commonSuperviewWith:(UIView *)view1 anotherView:(UIView *)view2 {
NSParameterAssert(view1);
NSParameterAssert(view2);
if (view1 == view2) return view1.superview;
// They are in diffrent window, so they wont have a common ancestor.
if (view1.window != view2.window) return nil;
// As we don’t know which view has a heigher level in view hierarchy,
// We will add these view and their superview to an array.
NSMutableArray *mergedViewHierarchy = [#[ view1, view2 ] mutableCopy];
UIView *commonSuperview = nil;
// Loop until all superviews are included in this array or find a view’s superview in this array.
NSInteger checkIndex = 0;
UIView *checkingView = nil;
while (checkIndex < mergedViewHierarchy.count && !commonSuperview) {
checkingView = mergedViewHierarchy[checkIndex++];
UIView *superview = checkingView.superview;
if ([mergedViewHierarchy containsObject:superview]) {
commonSuperview = superview;
}
else if (checkingView.superview) {
[mergedViewHierarchy addObject:superview];
}
}
return commonSuperview;
}
Mine is a bit longer and without using UIKit isDescendant function.
Method 1: With a method of finding LCA in trees. Time complexity:O(N), Space complexity: (1)
func findCommonSuper(_ view1:inout UIView, _ view2:inout UIView) -> UIView? {
var level1 = findLevel(view1)
var level2 = findLevel(view2)
if level1 > level2 {
var dif = level1-level2
while dif > 0 {
view1 = view1.superview!
dif -= 1
}
} else if level1 < level2 {
var dif = level2-level1
while dif > 0 {
view2 = view2.superview!
dif -= 1
}
}
while view1 != view2 {
if view1.superview == nil || view2.superview == nil {
return nil
}
view1 = view1.superview!
view2 = view2.superview!
}
if view1 == view2 {
return view1
}
return nil
}
func findLevel(_ view:UIView) -> Int {
var level = 0
var view = view
while view.superview != nil {
view = view.superview!
level += 1
}
return level
}
Method 2: Inserting one view's ancestors to set and then iterating second ones ancestors. Time complexity: O(N), Space complexity: O(N)
func findCommonSuper2(_ view1:UIView, _ view2:UIView) -> UIView? {
var set = Set<UIView>()
var view = view1
while true {
set.insert(view)
if view.superview != nil {
view = view.superview!
} else {
break
}
}
view = view2
while true {
if set.contains(view) {
return view
}
if view.superview != nil {
view = view.superview!
} else {
break
}
}
return nil
}
Swift 2.0:
let view1: UIView!
let view2: UIView!
let sharedSuperView = view1.getSharedSuperview(withOtherView: view2)
/**
* A set of helpful methods to find shared superview for two given views
*
* #author Alexander Volkov
* #version 1.0
*/
extension UIView {
/**
Get nearest shared superview for given and otherView
- parameter otherView: the other view
*/
func getSharedSuperview(withOtherView otherView: UIView) {
(self.getViewHierarchy() as NSArray).firstObjectCommonWithArray(otherView.getViewHierarchy())
}
/**
Get array of views in given view hierarchy
- parameter view: the view whose hierarchy need to get
- parameter accumulator: the array to accumulate views in
- returns: the list of views from given up to the top most view
*/
class func getHierarchyForView(view: UIView?, var accumulator: [UIView]) -> [UIView] {
if let superview = view?.superview {
accumulator.append(view!)
return UIView.getHierarchyForView(superview, accumulator: accumulator)
}
return accumulator
}
/**
Get array of views in the hierarchy of the current view
- returns: the list of views from cuurent up to the top most view
*/
func getViewHierarchy() -> [UIView] {
return UIView.getHierarchyForView(self, accumulator: [])
}
}
Swift 5 version of Carl Lindberg's solution:
func nearestCommonSuperviewWith(other: UIView) -> UIView? {
var nearestAncestor: UIView? = self
while let testView = nearestAncestor, !other.isDescendant(of: testView) {
nearestAncestor = testView.superview
}
return nearestAncestor
}

Present popover from cells accessory button?

I'm having a hard time presenting a popover correctly from the accessoryButton of a tableviewCell.
The reason I'm not using accessory view is because the cell is in edit mode and I couldn't display both the green plus sign + custom accessory view.. Maybe I overlooked something on that front?
Currently my popover shows correctly, but that's only the case for this configuration since I set a static distance from the origin... Any Ideas how to solve this?
Code:
-(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if (![self duplicateDayContent]) {
duplicateDayContent = [[self storyboard]instantiateViewControllerWithIdentifier:#"CopyDay"];
[duplicateDayContent setDelegate:self];
duplicateDayPopover = [[UIPopoverController alloc]initWithContentViewController:duplicateDayContent];
duplicateDayPopover.popoverContentSize = CGSizeMake(320, 600);
}
CGRect rect = CGRectMake(cell.bounds.origin.x+800, cell.bounds.origin.y+10, 50, 30);
[duplicateDayPopover presentPopoverFromRect:rect inView:cell permittedArrowDirections:UIPopoverArrowDirectionRight animated:YES];
}
This works quite nicely for a cell with accessoryType .detailDisclosureButton:
if let ppc = vc.popoverPresentationController, let cell = tableView.cellForRow(at: indexPath) {
ppc.sourceView = cell
ppc.sourceRect = CGRect(x: cell.bounds.width - 58, y: cell.bounds.height/2 - 11, width: 22, height: 22)
vc.modalPresentationStyle = .popover
}
present(vc, animated: true, completion: nil)
You would typically do this in tableView(_ accessoryButtonTappedForRowWith indexPath:)
You can also test the position of the calculated frame with a marker:
let marker = UIView(frame: ppc.sourceRect)
marker.backgroundColor = UIColor.red.withAlphaComponent(0.2)
cell.addSubview(marker)
this code from this thread helped me: How to correctly present a popover from a UITableViewCell with UIPopoverArrowDirectionRight or UIPopoverArrowDirectionLeft
thanks to rachels hint
UIView *accessoryView = cell.accessoryView; // finds custom accesoryView (cell.accesoryView)
if (accessoryView == nil) {
UIView *cellContentView = nil;
for (UIView *accView in [cell subviews]) {
if ([accView isKindOfClass:[UIButton class]]) {
accessoryView = accView; // find generated accesoryView (UIButton)
break;
} else if ([accView isKindOfClass:NSClassFromString(#"UITableViewCellContentView")]) {
// find generated UITableViewCellContentView
cellContentView = accView;
}
}
// if the UIButton doesn't exists, find cell contet view (UITableViewCellContentView)
if (accessoryView == nil) {
accessoryView = cellContentView;
}
// if the cell contet view doesn't exists, use cell view
if (accessoryView == nil) {
accessoryView = cell;
}
}
In swift, this has worked for me:
Create a popover presentation segue, then use prepareForSegue to configure the UIPopoverPresentationController of the destination view controller:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == Storyboard.providerInfoSegue) {
if let vc = segue.destinationViewController.contentViewController as? /*DestinationViewControllerType*/ {
//Configure view controllers here
if let popOverPresentationController : UIPopoverPresentationController = vc.popoverPresentationController {
if let cell = tableView.cellForRowAtIndexPath(selectedAccessoryIndexPath) {
var accessoryView: UIButton!
for accView in cell.subviews {
if accView is UIButton {
accessoryView = accView as? UIButton
break
}
}
popOverPresentationController.delegate = self
popOverPresentationController.sourceView = cell
popOverPresentationController.sourceRect = accessoryView.frame
popOverPresentationController.permittedArrowDirections = UIPopoverArrowDirection.Right
}
}
}
}
}

Resources