Expression type is ambiguous without more context - closures

I am trying to migrate 3rd party lib (expanding collection by Ramotion) to swift 3.
And I am pointing some bottleneck.
I have this struct:
struct ConstraintInfo {
var attribute: NSLayoutAttribute = .left
var secondAttribute: NSLayoutAttribute = .notAnAttribute
var constant: CGFloat = 0
var identifier: String?
var relation: NSLayoutRelation = .equal
}
This operator with some overloads:
precedencegroup ComparisonPrecedence {
associativity: left
higherThan: LogicalConjunctionPrecedence
}
infix operator >>>- : ComparisonPrecedence
func >>>- <T: UIView> (left: (T, T), block: (_: inout ConstraintInfo) -> ()) -> NSLayoutConstraint {
var info = ConstraintInfo()
block(&info)
info.secondAttribute = info.secondAttribute == .notAnAttribute ? info.attribute : info.secondAttribute
let constraint = NSLayoutConstraint(item: left.1,
attribute: info.attribute,
relatedBy: info.relation,
toItem: left.0,
attribute: info.secondAttribute,
multiplier: 1,
constant: info.constant)
constraint.identifier = info.identifier
left.0.addConstraint(constraint)
return constraint
}
And this method in UIView extension:
func addScaleToFillConstratinsOnView(_ view: UIView) {
[NSLayoutAttribute.left, .right, .top, .bottom].forEach { attribute in
(self, view) >>>- { $0.attribute = attribute } // this line is erroring
}
}
Error says:
"'>>>-' produces 'NSLayoutConstraint', not the expected contextual result type 'Void' (aka '()')"
I changed erroring line to
var _: NSLayoutConstraint = (self, view) >>>- { $0.attribute = attribute }
And now I am getting:
Expression type '()' is ambiguous without more context
Also, if I write:
$0.attribute as! NSLayoutAttribute = attribute as! NSLayoutAttribute
I am getting immutability error.
What am I missing out?

In my case I solved it this way:
[NSLayoutAttribute.left, .right, .centerY].forEach { attribute in
_ = (view, collectionView) >>>- {
var _: ConstraintInfo = $0
$0.attribute = attribute
}
}

Related

Unknow constrain found inside a view

