UIView.animateWithDuration not executing with semaphore - animation

I'm attempting to write a function that will flash a view's background color to white then back to it's original color. What I have is:
func flashView(view: UIView) {
var sema = dispatch_semaphore_create(0)
var color = view.backgroundColor
println("Animating")
UIView.animateWithDuration(0.5, animations: { view.backgroundColor = UIColor.whiteColor() }) {
(success: Bool) in
println("First block completion")
UIView.animateWithDuration(0.5, animations: { view.backgroundColor = color }) {
(success: Bool) in
println("Nested block completion")
dispatch_semaphore_signal(sema)
}
}
println("Waiting")
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)
println("Returning")
}
Now, what I'm experiencing is that the Animating message will display followed by the Waiting. The first animateWithDuration block never gets executed (and needless to say the nested one does not either. However, according to Apple's own documentation the UIView.animateWithDuration occurs on a separate thread. So, my understanding, is that the semaphore should wait, the animateWithDuration should complete on a separate thread, and the function should return (after waiting on the semaphore that's signaled in the nested closure). What am I misunderstanding?
Thanks!

Animation block executes in main thread, and your dispatch_semaphore_wait blocks main thread.
So animation block never executes.
This is one solution.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)
println("Returning")
})
other solutions are here:
How do I wait for an asynchronously dispatched block to finish?

Related

Adding SKNodes into a ScrollView?

On Android, I've previously used CCScrollView (part of Cocos2D-X) to add nodes to a scrollable container, with all the extras like the elastic bounce, scrollbars, swipe gestures, etc.
But I can't work out how to do this in Xcode. I know there's UIScrollView, but I'm writing for macOS. And there's SKCameraNode, but this seems to target the whole scene, rather than just part of the scene, I think.
I'd like to create a horizontal scrollable container for interactive sprite nodes. I know I could simply make the node move in relation to the mouse's x value, but that's not as pleasing as a simple scrollview.
I guess I could place a second scene within the scrollview, but that seems overkill and a performance hit. There's also third party solutions like SwiftySKScrollView but I'd like to avoid relying on such dependencies.
override func mouseDown(with event: NSEvent) {
scroller.removeAllActions()
scrollerPosX = scroller.position.x - event.location(in: self).x
}
override func mouseDragged(with event: NSEvent) {
mouseMomentum = event.deltaX
for node in nodes(at: event.location(in: self)) {
if node.isMember(of: Scroller.self) {
scroller.position.x = event.location(in: self).x + scrollerPosX
}
}
}
// This needs work to improve the ease out momentum, and a boundary bounce needs adding
override func mouseUp(with event: NSEvent) {
let moveby = SKAction.moveBy(x: mouseMomentum*10, y: 0, duration: 0.3)
moveby.timingMode = .easeOut
scroller.run(moveby)
}

iOS Autolayout: Oddly expand-animation of UITextView inside an UIScrollView

I'm trying to animate the height constraint of a UITextView inside a UIScrollView. When the user taps the "toggle" button, the text should appear in animation from top to bottom. But somehow UIKit fades in the complete view.
To ensure the "dynamic" height depending on the intrinsic content-size, I deactivate the height constraint set to zero.
#IBAction func toggle() {
layoutIfNeeded()
UIView.animate(withDuration: 0.6, animations: { [weak self] in
guard let self = self else {
return
}
if self.expanded {
NSLayoutConstraint.activate([self.height].compactMap { $0 })
} else {
NSLayoutConstraint.deactivate([self.height].compactMap { $0 })
}
self.layoutIfNeeded()
})
expanded.toggle()
}
The full code of this example is available on my GitHub Repo: ScrollAnimationExample
Reviewing your GitHub repo...
The issue is due to the view that is animated. You want to run .animate() on the "top-most" view in the hierarchy.
To do this, you can either create a new property of your ExpandableView, such as:
var topMostView: UIView?
and then set that property from your view controller, or...
To keep your class encapsulated, let it find the top-most view. Replace your toggle() func with:
#IBAction func toggle() {
// we need to run .animate() on the "top" superview
// make sure we have a superview
guard self.superview != nil else {
return
}
// find the top-most superview
var mv: UIView = self
while let s = mv.superview {
mv = s
}
// UITextView has subviews, one of which is a _UITextContainerView,
// which also has a _UITextCanvasView subview.
// If scrolling is disabled, and the TextView's height is animated to Zero,
// the CanvasView's height is instantly set to Zero -- so it disappears instead of animating.
// So, when the view is "expanded" we need to first enable scrolling,
// and then animate the height (to Zero)
// When the view is NOT expanded, we first disable scrolling
// and then animate the height (to its intrinsic content height)
if expanded {
textView.isScrollEnabled = true
NSLayoutConstraint.activate([height].compactMap { $0 })
} else {
textView.isScrollEnabled = false
NSLayoutConstraint.deactivate([height].compactMap { $0 })
}
UIView.animate(withDuration: 0.6, animations: {
mv.layoutIfNeeded()
})
expanded.toggle()
}

