Animated-Voice-Blob

Animated Voice Blob made like in Telegram mobile app.

Sample View controller

The main and the only app’s screen ViewController has sample usage of Telegram-like voice blobs in different styles and with a color picker. It creates Animated Blobs of different styles, simulates voice level changes, and has the color selection and starts/stop animation possibilities.

BlobNode class

BlobNode is a building block of VoiceBlobView. It can be made of different sizes and have different opacities of the primary color.

Each BlobNode will have the following fields to be initialized with:

class BlobNode: UIView {  
	init(  
		pointsCount: Int,  
		minRandomness: CGFloat,  
		maxRandomness: CGFloat,  
		minSpeed: CGFloat,  
		maxSpeed: CGFloat,  
		minScale: CGFloat,  
		maxScale: CGFloat,  
		scaleSpeed: CGFloat,  
		isCircle: Bool  
	) ...  
}

It has some fields that could be changed dynamically via parameters and methods:

/// Makes more detailed view corners  
var pointsCount: Int  
var isCircle: Bool  
/// Controls the blob's size 
var level: CGFloat  
  
func setColor()  
func updateSpeedLevel(to newSpeedLevel: CGFloat)  
func startAnimating()  
func stopAnimating()

VoiceBlobView class

VoiceBlobView controls how the blobs will look together. This class also has some place for configurations via init:

class VoiceBlobView: UIView {  
  // ...  
  typealias BlobRange = (min: CGFloat, max: CGFloat)  
  // ...
  init(  
    frame: CGRect,  
    maxLevel: CGFloat,  
    smallBlobRange: BlobRange,  
    mediumBlobRange: BlobRange,  
    bigBlobRange: BlobRange  
  ) 
}

After init, it is already configured and has added child blobs. Also, it sets up the animation loop, to keep the blobs animating:

// Init scope ...
displayLinkAnimator = ConstantDisplayLinkAnimator() { [weak self] in  
	guard let self = self else { return }  

	self.presentationAudioLevel = self.presentationAudioLevel * 0.9 + self.audioLevel * 0.1  

	self.smallBlob.level = self.presentationAudioLevel  
	self.mediumBlob.level = self.presentationAudioLevel  
	self.bigBlob.level = self.presentationAudioLevel  
}
// Init scope ...

Adding views in ViewController

The default VoiceBlobView configuration, used in the project is the following:

VoiceBlobView(  
	frame: .zero,  
	maxLevel: 50,  
	smallBlobRange: (0.40, 0.54),  
	mediumBlobRange: (0.52, 0.87),  
	bigBlobRange: (0.55, 1.00))

By changing pointsCount and isCircle property of child blobs we have the following results:

We can also create the recursive animate method to test what updateLevel changes for the blob. And call animate() method it in viewDidLoad:

override func viewDidLoad {
	super.viewDidLoad
	// ...
	animate()
}

private func animate() {  
	DispatchQueue.main.asyncAfter(deadline: .now().advanced(by: .milliseconds(500))) { [weak self] in  
		let randomValue = CGFloat.random(in: 10...50)  
		self?.voiceBlob1.updateLevel(randomValue)  
		self?.voiceBlob2.updateLevel(randomValue)  
		self?.voiceBlob3.updateLevel(randomValue)  
		self?.animate()  
	}
}

Instead of random values and time, this can be synced with the audio level. For example, for the user’s audio messages (like Telegram did) or playing video/audio content.

Color picker

The project also has a custom color picker, which changes the color of our blobs and the tint of the screen elements.

GitHub

View Github