I'm creating a custom class to make a view expandable; the class works with a UIPanGestureRecognizer that modifies the view's height constraints. While debugging the code I used the following code to show the height constraints associated with the view.
print(self.constraints.filter({ $0.firstAttribute == .height}).map{print("costraints \($0)")})
The strange thing is that in addition to the costrains created by the method to expand the view there's one more costrain that I have no idea where comes frome. Here's what's printed in the console:
constraint <NSAutoresizingMaskLayoutConstraint:0x6140002828f0 h=--& v=--& UIStackView:0x7ff1c871d0e0.height == 190 (active)>
constraint <NSLayoutConstraint:0x60800009c160 PhotoMapping.ExpandableView:0x7ff1c8416fa0.height == 100 (active)>
Here's the complete code for the class.
import Foundation
import UIKit
class ExpandableView: UIView {
let panGestureRecognizer = UIPanGestureRecognizer()
var minHeight: CGFloat = 0 {
didSet {
initialHeight = minHeight
}
}
var maxHeight: CGFloat = 0
var initialHeight: CGFloat = 0
var initialPoint: CGPoint? = nil
var heightConstraintConstant: CGFloat {
get {
if !self.constraints.filter({ $0.firstAttribute == .height}).isEmpty {
return self.constraints.filter({$0.firstAttribute == .height && $0.secondItem == nil}).first!.constant
} else {
let constrain = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: initialHeight)
self.addConstraint(constrain)
return constrain.constant
}
}
set(newConstant){
if !self.constraints.filter({ $0.firstAttribute == .height}).isEmpty {
self.constraints.filter({$0.firstAttribute == .height && $0.secondItem == nil}).first?.constant = newConstant
self.constraints.filter({$0.firstAttribute == .height && $0.secondItem == nil}).map{print("constant \($0.constant)")}
} else {
let constrain = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: newConstant)
self.addConstraint(constrain)
}
self.layoutIfNeeded()
}
}
func initialize(minHeight: CGFloat, maxHeight: CGFloat) {
panGestureRecognizer.addTarget(self, action: #selector(handlePan(_:)))
panGestureRecognizer.minimumNumberOfTouches = 1
panGestureRecognizer.maximumNumberOfTouches = 1
self.addGestureRecognizer(panGestureRecognizer)
self.minHeight = minHeight
self.maxHeight = maxHeight
}
#objc func handlePan(_ sender: Any) {
let translation = panGestureRecognizer.translation(in: self)
if initialPoint == nil {
initialPoint = translation
}
let translationHeight = CGFloat(translation.y - initialPoint!.y)
print("translationHeight: \(translationHeight)")
if panGestureRecognizer.state == .changed {
let finalHeight = translationHeight + initialHeight
print("finalHeight: \(finalHeight)")
if finalHeight <= maxHeight && finalHeight >= minHeight {
heightConstraintConstant = finalHeight
}
} else if panGestureRecognizer.state == .ended {
let mediumY = maxHeight / 2 + minHeight / 2
if translation.y <= 0 {
heightConstraintConstant = minHeight
} else if translationHeight >= maxHeight {
heightConstraintConstant = minHeight
} else if heightConstraintConstant >= mediumY {
UIView.animate(withDuration: 0.4) {
self.heightConstraintConstant = self.maxHeight
self.layoutIfNeeded()
}
} else if heightConstraintConstant <= mediumY {
UIView.animate(withDuration: 0.2) {
self.heightConstraintConstant = self.minHeight
self.layoutIfNeeded()
}
}
initialPoint = nil
initialHeight = heightConstraintConstant
}
layoutIfNeeded()
}
}
I set the view's class in the storyBoard as "ExpandableView" and I initialized it inside the superclass viewController as shown below:
And here's the code inside the ViewController where I initialize the view called profileView.
profileView.isUserInteractionEnabled = true
profileView.initialize(minHeight: 100, maxHeight: 190)
Set translatesAutoresizingMaskIntoConstraints on your view to false.
When it's true, it will create some constraints defining it's position and size.
Found the bug. The second constrains came from an UIStackView inside the view itself. I needed just to set stackView.translatesAutoresizingMaskIntoConstraints = false to solve the problem.
The unexpected constraint you are seeing is an instance of NSAutoresizingMaskLayoutConstraint. This is added because you have added a component (it appears to be an instance of UIStackView) programatically and you have not set its translatesAutoResizingMaskIntoConstraints property to false)

How animate properly height constraint

Inside my app I'm animating the height constraint of a view and when the height constraint changes the view does translate for a while a little bit down despite I set the constraint to the top as 0. Here's the code I used to animate the change:
UIView.animate(withDuration: 0.5) {
if finalHeight >= mediumY {
self.heightConstraintConstant = self.maxHeight
} else {
self.heightConstraintConstant = self.minHeight
}
}
And here's the one for creating all the expandable view:
//
// ExpandibleView.swift
// PhotoMapping
//
// Created by Lorenzo Santini on 04/09/18.
// Copyright © 2018 Lorenzo Santini. All rights reserved.
//
import Foundation
import UIKit
class ExpandableView: UIView {
let panGestureRecognizer = UIPanGestureRecognizer()
var minHeight: CGFloat = 0 {
didSet {
initialHeight = minHeight
}
}
var maxHeight: CGFloat = 0
var initialHeight: CGFloat = 0
var initialPoint: CGPoint? = nil
var heightConstraintConstant: CGFloat {
get {
if !self.constraints.filter({ $0.firstAttribute == .height}).isEmpty {
return self.constraints.filter({$0.firstAttribute == .height && $0.secondItem == nil}).first!.constant
} else {
let constrain = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: initialHeight)
constrain.priority = .defaultHigh
self.addConstraint(constrain)
return constrain.constant
}
}
set(newConstant){
if !self.constraints.filter({ $0.firstAttribute == .height}).isEmpty {
self.constraints.filter({$0.firstAttribute == .height && $0.secondItem == nil}).first?.constant = newConstant
} else {
let constrain = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1.0, constant: newConstant)
self.addConstraint(constrain)
}
//self.layoutIfNeeded()
}
}
func initialize(minHeight: CGFloat, maxHeight: CGFloat) {
//Create panGestureRecognizer
panGestureRecognizer.addTarget(self, action: #selector(handlePan(_:)))
panGestureRecognizer.minimumNumberOfTouches = 1
panGestureRecognizer.maximumNumberOfTouches = 1
self.addGestureRecognizer(panGestureRecognizer)
//Set min and max height
self.minHeight = minHeight
self.maxHeight = maxHeight
self.translatesAutoresizingMaskIntoConstraints = false
self.subviews.map{$0.translatesAutoresizingMaskIntoConstraints = false}
}
#objc func handlePan(_ sender: Any) {
let translation = panGestureRecognizer.translation(in: self)
if initialPoint == nil {
initialPoint = translation
}
let translationHeight = CGFloat(translation.y - initialPoint!.y)
let finalHeight = translationHeight + initialHeight
if panGestureRecognizer.state == .changed {
if finalHeight <= maxHeight && finalHeight >= minHeight {
heightConstraintConstant = finalHeight
} else if finalHeight >= maxHeight {
heightConstraintConstant = maxHeight
} else if finalHeight <= minHeight {
heightConstraintConstant = minHeight
}
} else if panGestureRecognizer.state == .ended {
let mediumY = maxHeight / 2 + minHeight / 2
if finalHeight >= mediumY {
self.heightConstraintConstant = self.maxHeight
} else {
self.heightConstraintConstant = self.minHeight
}
UIView.animate(withDuration: 0.5) {
self.layoutIfNeeded()
}
initialPoint = nil
initialHeight = heightConstraintConstant
}
//layoutIfNeeded()
}
}
The problem for me isn't how animate the height change because that works but instead is solve the fact that for a while the app seems to ignore the constraint to the top.
Here's the gif showing the animation
I believe what you need to do is actually animate the layout being updated. More specifically something like this
if finalHeight >= mediumY {
self.heightConstraintConstant = self.maxHeight
} else {
self.heightConstraintConstant = self.minHeight
}
UIView.animate(withDuration: 0.5) {
viewThatTheConstraintBelongsTo.layoutIfNeeded()
}