Start and stop UISlider animation in Swift?

The question is simple - how to start and stop animation for UISlider programmatically?
func playSliderAnimation() {
view.slider.value = 0
UIView.animate(withDuration: duration) {
self.view.slider.setValue(1, animated: true)
}
}
func stopSliderAnimation() {
view.slider.value = 0
}
tried a lot of combinations for stopSliderAnimation - animating, without of animation, small animation duration, dispath into main queue - nothing works. The circle jumps left somewhere outside the bounds

Swift animations appear to be working in reverse

I'm very new to iOS/xcode development, I'm trying to do something very basic as part of a tutorial exercise which is to fade a UILabel in once a user has completed a game.
So I'm hiding the label when the view loads:
override func viewDidLayoutSubviews() {
winMessageLabel.alpha = 0
}
And then on completion of the game I'm trying to fade it back in:
UIView.animateWithDuration(1, animations: { () -> Void in
self.winMessageLabel.alpha = 1
self.view.layoutIfNeeded()
})
But rather than fade in it appears with alpha 1 and then fades out. I get the same behaviour if I try to animate the x/y coordinates whereby it jumps to where I want it to go and then animates back to its original position
I've just worked it out - the layout is invalid so if I call layoutIfNeeded() prior to my animation it sorts everything out and then the label animates as expected:
self.view.layoutIfNeeded()
UIView.animateWithDuration(1, animations: { () -> Void in
self.winMessageLabel.alpha = 1
self.view.layoutIfNeeded()
})

Different Colored currentPageIndicatorTintColor Swift Xcode

Does anyone have any recommendations on changing the Multicolored currentPageIndicatorTintColor for each different page. Something just like this but for ios mobile phones
WatchKit UIPageControl Dot Colour
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
var pageController = UIPageControl.appearance()
pageController.pageIndicatorTintColor = UIColor.whiteColor()
pageController.frame = CGRectMake(200,255,200,200)
//pageController.frame = CGRectMake(40, 50, 240, 150);
//blue
//pageController.currentPageIndicatorTintColor = UIColor(red: (107.0/255.0), green: (185.0/255.0), blue: (198.0/255.0), alpha: 1.0)
//(red: (107.0/255.0), green: (185.0/255.0), blue: (198.0/255.0), alpha: 1.0)
//purple
/*pageController.currentPageIndicatorTintColor = UIColor(red: (159/255.0), green: (125/255.0), blue: (144/255.0), alpha: 1.0)*/
//pink
/*pageController.currentPageIndicatorTintColor = UIColor(red: (226/255.0), green: (145/255.0), blue: (164/255.0), alpha: 1.0)*/
//red
/*pageController.currentPageIndicatorTintColor = UIColor(red: (215/255.0), green: (118/255.0), blue: (118/255.0), alpha: 1.0)*/
//Yellow
/*pageController.currentPageIndicatorTintColor = UIColor(red: (239/255.0), green: (223/255.0), blue: (125/255.0), alpha: 1.0)*/
pageController.backgroundColor = UIColor.clearColor()
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
Try this code.
This is for changing the color of the dots.
self.pageControl.pageIndicatorTintColor = UIColor.blackColor()
This is for changing the current page dot with different colors depending on the index of the page.
if index == 0 {
self.pageControl.currentPage = 1
self.pageControl.currentPageIndicatorTintColor = UIColor.whiteColor()
} else if index == 1 {
self.pageControl.currentPage = 2
self.pageControl.currentPageIndicatorTintColor = UIColor.greenColor()
} else if index == 2 {
self.pageControl.currentPage = 0
self.pageControl.currentPageIndicatorTintColor = UIColor.redColor()
}

Resources