UIImageView not gets Focused on tvOS - ios

I have the following UIViewController which i am doing in story board.
The problem i am facing here is that when i try to swipe right on tv remote my UIImageView does not get any focus. Infact no focus event happen, it try to put some logs in
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator)
but there is no focus event updated. Earlier i have UIImageView directly added on self.view in storyboard but i read somewhere the UIImageView itself is not focusable so i put my UIImageView inside another UIView, but it still does not gets any focus. I have aligned top edges of UIView which contains UIImageView and UITextView.
Any help will be highly appreciated
On a side note i also tried to do with UIFocusGuide but that didn't work also. I think i don't need to use UIFocusGuide because it a basic right swipe focus thing.

UIImageView is not by default focusable, and will not get focus effect, you can do two things to make it focusable.
Subclass UIImageView and in subclass override canBecameFocused and
return YES, and then override didUpdateFoucsInContext and update appearance for focus state, like if focus make it bigger or change color etc.
Second option is add it to a view which is focusable and then set imageView.adjustImageWhenAncestorFocused = YES also set clipToBounds = NO on superview
So In your case as you are adding imageView to a view then that view should return YES/True from canBecameFocused.

#C_X is right. The most important thing is that if UIImageView is added to self.view then don't forget to subclass self.view and override the canBecameFofused method.

Related

iOS 11 on NavigationBar pull down - height of bar changes?

What I want to to: I want to drag down the whole view of a viewController to dismiss to the parent viewController using a pan gesture recognizer.
The Problem: When I drag the view down, the navigationBar decreases its height and does not look good. When the view returns to its original position, the navigationBar returns to the default size. I want the navigationBar to stay at its size. I also tried to use the new large titles and some other properties of the navigationController/-bar, but that did not solve it.
Note: Everything worked fine already before iOS 11.
My code:
override func viewDidLoad() {
super.viewDidLoad()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(dragViewDown(_:)))
navigationController!.view.addGestureRecognizer(panGesture)
}
#IBAction func dragViewDown(_ gesture: UIPanGestureRecognizer) {
if let dragView = gesture.view {
let translation = gesture.translation(in: dragView)
dragView.center.y = (dragView.center.y + translation.y)
gesture.setTranslation(CGPoint.zero, in: dragView)
}
}
This test project only has one viewController and does not provide the dismissal, but the problem is the same as in my working project.
I also uploaded the project to GitHub: https://github.com/maddinK7/navitationBar-pull-down-problem
Does anyone have an idea how to solve this? Thanks in advance.
I want the navigationBar to stay at its size
It is staying at its size. If you check the navigation bar's bounds size height before, during, and after the drag, you will see that it remains the same (probably 44) at all times. What's changing is the drawing extension that causes the drawing of the nav bar to extend up behind the status bar. It can't do that when you pull the whole thing away from the top of the screen, because it is not at the top next to the status bar any more. iOS 11 is more strict about the way it performs this drawing extension, probably because it has to do it in a special way on the iPhone X.
So, let's make sure you're doing this correctly:
Make sure that the navigation bar has a top constraint pinned to the safe area layout guide's top, with a constant of zero.
Make sure that the navigation bar has a delegate that returns .topAttached from position(forBar:).
If you are doing both those things and it doesn't help, you'll have to implement this in some other way entirely. Making the view directly draggable like this, without a custom parent view controller, was always dubious.
When UINavigationController attached top, system will add safe area top margin in the navigation background.
(NOTICE: Background margin will not changed when offset value is between 1 and 0)
So you have to handle attached/detached top event by handle gesture offset to change the right offset and content insets.
You can try the solution in my lib example. ;)
My example include UITableViewController in the UINavigationController, so it will relatively complex.
https://github.com/showang/OverlayModalViewController

Attaching UIButton on top of UIScrollView or UITableView