How to add drag to move function to a transformed view?

I made a custom view which can be resized and rotated by dragging a corner circle button.
I need the view to be able to drag to change its position.
I tried several ways but none worked property( the view just acts strangely when combining rotation and resize actions).
What is a proper way to do this?
According to this answer quoting apple's document, What to use to move UIView self.frame or self.transform property?
I should not use frame property along with transformation so I assume I need to user CGAffineTransform to change position of the view?
(I tried this but did not work well)
import Foundation
import UIKit
class ResizeRotateView: UIImageView, Resizable {
private let buttonWidth: CGFloat = 40
private var themeColor: UIColor = UIColor.magentaColor()
// Rotation Resize
var radian: CGFloat = 0
private var tempAnglePanStart : Float = 0
private var tempAngleLastPanEnd: Float = 0
// UI Subviews
lazy var cornerButton: UIView = {
let b = UIView()
b.layer.cornerRadius = self.buttonWidth / 2
b.layer.borderWidth = 1
b.layer.borderColor = self.themeColor.CGColor
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(buttonTouchMoved) )
b.addGestureRecognizer(panGesture)
return b
}()
// Init
override init(frame: CGRect) {
super.init(frame: frame)
setupSuviews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupSuviews() {
addSubview( cornerButton )
addConstraintsWithFormat("H:[v0(\(buttonWidth))]", views: cornerButton)
addConstraintsWithFormat("V:[v0(\(buttonWidth))]", views: cornerButton)
addConstraint(NSLayoutConstraint(item: cornerButton, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .Right, multiplier: 1, constant: 0))
addConstraint(NSLayoutConstraint(item: cornerButton, attribute: .CenterY, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0))
}
override func hitTest(point: CGPoint, withEvent e: UIEvent?) -> UIView? { // This will enable gesture out of self.view's boundary. From stackoverflow.
if let result = super.hitTest(point, withEvent:e) {
return result
}
for sub in self.subviews.reverse() {
let pt = self.convertPoint(point, toView:sub)
if let result = sub.hitTest(pt, withEvent:e) {
return result
}
}
return nil
}
////// Event
func buttonTouchMoved(gestureRecognizer: UIPanGestureRecognizer) {
resize(gestureRecognizer)
rotate(gestureRecognizer)
}
////// Action
// Resize
func resize(gestureRecognizer: UIPanGestureRecognizer) {
// Temporary set aglet to 0
self.transform = CGAffineTransformMakeRotation( 0 )
// Resize
let locationInSelf = gestureRecognizer.locationInView(self)
resizeByKeepingAspectRatioByDistanceFromCenter(toLocation: locationInSelf)
layoutSubviews()
// Recover angle
self.transform = CGAffineTransformMakeRotation( radian )
layoutSubviews()
}
// Rotation
func rotate(gestureRecognizer: UIPanGestureRecognizer) {
guard let superView = self.superview else {
print("#Error: Super View not found")
return
}
let angle = self.angle(byLocation: gestureRecognizer.locationInView( superView ) )
if gestureRecognizer.state == .Began {
print("Pan begin:\(angle)")
tempAnglePanStart = angle - tempAngleLastPanEnd
}
let destinationAngle = angle - tempAnglePanStart
self.transform = CGAffineTransformMakeRotation( destinationAngle.degreesToRadians )
if gestureRecognizer.state == .Ended {
print("Pan ended")
tempAngleLastPanEnd = destinationAngle
}
}
////// Helper
func angle( firstPoint: CGPoint, secondPoint: CGPoint ) -> Float {
let dx = firstPoint.x - secondPoint.x
let dy = firstPoint.y - secondPoint.y
let angle = atan2(dy, dx).radiansToDegrees
let angleInFloat = Float( angle.double )
let formattedAngle = angleInFloat < 0 ? angleInFloat + 360.0 : angleInFloat
return formattedAngle
}
func angle(byLocation location: CGPoint) -> Float {
let targetViewCnter = self.center
return self.angle(targetViewCnter, secondPoint: location)
}
func setAngle(angle: Float) {
let radian = angle.degreesToRadians
self.transform = CGAffineTransformMakeRotation( radian )
self.radian = radian
}
}
Update
I came very close. I could move by keeping rotation angle and view's size. The remaining problem is view jumps a bit when I start dragging.
import Foundation
import UIKit
class ResizeRorateMovableView: ResizeRotateView {
var startCenter: CGPoint = CGPointZero
override init(frame: CGRect) {
super.init(frame: frame)
setupSuviews()
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureEvent))
addGestureRecognizer( panGesture )
userInteractionEnabled = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func panGestureEvent(panGesture: UIPanGestureRecognizer) {
if panGesture.state == .Began {
self.startCenter = self.center
return
}
if panGesture.state == .Changed {
let location = panGesture.translationInView(self)
// Rotate first, then move
var transfrom = CGAffineTransformMakeRotation( self.radian )
transfrom = CGAffineTransformTranslate(transfrom, location.x, location.y)
self.transform = transfrom
return
}
}
}
It probably jumps because the user doesn't necessarily touch in the center, but you're setting the center to where they dragged. If you start dragging close to the center, it won't jump as much as if you start dragging close to the edge. To fix, when the gesture begins, get the distance from the touch to the current center and use that offset when making the translation.
All I need was just to add a superview to implement move function.
This case, I can completely separate rotation function and move function making it real easy to handle.
ViewA. Move function implemented. It is visible below to make it easy to understand but its color should be clear. I made touch for move function only enabled on ViewB(the subview).
ViewB. Rotation function implemented. It is a subview of ViewA

