I'm trying to place a date field to the left of a cell in UITableView that is revealed when the user slides the cell from left to right. When released, the cell should spring back into place, hiding the date field. Does anyone know how to do this? Is it handled in
func tableView(_ tableView: UITableView, editActionsForRowAt: IndexPath) -> [UITableViewRowAction]? {
or defined somehow within a custom cell?
I wouldnt use the editing delegate as you posted.
Put the cells content in a UIView, in a UIScrollView, then setup the information behind the UIScrollView you wish to reveal.
Then on the scroll delegate, scrollViewDidEndDragging, set the contentOffset back to 0 animated.
EDIT: Pan gesture example
If you wish for the simplest route, setup your cell with a customContentView that is the size of your cell, add the pan gesture to this, then setup and buttons you want, in my example below I had a button each side.
Note: This is swift 2, but should be easily translatable, I made this a longgg time ago when swift 2 was released, it could do with refactoring
var savedX = 0 as CGFloat
var buttonWidth = 60 as CGFloat
var open = false
func panGestureHandler(gesture: UIPanGestureRecognizer) {
if gesture.state == .Changed {
let translation = gesture.translationInView(tagView)
let difference = -translation.x
if difference > 0 && !allowScrollRight {
return
}
let newConstant = savedX + difference
tagViewCenterXConstraint.constant = newConstant
let alpha = abs(tagViewCenterXConstraint.constant) / buttonWidth
deleteButton.alpha = min(alpha, 1)
followButton.alpha = min(alpha, 1)
if let action = swipe {
action(self)
}
}
if gesture.state == .Ended {
let translation = gesture.translationInView(self)
let trans = fabs(translation.x)
open = !open && trans > buttonWidth
if open {
if(translation.x > 0){
resetRight(true)
} else {
if allowScrollRight {
resetLeft(true)
}
}
} else {
resetView(true){
}
}
}
}
func resetLeft (animated : Bool) {
tagViewCenterXConstraint.constant = self.buttonWidth
savedX = self.buttonWidth
if animated {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [UIViewAnimationOptions.CurveEaseIn, UIViewAnimationOptions.BeginFromCurrentState], animations: { () -> Void in
self.tagView.layoutIfNeeded()
self.leftView.layoutIfNeeded()
self.rightView.layoutIfNeeded()
}, completion: { (finished) -> Void in
})
}
}
func resetRight (animated : Bool) {
tagViewCenterXConstraint.constant = -self.buttonWidth
savedX = -self.buttonWidth
if animated {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [UIViewAnimationOptions.CurveEaseIn, UIViewAnimationOptions.BeginFromCurrentState], animations: { () -> Void in
self.tagView.layoutIfNeeded()
self.leftView.layoutIfNeeded()
self.rightView.layoutIfNeeded()
}, completion: { (finished) -> Void in
})
}
}
func resetView (animated : Bool, completion: () -> Void ) {
tagViewCenterXConstraint.constant = 0
savedX = 0
open = false
if animated {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: [UIViewAnimationOptions.CurveEaseIn, UIViewAnimationOptions.BeginFromCurrentState], animations: { () -> Void in
self.tagView.layoutIfNeeded()
self.leftView.layoutIfNeeded()
self.rightView.layoutIfNeeded()
}, completion: { (finished) -> Void in
completion()
})
} else {
completion()
}
}
Related
I am looking for a slide out navigation. I am not using cocoa pods and storyboard. Any good reference available so that my side view slides on top of the centre view. I followed rayweinderlich sample code however the side menu comes under the main view.Here is the shadow code and animation code
if shouldShowShadow {
centerNavigationController.view.layer.shadowOpacity = 0.8
} else {
centerNavigationController.view.layer.shadowOpacity = 0.0
}
This is the animation method.The centre panel should be at the bottom and side menu should come over
func animateCenterPanelXPosition(targetPosition: CGFloat, completion: ((Bool) -> Void)? = nil) {
UIView.animate(withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 0.8,
initialSpringVelocity: 0,
options: .curveEaseInOut, animations: {
self.centerNavigationController.view.frame.origin.x = targetPosition
}, completion: completion)
}
func animateLeftPanel(shouldExpand: Bool) {
if shouldExpand {
currentState = .leftPanelExpanded
animateCenterPanelXPosition(
targetPosition: centerNavigationController.view.frame.width - centerPanelExpandedOffset)
} else {
animateCenterPanelXPosition(targetPosition: 0) { _ in
self.currentState = .bothCollapsed
self.leftViewController?.view.removeFromSuperview()
self.leftViewController = nil
}
}
}
This is how a side panel is added.MenuViewController is the side panel
func addChildSidePanelController(_ sidePanelController: MenuViewController) {
sidePanelController.delegate = centerViewController
view.insertSubview(sidePanelController.view, at: 0)
addChild(sidePanelController)
sidePanelController.didMove(toParent: self)
}
Hi I am using the PageController in my application.
I have inserted all the data on scrollView.
What I want do is, I want to move the scrollview automatically like page control with specific time.
When the scrollView reaches to last page it again recall all pages speedy and start from first onwards.
I want to load/get first page very smoothly after last page completion.
Find my code with following.
var slides:[Slide] = [];
var offSet: CGFloat = 0
override func viewDidLoad() {
super.viewDidLoad()
slides = createSlides()
setupSlideScrollView(slides: slides)
pageControl.numberOfPages = slides.count
pageControl.currentPage = 0
view.bringSubview(toFront: pageControl)
let timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(autoScroll), userInfo: nil, repeats: true)
}
#objc func autoScroll() {
let totalPossibleOffset = CGFloat(slides.count - 1) * self.view.bounds.size.width
if offSet == totalPossibleOffset {
offSet = 0 // come back to the first image after the last image
}
else {
offSet += self.view.bounds.size.width
}
DispatchQueue.main.async() {
UIView.animate(withDuration: 0.3, delay: 0, options: UIViewAnimationOptions.curveLinear, animations: {
self.scrollView.contentOffset.x = CGFloat(self.offSet)
}, completion: nil)
}
}
#objc func autoScroll() {
let totalPossibleOffset = CGFloat(slides.count - 1) * self.view.bounds.size.width
if offSet == totalPossibleOffset {
offSet = 0 // come back to the first image after the last image
}
else {
offSet += self.view.bounds.size.width
}
DispatchQueue.main.async() {
UIView.animate(withDuration: 0.1, delay: 0, options: UIView.AnimationOptions.curveLinear, animations: {
self.scrollView.scroll(topOffset:offSet, animated: true)
}, completion: nil)
}
}
extension UIScrollView {
// Bonus: Scroll
func scroll(topOffset:Float, animated: Bool) {
let ofSetValue = CGPoint(x: topOffset, y: 0)
setContentOffset(ofSetValue, animated: animated)
}
}
Use the following code working fine.
#objc func autoScroll() {
let totalPossibleOffset = CGFloat(slides.count - 1) * self.view.bounds.size.width
if offSet == totalPossibleOffset {
offSet = 0 // come back to the first image after the last image
}
else {
offSet += self.view.bounds.size.width
}
DispatchQueue.main.async() {
UIView.animate(withDuration: 0.0, delay: 0, options: UIViewAnimationOptions.curveLinear, animations: {
self.scrollView.contentOffset.x = CGFloat(self.offSet)
}, completion: nil)
}
}
I have a button and a collection view (horizontal) inside keyboard input view. Auto Layout is used. The button is hidden by default using Leading Constraint set to -50. When user start using collection view and contentOffset.x of collection view is greater than 80, The button will show. The code is working fine but animation is not working.
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.collectionView.contentOffset.x > 80 {
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.sideButtonLeadingConstraint.constant = 0
self.view.layoutIfNeeded()
}, completion: nil)
} else {
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.sideButtonLeadingConstraint.constant = -50
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
First, you shouldn't change you constraints inside animations block. Second, scrollViewDidScroll method is called a large number of times, and you should set some limitations for calling animation code inside it. Try something like this:
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let needsShow = collectionView.contentOffset.x > 80 && sideButtonLeadingConstraint.constant != 0
let needsHide = collectionView.contentOffset.x <= 80 && sideButtonLeadingConstraint.constant != -50
if needsShow {
sideButtonLeadingConstraint.constant = 0
} else if needsHide {
sideButtonLeadingConstraint.constant = -50
}
if needsShow || needsHide {
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}
}
Try as follows by updating the constant outside the animation block. It will do the update with animation effect.
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.collectionView.contentOffset.x > 80 {
self.sideButtonLeadingConstraint.constant = 0
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
else {
self.sideButtonLeadingConstraint.constant = -50
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseIn, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
}}
I'm doing an app project in Swift, and I have an UIView moving inside the main View (the one that it's by default in the ViewController).
I want to know the exact position of the UIView when a button is pressed (X and Y).
I tried things like this but doesn't work for me:
let X = pointView.frame.origin.x + pointView.frame.origin.x;
let Y = pointView.frame.origin.y + pointView.frame.origin.y;
or
let frame = self.view.convertRect(self.view.frame, fromView:pointView)
The code I have at the moment:
#IBAction func pressButton(sender: AnyObject) {
}
override func viewDidLoad() {
super.viewDidLoad()
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
let screenHeight = screenSize.height
UIView.animateKeyframesWithDuration(4, delay: 0, options: UIViewKeyframeAnimationOptions.Repeat, animations: { () -> Void in
UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 1.5/4.0, animations: { () -> Void in
//1
self.pointView.center = CGPointMake(2, screenHeight-2)
//2
})
UIView.addKeyframeWithRelativeStartTime(1.5/4, relativeDuration: 1/4, animations: { () -> Void in
self.pointView.center = CGPointMake(screenWidth-2, screenHeight-2)
})
UIView.addKeyframeWithRelativeStartTime(2.5/4, relativeDuration: 1.5/4.0, animations: { () -> Void in
self.pointView.center = CGPointMake(screenWidth-2, 1)
})
//3
}, completion: nil)
//4
}
Try this then:
let frame = pointView.superview?.convertRect(pointView.frame, toView: nil)
Sometime the view need his superView to find it's coordinates
Put it when you animation is finished
UIView.animateWithDuration(1, animations: { () -> Void in
//animation code
}) { (success) -> Void in
let frame = self.pointView.superview?.convertRect(self.pointView.frame, toView: nil)
}
I'm really stuck here. I would like to shrink the Navigation Bar when I scroll down a UITableView and enlarge it again when scrolling up. I managed to alter the size of the Navigation Bar, but the title image is not shrinking with the Navigation Bar.
I want to do it exactly like Safari, and the problem is that the height of my TitleView is shrinking but the width never changes.
Here's the code I've used to get the height of the scrollbar to change.
func scrollViewDidScroll(scrollView: UIScrollView) {
var navbar = navigationController?.navigationBar
var dir:CGPoint = tableview.panGestureRecognizer.translationInView(self.tableview)
var scrollViewHeight = tableview.frame.size.height
var scrollContentSizeHeight = tableview.contentSize.height
var scrollOffset = tableview.contentOffset.y
if (dir.y > 0 && self.formernavstate == "small") {
self.formernavstate = "big"
UIView.animateWithDuration(0.5, delay:0.0, options: UIViewAnimationOptions.AllowAnimatedContent, animations: { () -> Void in
println("")
navbar?.frame.origin.y = 20
self.navigationItem.titleView?.transform = CGAffineTransformMakeScale(0.52, 0.6)
}, completion: nil)
}
if (dir.y < 0 && self.formernavstate == "big") {
self.formernavstate = "small"
navbar?.frame.origin.y = 0
navigationItem.titleView?.transform = CGAffineTransformMakeScale(0.0001, 0.2)
}
}
I implemented a Swift 3 version with a custom "header" and resizing it's height constraint based on #chauhan's answer:
extension ViewController: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollViewOffset < scrollView.contentOffset.y {
shrinkHeader(shrink: true)
} else if scrollViewOffset > scrollView.contentOffset.y {
shrinkHeader(shrink: false)
}
}
func shrinkHeader(shrink: Bool) {
if shrink {
if self.headerContainerHeightConstraint.constant > CGFloat(minHeaderHeight) {
UIView.animate(withDuration: 0.1, animations: {
self.headerContainerHeightConstraint.constant = self.headerContainerHeightConstraint.constant - 2
self.view.layoutIfNeeded()
})
}
} else {
if self.headerContainerHeightConstraint.constant < CGFloat(maxHeaderHeight) {
UIView.animate(withDuration: 0.1, animations: {
self.headerContainerHeightConstraint.constant = self.headerContainerHeightConstraint.constant + 6
self.view.layoutIfNeeded()
})
}
}
}
}