What is the best approach for attaching a UIButton on top of UIScrollView or UITableView so when the view is scrolled, the button stays in its place.
Here examples below:
UIButton stays in the right bottom corner when the view is scrolled.
google+ app example
yahoo mail app example
I think this should work. Lay Out your button in a view that is outside of the tableviewcontroller. Then drag an outlet to the tableviewcontroller file. Then add it in code. This code would hold it at the top of the screen.
#IBOutlet var buttonView: UIView!
override func viewDidLayoutSubviews() {
self.view.addSubview(buttonView)
}
override func scrollViewDidScroll(scrollView: UIScrollView) {
var rect = self.buttonView.frame
rect.origin.y = max(0,scrollView.contentOffset.y + scrollView.contentInset.top)
self.buttonView.frame = rect
}
Thank you all for great answers!
I got it worked through storyboard by moving the button from scrollView to View itself. That way it's attached on UIView and it's independent of scrollview.
storyboard snapshot
So now the structure is:
- View
- ScrollView
- Button
Before it was:
- View
- ScrollView
- Button
There are many ways to go about doing this but two that I use most often are as follows.
One approach is embedding the view controller within a navigation controller. This will set a bar on the top and bottom if you choose that you can place bar button items upon.
Another approach is to place a UIView along the top and snap the constraints to the left, right, and top with 0 no-margin. Then set the height. I usually use 40px for the height but you can use what is applicable to your needs. After that you can place a button in that UIView and then set constraints on it to keep in in place.
In my experience, this isn't reliably possible to do with the scrollView itself.
My solution is usually to put anything that needs to float above the tableView/scrollView in a plain ViewController that also contains the tableView/scrollView parent.
If you're using storyboards with a UITableViewController scene, this will likely mean you need to use another scene with UIViewController with a container that has your UITableViewController.
For UITableView use tableHeaderView. For UIScrollView you need to create a separate view not in the scroll view's hierarchy.
Another solution is to put your UIButton in a UIToolbar, and then make the toolbar a child of the UINavigationController's view. After that, in viewDidLayoutSubviews, you can set the rect of the toolbar to sit just below the navigation bar and offset the top of the UIScrollView or UITableView.
Add button which you want in the storyboard.
Design your scrollview
self.view.sendSubviewToBack(scrollViewObj)(in the code)
This worked for me.

UIScrollView scroll over background view

I have a UIScrollView (with a clear background) and behind it I have a UIImage that takes up about 1/3 of the devices height. In order to initial display the image which is sitting being the scroll view I set the scrollviews contentInset to use the same height as the image. This does exactly what I want, initialing showing the image, but scrolling down will eventually cover the image with the scroll views content.
The only issue is I added a button onto of the image. However it cannot be touched because the UIScrollView is actually over the top of it (even though the button can be seen due to the clear background). How can I get this to work.
Edit:
The following solved the problem:
//viewdidload
self.scrollView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "onScrollViewTapped:"))
...
func onScrollViewTapped(recognizer:UITapGestureRecognizer)
{
var point = recognizer.locationInView(self.view)
if CGRectContainsPoint(self.closeButton.frame, point) {
self.closeButton.sendActionsForControlEvents(UIControlEvents.TouchUpInside)
}
}
Thanks for the screenshots and reference to Google maps doing what you're looking for, I can see what you're talking about now.
I noticed that the image is clickable and is scrolled over but there is no button showing on the image itself. What you can do is put a clear button in your UIScrollView that covers the image in order to make it clickable when you're able to see it. You're not going to be able to click anything under a UIScrollView as far as I can tell.
Please let me know if that works for you.
a simple solution is to reorder the views in the document out line. The higher the view in the outline, the lower the view is as a layer
Two things to test:
1) Make sure the image that contains the button has its userInteractionEnabled set to true (the default is false). Although, since the button is a subview and added on top of the ImageView (I assume) then this might not help.
2) If that doesn't help, can you instead add the button as a subview of the UIScrollView and set its position to be where the image is? This way it should stay on the image and will be hidden as the user scrolls down, but clickable since it is a child of the ScrollView.
Some code and/or images would help as well.
I think the way to do this is to subclass whatever objects are in your UIScrollView and override touches began / touches ended. Then figure out which coordinates are being touched and whether they land within the bounds of your button
e.g. in Swift this would be:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent?) {
println("!!! touchesBegan")
if var touch = touches.first {
var touchObj:UITouch = touch as! UITouch
println("touchesBegan \(touchObj.locationInView(self))") //this locationInView should probably target the main screen view and then test coordinates against your button bounds
}
super.touchesBegan(touches, withEvent:event!)
}
See :
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIResponder_Class/index.html#//apple_ref/occ/instm/UIResponder/touchesBegan:withEvent:
And:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITouch_Class/index.html#//apple_ref/occ/instm/UITouch/locationInView:
You should subclass UIScrollView and override -hitTest:withEvent: like so, to make sure it only eats touches it should.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *const inherited = [super hitTest:point withEvent:event];
if (inherited == self) return nil;
return inherited;
}
Also make sure to set userInteractionEnabled to YES in your image view.
There is 2 way you can checked that weather touch event is fire on UIButton or not?
Option 1 : You need to add UITapGesture on UIScrollView. while tapping on UIScrollView. Tap gesture return touch point with respect to UIScrollView. you need to convert that touch point with respect to main UIView(that is self.view) using following method.
CGPoint originInSuperview = [superview convertPoint:CGPointZero fromView:subview];
after successfully conversation, you can checked that weather touch point is interact with UIButton frame or what. if it interact then you can perform you action that you are going to perform on UIButton selector.
CGRectContainsPoint(buttonView.frame, point)
Option 2 : Received first touch event while user touch on iPhone screen. and redirect touch point to current UIViewController. where you can check interact as like in option 1 describe. and perform your action.
Option 2 is already integrated in one of my project successfully but i have forgot the library that received first tap event and redirect to current controller. when i know its name i will remind you.
May this help you.