weird positioning of the cursor in the UITextView

I am writing from scratch growing UITextView in my swift app.
I put a textView on the view like this:
it is right above the keyboard.
The textView has constraints attached to the view: leading, bottom, top and trailing, all equals = 4.
The view has the following constraints:
trailing, leading, bottom, top and height
Height is an outlet in my code. I'm checking how many lines are in the textView and based on that I'm modifying height:
func textViewDidChange(textView: UITextView) { //Handle the text changes here
switch(textView.numberOfLines()) {
case 1:
heightConstraint.constant = 38
break
case 2:
heightConstraint.constant = 50
break
case 3:
heightConstraint.constant = 70
break
case 4:
heightConstraint.constant = 90
break
default:
heightConstraint.constant = 90
break
}
}
The number of lines above is calculated by this extension:
extension UITextView{
func numberOfLines() -> Int{
if let fontUnwrapped = self.font{
return Int(self.contentSize.height / fontUnwrapped.lineHeight)
}
return 0
}
}
The initial height of the textView is 38.
The initial font size in the textView is 15.
Now, it works nice, when user starts typing new line, but the textView is not set within full bounds of the view. I mean by that the fact, that it looks like this:
and it should look like this:
Why there is this extra white space being added and how can I get rid of it?
Currently when new line appears there's this white space, but when user scrolls the textView to center the text and get rid of the white space - it is gone forever, user is not able to scroll it up again so the white line is there. So for me it looks like some problem with refreshing content, but maybe you know better - can you give me some hints?
Here is a bit different approach I use in the comment section of one of the apps I'm developing. This works very similar to Facebook Messenger iOS app's input field. Changed outlet names to match with the ones in your question.
//Height constraint outlet of view which contains textView.
#IBOutlet weak var heightConstraint: NSLayoutConstraint!
#IBOutlet weak var textView: UITextView!
//Maximum number of lines to grow textView before enabling scrolling.
let maxTextViewLines = 5
//Minimum height for textViewContainer (when there is no text etc.)
let minTextViewContainerHeight = 40
func textViewDidChange(textView: UITextView) {
let textViewVerticalInset = textView.textContainerInset.bottom + textView.textContainerInset.top
let maxHeight = ((textView.font?.lineHeight)! * maxTextViewLines) + textViewVerticalInset
let sizeThatFits = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max))
if sizeThatFits.height < minTextViewContainerHeight {
heightConstraint.constant = minTextViewContainerHeight
textView.scrollEnabled = false
} else if sizeThatFits.height < maxHeight {
heightConstraint.constant = sizeThatFits.height
textView.scrollEnabled = false
} else {
heightConstraint.constant = maxHeight
textView.scrollEnabled = true
}
}
func textViewDidEndEditing(textView: UITextView) {
textView.text = ""
heightConstraint.constant = minTextViewContainerHeight
textView.scrollEnabled = false
}
I'm using ASTextInputAccessoryView. It handles everything for you and is very easy to set up:
import ASTextInputAccessoryView
class ViewController: UIViewController {
var iaView: ASResizeableInputAccessoryView!
var messageView = ASTextComponentView()
override func viewDidLoad() {
super.viewDidLoad()
let photoComponent = UINib
.init(nibName: "PhotosComponentView", bundle: nil)
.instantiateWithOwner(self, options: nil)
.first as! PhotosComponentView
messageView = ASTextComponentView(frame: CGRect(x: 0, y: 0, width: screenSize.width , height: 44))
messageView.backgroundColor = UIColor.uIColorFromHex(0x191919)
messageView.defaultSendButton.addTarget(self, action: #selector(buttonAction), forControlEvents: .TouchUpInside)
iaView = ASResizeableInputAccessoryView(components: [messageView, photoComponent])
iaView.delegate = self
}
}
//MARK: Input Accessory View
extension ViewController {
override var inputAccessoryView: UIView? {
return iaView
}
// IMPORTANT Allows input view to stay visible
override func canBecomeFirstResponder() -> Bool {
return true
}
// Handle Rotation
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({ (context) in
self.messageView.textView.layoutIfNeeded()
}) { (context) in
self.iaView.reloadHeight()
}
}
}
// MARK: ASResizeableInputAccessoryViewDelegate
extension ViewController: ASResizeableInputAccessoryViewDelegate {
func updateInsets(bottom: CGFloat) {
var contentInset = tableView.contentInset
contentInset.bottom = bottom
tableView.contentInset = contentInset
tableView.scrollIndicatorInsets = contentInset
}
func inputAccessoryViewWillAnimateToHeight(view: ASResizeableInputAccessoryView, height: CGFloat, keyboardHeight: CGFloat) -> (() -> Void)? {
return { [weak self] in
self?.updateInsets(keyboardHeight)
self?.tableView.scrollToBottomContent(false)
}
}
func inputAccessoryViewKeyboardWillPresent(view: ASResizeableInputAccessoryView, height: CGFloat) -> (() -> Void)? {
return { [weak self] in
self?.updateInsets(height)
self?.tableView.scrollToBottomContent(false)
}
}
func inputAccessoryViewKeyboardWillDismiss(view: ASResizeableInputAccessoryView, notification: NSNotification) -> (() -> Void)? {
return { [weak self] in
self?.updateInsets(view.frame.size.height)
}
}
func inputAccessoryViewKeyboardDidChangeHeight(view: ASResizeableInputAccessoryView, height: CGFloat) {
let shouldScroll = tableView.isScrolledToBottom
updateInsets(height)
if shouldScroll {
self.tableView.scrollToBottomContent(false)
}
}
}
Now you just need to set up the actions for the buttons of the AccessoryView.
// MARK: Actions
extension ViewController {
func buttonAction(sender: UIButton!) {
// do whatever you like with the "send" button. for example post stuff to firebase or whatever
// messageView.textView.text <- this is the String inside the textField
messageView.textView.text = ""
}
#IBAction func dismissKeyboard(sender: AnyObject) {
self.messageView.textView.resignFirstResponder()
}
func addCameraButton() {
let cameraButton = UIButton(type: .Custom)
let image = UIImage(named: "camera")?.imageWithRenderingMode(.AlwaysTemplate)
cameraButton.setImage(image, forState: .Normal)
cameraButton.tintColor = UIColor.grayColor()
messageView.leftButton = cameraButton
let width = NSLayoutConstraint(
item: cameraButton,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 40
)
cameraButton.superview?.addConstraint(width)
cameraButton.addTarget(self, action: #selector(self.showPictures), forControlEvents: .TouchUpInside)
}
func showPictures() {
PHPhotoLibrary.requestAuthorization { (status) in
NSOperationQueue.mainQueue().addOperationWithBlock({
if let photoComponent = self.iaView.components[1] as? PhotosComponentView {
self.iaView.selectedComponent = photoComponent
photoComponent.getPhotoLibrary()
}
})
}
}
}

