UICollectionView.scrollToItemAtIndexPath animation works weirdly with virtual keyboard - ios

When I call scrollToItemAtIndexPath method to scroll to bottom with keyboard-on,
collectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: .None, animated: true)
I always get this strange animation.
It first scrolls down to the end, and scrolls up again a bit, and scrolls down to the end again. Why this happens and how can I fix this?
I checked this is happening in iOS 9.3 simulator.

UICollectionView shows this behaviour when its contentInsets.top != 0.
I haven't set it explicitly, but it's been set automatically by UIViewController. This can be disabled by setting automaticallyAdjustsScrollViewInsets property.
automaticallyAdjustsScrollViewInsets = false
Then the strange animation magically goes away. Anyway, now my collection view has no top space, so it renders badly.
To make this to work, just set insets manually.
// collectionView.contentInset.top = 20 + 44
collectionView.scrollIndicatorInsets.top = 20 + 44
But if we set contentInset.top to non-zero value, it shows the weird animation again, so you shouldn't set it. Instead of, we should try another workaround. For example, you can adjust UICollectionViewFlowLayout.sectionInset.top if you are using flow layout.
collectionView.scrollIndicatorInsets.top = 20 + 44
let flowLayout = (collectionView.collectionViewLayout as? UICollectionViewFlowLayout)
flowLayout?.sectionInset.top = 20 + 44
Or you can put an empty section. Or whatever.
Now it works properly.

Related

Swift button frame height issue (viewDidLayoutSubviews)

I've got some square buttons that I'd like to add rounded corners to that are proportional to the button's height. In past versions of my app, I had implemented this feature without issues using viewDidLayoutSubviews(). For some reason, after pushing a new version of my app with other features I had tweaked, this section of code no longer functions as expected. Here is the code:
override func viewDidLayoutSubviews() {
for button in buttons {
button!.layer.shadowColor = UIColor.black.cgColor
button!.layer.shadowOffset = CGSize(width: 0, height: 1.0)
button!.layer.shadowOpacity = 0.4
button!.layer.shadowRadius = button!.frame.height / 40
button!.layer.cornerRadius = button!.frame.height / 10
}
Again, this block of code used to work just fine but for some reason it no longer works. What I am experiencing is much larger relative radii on smaller buttons (iPhone SE) compared to bigger buttons (iPads).
To troubleshoot, in viewDidLayoutSubviews(), I'm printing the button!.frame.height and I'm noticing that no matter what device I use the frame height is 395.5, which I believe is the correct size only on the 12.9" iPad. Therefore, the buttons look correct on the 12.9" iPad but the radii end up being too large on all of the smaller devices.
Any idea what's going on here? Why is it that they're all returning the same frame height even though they're visually very different sizes on the different devices?
I copy and pasted the above code into the viewWillAppear() method and
the problem was resolved. I then deleted the code from
viewWillAppear(), leaving me with my original code during posting of
question, and it is continuing to run as expected (working). What
could possibly be the cause of this intermittent behavior
The reason when you initialized the buttons in viewWillAppear and remove them but it still work because your button's frame did not change in the viewDidLayoutSubview method. And the viewDidLayoutSubview is invoked only controller's view is updated, rotated, or changed, which in your case it does not.
If you try to rotate your device you will see your parent view's frame changed.
For more information about view hierarchy. See this article
Try like this:-
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
for button in buttons {
button!.layer.shadowColor = UIColor.black.cgColor
button!.layer.shadowOffset = CGSize(width: 0, height: 1.0)
button!.layer.shadowOpacity = 0.4
button!.layer.shadowRadius = button!.frame.height / 40
button!.layer.cornerRadius = button!.frame.height / 10
}

Autoresizing tableview cell with fixed subview

I want to have a cell with a header with title and switch. When the switch is on, it should show the content expanding the cell with an animation. The header should be always visible and have a constant height.
The cell consists of a container view with 2 subviews, the header, and a body. The body has a label for the cell's content.
The cell has a delegate that tells the controller to reload the content like this:
tableView.beginUpdates()
tableView.endUpdates()
(Nothing in between. Should I specify something just to be sure?)
I have tried having a height constraint for the header and one for the body and when the switch updates its value, I update the body's constraint to the height of the label (the calculation is being done correctly). I have also tried having a single constraint for the container (constant header height + label height + padding) which also didn't work.
The controller has is the tableView's delegate. I'm not setting a estimated row height (should I?) and for heightForRowAt I'm returning UITableView.automaticDimension.
If you have any suggestion on how it could be done, even for a different view/subviews organization, please comment.
EDIT:
This was actually a silly mistake. I was updating the constraints after I was calling my delegate cell. For future reference I'm posting here the code.
func configure(with reminder: Reminder, delegate: CellUpdateDelegate) {
self.delegate = delegate
self.reminder = reminder
bodyLabel.text = reminder.body
headerTitle.text = reminder.title
let height = bodyLabel.requiredHeight + 16
containerHeight.constant = 48 + (alertSwitch.isOn ? height : 0 )
}
#objc func switching() {
let height = bodyLabel.requiredHeight + 16
containerHeight.constant = 48 + (alertSwitch.isOn ? height : 0 )
delegate?.didSelect(reminder)
}

UIView does not get refreshed after NSLayoutConstraint constant is changed