Why a UIButton on UITableViewCell only drawn darker when touch gesture continued for a short time

Pressing the button quickly and not holding for a short time, will not highlight the button.
Different from a UIButton on a common UIView.
Like the head photo in official Twitter client got same issue.
Instagram client seems solved this, all buttons works fine.
Find same question here:
Why doesn't UIButton showsTouchWhenHighlighted work when the button is on a UITableViewCell?
But I still don't know how to fix it.
Well... a UITableView is a subclass of UIScrollView and the UIScrollView class is known to eat touches for it's own purpose.
When it realizes the touch was not meant for it, it passes it to it's immediate subview.
This feature is the delaysContentTouches property (which by default is YES).
Which is why, the UIButton shows it's highlighted state only after a extended touch because the touch event was with the UITableView for a short while until it determined whether the touch was meant for scrolling or swiping the cell and on realizing the touch was for neither, it immediately passes the touch event to the subView directly below it.
In case of a quick-tap, the button's highlighted state is bypassed due to this delay and the target selector method is called directly.
To show the highlighted state of the button in a UITableView (just as it would on a UIView) do:
For iOS7+:
In -viewDidLoad or anywhere appropriate do:
[yourTableViewObject setDelaysContentTouches:NO];
Also... The cell.subviews has a class UITableViewCellScrollView which apparently is another scrollView and we need to disable the delaysContentTouches property of this class as well.
So... in the -cellForRowAtIndexPath: method (just before return cell;) do:
NSArray *test = cell.subviews;
for (UIView *currentView in cell.subviews) {
if ([NSStringFromClass([currentView class]) isEqualToString:#"UITableViewCellScrollView"]) {
UIScrollView *svTemp = (UIScrollView *) currentView;
[svTemp setDelaysContentTouches:NO];
break;
}
}
For iOS 6-:
In iOS6, the cell.subviews has a UITableViewCellContentView class which is not a scrollView subclass and so all it takes is setting one parameter for the tableView alone.
So, in -viewDidLoad or anywhere appropriate, this is all that you need:
[yourTableViewObject setDelaysContentTouches:NO];
PS: By doing this, it will mess up with the scrolling of the tableView so use your better judgement.

Dragging uiimageview into a "trashcan" while zoomed in on a uiscrollview

Hey everyone. I've searched the web and really haven't found a problem quite like this. My set up is as follows: I am trying to make an app where a user drags uiimageviewss on a uiview inside a uiscrollview (which can zoom in), but also drag the image to a trashcan to be deleted. When the scrollview is zoomed in, the user can still drag because I have subclassed uiimageview and overridden the touchesMoved method. I want the user to be able to drag the uiimageviews to a trashcan in the bottom right corner of the screen. What I implemented already works when the uiscrollview is normal zoom scale, but when you zoom in you cannot drag the image to the trashcan, because well, the trashcan is a subview of the controller's view itself (this was so that it wouldn't zoom with the uiview) and of course is not in the right coordinates of the screen for the uiscrollview.
I've found a view method called ConvertRect that converts a rectangle to the coordinates of whatever view you give to the method, but this doesn't seem to be working. I do a check within each mapimage(the imageview subclass) that checks the frame of the uiimageview intersects the frame of the trashcan. So I try this method before I do that check, but like I said it doesn't seem to be working.
So with all that complicatedness, let me say in a nutshell my problem. I want to drag images over to a trashcan regardless of what zoom level at uiscrollview is at. I can make the trashcan part of the uiscrollview but I need a way to make it keep in the bottom right corner and also not zoom when the uiscrollview does. Any ideas would be so greatly appreciated, thanks guys. :)
I had the exact same challenge.
In can just describe it in short:
When you start dragging the image REMOVE it from the UISCrollView and add it as a subview the the main view of your ViewController.
make sure that the image won't get lost in the process (means: someone still retaining it).
While dragging, constantly check to see if your image entered or left the boundaries of the trash can. you can do it with the method bellow:
-(void) checkForTrashCan
{
CGRect rect = [self.delegate.trashCan convertRect:self.view.frame fromView:self.view.superview];
if ([self.delegate.trashCan rectIntersectBounds:rect])
{
if (NOT self.isInTrashMode)
{
[self enterTrashMode];
}
}
else
{
if (self.isInTrashMode)
{
[self exitTrashMode];
}
}
}
When the method rectIntersectBounds is just a simple extension method for UIView class that i wrote:
-(BOOL) rectIntersectBounds : (CGRect) rect
{
return CGRectIntersectsRect(self.bounds, rect);
}
When the drag ended check if your view is inside the trash can, and act according to the result.

Resources