iOS Cell Reusing Changing Colors

I know this is a common problem, I researched but I could not find the problem.
My code:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let row = indexPath.row
let cell1=tableView.dequeueReusableCellWithIdentifier(textCellIdentifier) as! MessageCell
cell1.setCell(swiftBlogs[row])
return cell1
}
MessageCell.swift
func setCell(message:Message) {
messageLabel.text = message.text
if message.incoming != (tag == incomingTag) {
var layoutAttribute: NSLayoutAttribute
var layoutConstant: CGFloat
if message.incoming {
tag = incomingTag
bubbleImageView.image = bubbleImage.incoming
bubbleImageView.highlightedImage = bubbleImage.incomingHighlighed
messageLabel.textColor = UIColor.blackColor()
layoutAttribute = .Left
layoutConstant = 10
} else { // outgoing
tag = outgoingTag
if message.isSent == 1 {
bubbleImageView.image = bubbleImage.outgoing
}
if message.isSent == 0 {
bubbleImageView.image = bubbleImage.notYetSent
}
bubbleImageView.highlightedImage = bubbleImage.outgoingHighlighed
messageLabel.textColor = UIColor.whiteColor()
layoutAttribute = .Right
layoutConstant = -10
}
let constraints: NSArray = contentView.constraints
let indexOfConstraint = constraints.indexOfObjectPassingTest { (constraint, idx, stop) in
return (constraint.firstItem as! UIView).tag == bubbleTag && (constraint.firstAttribute == NSLayoutAttribute.Left || constraint.firstAttribute == NSLayoutAttribute.Right)
}
contentView.removeConstraint(constraints[indexOfConstraint] as! NSLayoutConstraint)
contentView.addConstraint(NSLayoutConstraint(item: bubbleImageView, attribute: layoutAttribute, relatedBy: .Equal, toItem: contentView, attribute: layoutAttribute, multiplier: 1, constant: layoutConstant))
}
}
Let me explain my code: As you can see in setCell If message.incoming is true then cell should be on the left side. If false then cell should be on the right side. This is working without any problem. But as you can see, I have one more if statement. If message.isSent == 1 then I am changing image property of bubbleImageView and my problem starts here. bubbleImage.outgoing is blue image and bubbleImage.notYetSent is red image. When I start scrolling cell colors are changing even isSent value is not changing.
I have no problem with first if statement (message.incoming check.) everythings looks great but colors are changing. How can I resolve this problem?
Example:
Before Scroll:
After Scroll:
I'm a little unclear on how you're using tag, but this line:
if message.incoming != (tag == incomingTag) {
will prevent you from reaching this code:
if message.isSent == 1 {
bubbleImageView.image = bubbleImage.outgoing
}
if message.isSent == 0 {
bubbleImageView.image = bubbleImage.notYetSent
}
in the case of cell reuse. This code needs to run every time you set up a cell and that cell is an outgoing message. Consider moving that part of the setup outside of the first conditional:
if message.incoming != (tag == incomingTag) {
// setup code
}
if !message.incoming {
if message.isSent == 1 {
bubbleImageView.image = bubbleImage.outgoing
}
if message.isSent == 0 {
bubbleImageView.image = bubbleImage.notYetSent
}
}

Resources