I want to change UITableView height depending upon user interaction with a button. When user clicks on the button, UITableView gets expanded and click on the same button UITableView height is decreased only to show 1st row. Following is my code
tblFaults?.setNeedsUpdateConstraints()
self.view.setNeedsUpdateConstraints()
if (currentNumber == 1)
{
currentNumber = 2
var newFrame = tblFaults?.frame
newFrame?.size.height += 67;//tried to change frame as well
tblFaults?.frame = newFrame!
faultViewHeight?.constant = 115
//faultView height is tableview height constraint
}
else
{
currentNumber = 1
var newFrame = tblFaults?.frame
newFrame?.size.height -= 67;
tblFaults?.frame = newFrame!
faultViewHeight?.constant = 48
}
tblFaults?.reloadData()
//self.view.setNeedsLayout()
//self.view.setNeedsDisplay()
I tried setNeedsDisplay, setNeedsLayout as well as layoutSubviews() after the code. But view does not get refreshed immediately after button click but gets refreshed only after next user interaction (e.g. scrollview change etc)
Try this :
faultViewHeight?.constant = 48
tblFaults.layoutIfNeeded() // you need to call this method
Thanks everyone.
Actually only constraint constant value change and tableview.reloadData() was enough. The BIG mistake I did was, I was checking the execution on simulator. I tried this on device and it worked perfectly fine. On simulator I was facing the issue even after trying self.view.layoutIfNeeded(). I still do not know why this is the behaviour on simulator and not on device. But eventually app has to run on device and its working there. :)
Thank you all.

Animated width label with swift

I want to change the width of my label since my swift code.
If possible, do it in a annimation prograssif for a change. Is it possible ?
When I do:
self.lblChoice1.frame.size.width += 50
My exchange label width not ... Why?
Thank you in advance.
EDIT :
The first response is not working.
print(self.lblChoice1.frame.size.width)
self.lblChoice1.frame.size.width += 150
self.lblChoice1.contentMode = UIViewContentMode.Redraw
self.lblChoice1.setNeedsDisplay()
print(self.lblChoice1.frame.size.width)
This code displays me well in my console:
150.0
300.0
But my label size does not change the display ...
You are changing the frame, but you aren't telling the view that it needs to redraw itself. After changing the frame you should then call
self.lblchoice1.setNeedsDisplay()
Although you may need to change the label's content mode to UIViewContentModeRedraw to make sure it redraws.
Although it would be better to use UIView block animation methods to do this.
I dont think you can edit the UILabel's frame directly. You should change entire frame instead.
var lblChoice1Frame = self.lblChoice1.frame
lblChoice1Frame.size.width += 150
UIView.animateWithDuration(0.3) { () -> Void in
self.lblChoice1.frame = lblChoice1Frame
}

IOS:UITabbar item click again and again it is reducing the UITabbar button item size in IOS 7

I'm took the Tabbar viewcontroller in this ,I added the 5 item and .I given the image insects is (24,0,0,6).
All button images are added in xib [under the Bar item -->image]Please help.
Thanks.
Adding to a similar answer here:
iOS Tab Bar icons keep getting larger
Not sure if this is an iOS7 bug but I've noticed that image insets need to be balanced.
You have specified insets for top and right but:
if you set a top inset, in order to balance it, you need to set the negative of it to the bottom inset
if you set a right inset, in order to balance it, you need to set the negative of it to the left inset
So, instead of having image insets like (24,0,0,6), use balanced image insets such as UIEdgeInsetsMake(24,-6,-24,6)
Doing so should protect your tabBarItem image from getting whacked on every tap.
If this doesn't suit your requirements, then redesign your tabBarItem image so you can have balance insets or... no insets at all.
Here's the workaround for a bug I've encountered with UITabBarController's UITabBar. If I tap a UITabBarItem once after it's selected, the icon shrinks. What I'd like to do is disable touches. UITabBarItem only has a setting for isEnabled, which grays it out if I set it to false...not what I was looking for.
I used a derivative of this answer to figure it out. With a UITabBarController with 3 tabs, printing tabBarController.subviews, I saw 3 UITabBarButtons and a UIBarBackground. The origin of UIBarBackground's frame was always (0, 0), putting it at the front of the sorted array, so I really don't need to know what the subview is, just "where it is" and whether it will always be there. The UIBarBackground is always going to be at the front of an array of tabBarController.subviews sorted by frame.minX, so I just need to remove it from the front.
Solution
Here's what the extension looks like:
extension UITabBarController {
var buttonViews: [UIView] {
var tabBarButtons = tabBar.subviews.sorted(by: {$0.frame.minX < $1.frame.minX})
tabBarButtons.removeFirst()
return tabBarButtons
}
}
I also created a struct in my Constants file, so I don't have to remember tab names:
struct TabBarItem {
static let firstTab = 0
static let secondTab = 1
static let thirdTab = 2
}
...and finally, where to use it:
In viewDidAppear (NOT viewDidLoad), add the following line to disable the UITabBarItem that you don't want to disable, but not gray out:
tabBarController?.buttonViews[TabBarItem.firstTab].isUserInteractionEnabled = false
In viewWillDisappear, re-enable the tab, as follows:
tabBarController?.buttonViews[TabBarItem.firstTab].isUserInteractionEnabled = true

Resources